Skip to main content

Deno 1.13 Release Notes

Deno 1.13 has been tagged and released with the following features and changes:

If you already have Deno installed, you can upgrade to 1.13 by running

deno upgrade

If you are installing Deno for the first time, you can use one of the methods listed below:

# Using Shell (macOS and Linux):
curl -fsSL | sh

# Using PowerShell (Windows):
iwr -useb | iex

# Using Homebrew (macOS):
brew install deno

# Using Scoop (Windows):
scoop install deno

# Using Chocolatey (Windows):
choco install deno

New features

Stabilize native HTTP server API

The native HTTP server API in this release is now a stable API. Out of the box Deno is now capable of efficiently serving HTTP/1.1 and HTTP/2 traffic. The system exposes the Hyper web server as a JavaScript API that will feel familiar to many web developer.

Since it was first introduced as an unstable API in Deno 1.9 many bugs have been fixed. For the past few weeks we have been serving many millions of requests to from servers powered by this new API. Through this dogfooding we are now confident that the native HTTP API is compatible and stable enough for general purpose use.

for await (const conn of Deno.listen({ port: 4500 })) {
  (async () => {
    for await (const { respondWith } of Deno.serveHttp(conn)) {
      respondWith(new Response("Hello World"));

If you are currently using std/http we encourage you to upgrade to the native HTTP server. std/http will be available in std for a few more releases, but will be removed soon. A guide for migration will be available in the manual soon. If you are using a recent version of oak, your application will seamlessly switch to using the native HTTP server after upgrading to Deno 1.13.

The built-in server-side WebSocket support is still marked unstable, but available since 1.12 via the Deno.upgradeWebSocket() function.

For an introduction to the new API, please take look at the manual entry.

Support for self.structuredClone()

The HTML spec authors (thanks Surma) recently added a new self.structuredClone function. It exposes the structured clone algorithm used for message passing between web workers and MessagePort in a simple, idiomatic, and synchronous API. Previously it was not possible to make a synchronous structured clone of an object.

The structured clone algorithm can deep clone JavaScript values and supports circular object references. For more info on structured cloning, visit MDN.

import { assert } from "";

// Create an object with a value and a circular reference to itself.
const foo = { bar: "baz" }; = foo;

// Clone it
const clone = self.structuredClone(foo);

assert(clone !== foo); // assert they are not the same object
assert( === "baz"); // assert they  do have the same value though
assert( === clone); // assert that the circular reference is preserved

console.log("All assertions passed!");

Try it yourself:

deno run

Deno is the first runtime to ship this new feature, but it will likely be landing in Chrome and Firefox within the next few releases. We are excited to continue collaboration with standards bodies and browser vendors to unify and streamline the server side and client side JavaScript ecosystems.

Thanks to @crowlKats for implementing this feature.

Use system certificate store for TLS

This release introduces a new DENO_TLS_CA_STORE environment variable that can be used to switch which certificate authorities Deno trusts for TLS. Up until this release, Deno has only supported trusting the bundled Mozilla root CA store, and individual additional certificates with the --cert flag.

This is a hindrance for users running Deno in networks that proxy TLS connections using self-signed TLS certificates to re-sign traffic. These TLS certificates are often installed into the system root trust store by network administrators.

Deno now has the ability to use the system root CA store by setting the environment variable DENO_TLS_CA_STORE=system. On macOS certificates are loaded from the keychain, on Windows they are loaded from the system certificate store, and on Linux the system CA bundle is read from well known paths on disk. Specifying this argument will disable the builtin CA store.

More info on DENO_TLS_CA_STORE can be found in the manual:

Thanks to Justin Chase for implementing this feature.

Disable TLS verification

Deno 1.13 adds a new flag called --unsafely-ignore-certificate-errors. This flag allows to disable SSL certificate verification.

Note that this is a dangerous setting. You should not use this flag to silence certificate errors. Disabling TLS certificate verification (ignoring certificate errors) makes TLS pointless because it allows MITM attacks. Data sent over a TLS connection is not confidential if the certificate has not been verified and trusted.

When you encounter TLS errors, instead of using this flag you should make sure the certificate for the CA that signed the servers TLS certificate is added to Deno’s root trust store. You can use the --cert flag to do this for one off certificates.

If you are inside of a corporate network that proxies all TLS connections with a self signed certificate, that certificate is generally installed into your system root CA store. To tell Deno to use this, globally set the DENO_TLS_CA_STORE=system environment variable. In many cases this will fix your TLS issues, without disabling certificate validation.

The --unsafely-ignore-certificate-errors flag optionally accepts a list of hostnames for which the verification should be disabled. If no argument is provided then no verification of certificates is preformed. This is very insecure, and should not be used. This flag impacts both internal Deno functions (eg. downloading dependencies), as well as runtime APIs like fetch, WebSocket, or Deno.connectTls.

Starting Deno with this flag will always print a warning message:

$ deno run --allow-net --unsafely-ignore-certificate-errors fetch.ts
DANGER: TLS certificate validation is disabled for all hostnames
$ deno run --allow-net --unsafely-ignore-certificate-errors=localhost fetch.ts
DANGER: TLS certificate validation is disabled for: localhost

Thank you to TheAifam5 who contributed this feature.

Updates to WebCrypto APIs

This release adds some more functionality to the WebCrypto APIs:

  • crypto.subtle.importKey() and crypto.subtle.exportKey() now support importing and exporting raw HMAC keys.
  • crypto.subtle.verify() now supports verifying signatures created from HMAC keys.

Thanks to Divy Srivastava for implementing this feature.

Updates to the Deno Language Server and VSCode Extension

Refactoring code actions

Refactoring code actions are now available for JavaScript and TypeScript files. These provide refactors for common tasks, like extracting code to functions and constants, or moving code to new files. There is nothing you need to do to make them available, as they will be provided to your editor.

Thanks to Jean Pierre for contributing the feature.

Ability to set cache/DENO_DIR via settings

The language server added the setting deno.cache which can be set via your editor. This configures the language server to use a specific path to the cache directory for Deno. This is similar to the DENO_DIR environment variable which can be used when starting Deno from the command line.

This is ideal in situations where you want to separate out cache directories while developing code, possibly checking in a cache directory as part of your project.

Various minor enhancements and bug fixes

There were several other minor enhancements, like improving the diagnostics information available, fixing problems when using import maps, and fixing an issue that caused detached language server process to not self terminate.

Improvements to the REPL

This release brings two significant improvements to the REPL (deno repl):

Exports are now ignored

The export keyword before functions, classes or TypeScript types will now be ignored. This is useful for when you copy paste some snippet of code from a module into the REPL. Previously the REPL would error on the export syntax.

--eval flag

The REPL now has an --eval flag. This allows you to run some code in the JS runtime before the user is dropped into a REPL. This is useful for importing some code you commonly use in the REPL, or modifying the runtime in some way:

--eval for `deno repl`

Support for navigator.hardwareConcurrency API

The navigator.hardwareConcurrency web API is added in this release, replacing the unstable Deno.cpuInfo() API. It can be used to retrieve the number of logical CPU cores a machine has. A machine with 4 hardware cores and simultaneous multithreading (Intel® Hyper-Threading) has 8 logical cores. This is often used to determine how many web workers should be spawned for a worker pool for most efficient use of resources.

// 8

More documentation is available on MDN.

Thanks to Divy Srivastava for implementing this feature.

V8 9.3

This release updates V8 to version 9.3. This release introduces two new JavaScript language features and we have also made these new features available via the built-in TypeScript type libraries:

Error cause

Error has a new cause property that can be used to chain errors:

const parentError = new Error("parent");
const error = new Error("parent", { cause: parentError });
console.log(error.cause === parentError);
// → true


Object.hasOwn is an easier-to-reach-for alias for

Object.hasOwn({ prop: 42 }, "prop");
// → true

The full release notes can be found on the v8 blog.

Type references in deno info

Deno ships with a built-in dependency inspector available as deno info command. The inspector shows a tree of dependencies for any given entry module. Starting with 1.13 type declarations that are referenced using /// <reference lib="...">, // @deno-types directives or X-TypeScript-Types headers will be shown in the tree view.

AbortSignal support in writeFile

This release adds support for specifying an abort signal in writeFile. This allows you to terminate the writing of a file if it turns out that the file is too large, or takes too long to write to disk.

const aborter = new AbortController();
const data = new UInt8Array(32 * 1024 * 1024);
Deno.writeFile("./super_large_file.txt", { signal: aborter.signal })
  .then((data) => console.log("File write:", data.length))
  .catch((err) => console.error("File write failed:", err));
setTimeout(() => aborter.abort(), 1000);

Thank you to Benjamin Gruenbaum who contributed this feature.

Type check code examples in Markdown files

In Deno 1.10 we introduced a feature that allows to type check example code in JSDoc comments, following up on that, Deno 1.13 adds support for type checking examples in code blocks in Markdown files. deno test --doc will now include *.md files and type check all code blocks with js, jsx, ts, tsx attributes. You can opt-out of the type checking by adding ignore attribute, eg. ts, ignore.

Deno uses this feature to ensure code examples in the standard library and in the manual are up to date.

Thank you to Casper Beyer who contributed this feature.

Spawn subprocess with clean environment

A new option called clearEnv has been added to Deno.RunOptions. It allows to spawn a subprocess with “clear” environment, ie. environmental variables from parent process will not be included in the child process.

This is an unstable feature and requires --unstable flag to use.

Thanks to @crowlKats for implementing this feature.

Permissions APIs accept URLs

This release brings an update to Deno.permissions API, which can now accept URLs in addition to strings when querying for “read”, “write” and “run” permissions. URLs are now also accepted in the permission configuration for Deno.test and web workers.

await Deno.permissions.query({
  name: "read",
  path: new URL(".", import.meta.url),
await Deno.permissions.query({
  name: "write",
  path: new URL(".", import.meta.url),
await Deno.permissions.query({
  name: "run",
  command: new URL(".", import.meta.url),

Thanks to @crowlKats for implementing this feature.

Experimental FFI replaces native plugin system

Deno shipped with a native plugin system that allowed you to open a dynamic Rust library using Deno.openPlugin() API and call “ops” defined in that library. Although the ideas behind that system were great, we faced many technical challenges arising mainly from lack of ABI stability in Rust.

In 1.13 the native plugins system was replaced with a more general FFI API that allows you to call libraries written in languages other than Rust directly from Deno - effectively Deno.openPlugin() was replaced by Deno.dlopen().

Note that this is the first iteration of new API, it’s considered unstable and requires --unstable API to use it. We’re planning to add support for more types that can cross the language boundary in the future releases.

Here’s an example showing how to call a C function from Deno:

// add.c
int add_numbers(int a, int b) {
  return a + b;

Compile file as a shared library:

// unix
cc -c -o add.o add.c
cc -shared -W -o add.o
// windows
cl /LD add.c /link /EXPORT:libadd

Call the shared library from Deno:

// ffi.js
let libSuffix = "so";

if ( == "windows") {
  libSuffix = "dll";

const libName = `add_numbers.${libSuffix}`;
const dylib = Deno.dlopen(libName, {
  "add_numbers": { parameters: ["i32", "i32"], result: "i32" },

console.log(dylib.symbols.add_numbers(123, 456));

Run it:

deno run --allow-ffi --unstable ffi.js

Thanks to Elias Sjögreen for implementing this feature.

Experimental WebSocketStream API

Over the last few months, the folks over at Chrome have been working on a new modern WebSocket API based on web streams. The new API is called WebSocketStream. It is a successor to the existing WebSocket API that is based on events.

Using streams solves a long standing issue with the existing WebSocket API: backpressure. Events could be dispatched as fast as the browser wants, without giving the developer any way to pause event dispatch. This meant it is easy to overload the JS thread if you do some computation on every event.

The new API solves this by using a ReadableStream as its receiving (readable) end, and a WritableStream as its sending (writable) end. Because a developer always has to “request” the next item from the stream, the browser can’t flood the script with events unasked.

The old API also had a "close" event that was dispatched when the stream closes. This has been replaced with a Promise that resolves when the stream is closed. This makes code using the new WebSocketStream integrate more effectively with async/await.

Here is a side by side example of the old API versus the new API. The new API is at the top, and the old API at the bottom. The example sends a message to a remote server, and then will print out all messages received from the server.

// Initiate a connection, and wait for it to be established. The
// `wss.connection` throws if the connection can not be established.
const wss = new WebSocketStream("wss://");
const { writable, readable } = await wss.connection;

// Send a text message to the remote server. If this errors (connection closed)
// the writer.write call will reject.
const writer = writable.getWriter();
await writer.write("Hello server!");

// Iterate over all incoming messages, and print them out. If this errors
// (connection closed) async iterator will reject, and the for await loop
// terminates.
for await (const message of readable) {
  console.log("new message:", message);
// Initiate a connection, and wait for it to be established. The below promise
// will reject if an error occurs during connection establishment.
const ws = await new Promise((resolve, reject) => {
  const ws = new WebSocket("wss://");
  ws.onerror = (e) => {
    reject(new Error(e.message));
  ws.onopen = (e) => {

// Listen for error events, because the below calls do not reject if an error
// occurs.
ws.onerror = (e) => {
  console.error("connection closed:", e.code, e.reason);

// Send a text message to the remote server. This does not reject if an error
// occurs while sending.
ws.send("Hello server!");

// Listen for the message event.
ws.onmessage = (e) => {
  console.log("new message:",;

The API is still in the prototyping stage, with a proper specification yet to be written (there is an explainer however). We have decided to implement the API in --unstable to start collecting some feedback from developers before the API is finalized and ships in browsers. Please report back any ergonomic issues on the Deno issue tracker, or the issue tracker in the explainer repository.

More info can also be found in this post.

Thanks to @crowlKats for implementing this feature.

HN Comments