Deno logoDeno

Deno 1.15 Release Notes

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

If you already have Deno installed, you can upgrade to 1.15 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 crypto APIs

In line with our goal to have a feature complete Web Crypto API by the end of the year, we have made good progress this release:

  • crypto.subtle.exportKey():
    • RSA keys can now be exported in spki format.
  • crypto.subtle.importKey():
    • ECDSA keys can now be imported in raw format.
  • crypto.subtle.deriveBits():
    • ECDH derivation is now supported (only for P256 keys).
  • crypto.subtle.wrapKey():
    • Key wrapping is now supported for all key & format combinations supported by crypto.subtle.exportKey().
  • crypto.subtle.encrypt():
    • AES-CBC encryption is now supported.
  • crypto.subtle.decrypt():
    • AES-CBC decryption is now supported.

You can subscribe to this tracking issue to be notified of further progress on the Web Crypto API.

Thanks to Divy Srivastava for the significant work on the Web Crypto implementation in Deno.

FFI improvements

FFI was added in Deno 1.13, this API allows users to call libraries written in languages that support C ABI such as C, C++, C#, Kotlin, Nim, Rust or Zig.

This release adds support for two very useful features to FFI API: non-blocking calls and buffer arguments.

Non-blocking calls

There are many use cases where users might want to run CPU-bound FFI functions in the background without blocking other tasks on the main thread.

With this release symbols can be marked nonblocking in Deno.dlopen. These function calls will run on a dedicated blocking thread and will return a Promise resolving to the desired result.

Example of executing expensive FFI calls with Deno:

// sleep.c
#ifdef _WIN32
#include <Windows.h>
#include <time.h>

int sleep(unsigned int ms) {
  #ifdef _WIN32
  struct timespec ts;
  ts.tv_sec = ms / 1000;
  ts.tv_nsec = (ms % 1000) * 1000000;
  nanosleep(&ts, NULL);

Calling it from Deno:

// nonblocking_ffi.ts
const library = Deno.dlopen("./", {
  sleep: {
    parameters: ["usize"],
    result: "void",
    nonblocking: true,

library.symbols.sleep(500).then(() => console.log("After"));


$ deno run --allow-ffi --unstable nonblocking_ffi.ts

Buffer arguments

Prior to 1.15 arguments to FFI symbols were limited to primitive types, but this release adds support for using buffers as arguments for FFI calls.

When calling FFI symbols with a buffer, the next argument must be buffer's length.


pub extern "C" fn print_buffer(ptr: *const u8, len: usize) {
  let buf = unsafe { std::slice::from_raw_parts(ptr, len) };
  println!("{:?}", buf);

Calling it from Deno:

// print_buffer.ts
const library = Deno.dlopen("./", {
  print_buffer: {
    parameters: ["buffer", "usize"],
    result: "void",

const buffer = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
dylib.symbols.print_buffer(buffer, buffer.length);


$ deno run --allow-ffi --unstable print_buffer.ts
[1, 2, 3, 4, 5, 6, 7, 8]

In addition to passing buffers-as-arg, we recognize the importance of being able to use buffers-as-return-value and plan to support this feature in the coming releases.

For more information on the FFI API visit manual page.

Thanks to Elias Sjögreen and Divy Srivastava for implementing these features.

deno uninstall command

This release introduces a deno uninstall subcommand. It lets you remove a command previously installed with deno install.

$ deno install
✅ Successfully installed welcome
$ welcome
Welcome to Deno!
$ deno uninstall welcome
deleted /Users/lucacasonato/.deno/bin/welcome
✅ Successfully uninstalled welcome

Uninstalling a command was previously already possible by just removing the command file from the .deno/bin directory.

deno lint --watch

deno lint now supports the --watch flag that will keep the process alive after printing diagnostics and watch for file changes to update diagnostics from changed files.

As in other subcommands that support --watch flag, the files that are needed to be watched are automatically discovered by Deno.

Thanks to @CGQAQ who contributed this feature.

In memory CA certificates

Over the past year, Deno has accumulated 3 different APIs that all deal with TLS: Deno.connectTls for establishing outbound TLS connections, Deno.startTls for upgrading existing connections to TLS, and Deno.createHttpClient for changing HTTP and TLS settings for outbound HTTP requests.

All of these three APIs had various ways to specify custom CA certificates. This has now been unified. All three APIs now take a caCerts option. This property must be given as an array of strings, where each string is a PEM encoded X.509 certificate. Multiple certificates can be specified in this array.

An example:

const caCert = await Deno.readTextFile("./custom_ca_cert.pem");
const client = Deno.connectTls({
  hostname: "database.internal",
  port: 4443,
  caCerts: [caCert],

This release also removes the unstable caData option from Deno.createHttpClient because this new API replaces it.

Nested testing API

This release adds a new experimental sub-steps API to the Deno testing framework. This addition allows users to define sub-steps for tests defined by Deno.test. These sub steps get their own sanitizer scopes and are rendered in the test runner with indents. The new API is general enough so it can be wrapped by polyfills to emulate existing test frameworks like mocha or node-tap. The original explainer for this new API explains it in more detail.

Here is an example of a test that uses the new API. It creates a database connection, runs some queries against it in sub tests and then closes the connection.

Deno.test("database test", async (t) => {
  const db = await Database.connect("postgres://localhost/test");

  await t.step("insert user", async () => {
    const users = await db.query(
      "INSERT INTO users (name) VALUES ('Deno') RETURNING *",
    assertEquals(users.length, 1);
    assertEquals(users[0].name, "Deno");

  await t.step("insert book", async () => {
    const books = await db.query(
      "INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
    assertEquals(books.length, 1);
    assertEquals(books[0].name, "The Deno Manual");


The same test written in Mocha style would look like this:

describe("database test", () => {
  let db: Database;

  beforeAll(async () => {
    db = await Database.connect("postgres://localhost/test");

  it("insert user", async () => {
    const users = await db!.query(
      "INSERT INTO users (name) VALUES ('Deno') RETURNING *",
    assertEquals(users.length, 1);
    assertEquals(users[0].name, "Deno");

  it("insert book", async () => {
    const books = await db!.query(
      "INSERT INTO books (name) VALUES ('The Deno Manual') RETURNING *",
    assertEquals(books.length, 1);
    assertEquals(books[0].name, "The Deno Manual");

  afterAll(() => {

For those more familiar with this style, we have written a simple polyfill for Mocha that builds on top of this new API:

Inline with this polyfill, we now have some examples showcasing how to use chai or sinon for assertions in Deno tests:

Please note that this API is still experimental and may change in the future. Feedback on this API is very welcome!

API stabilizations

1.15 brings stabilization of several Deno APIs:

  • Deno.kill
  • Deno.Process.kill
  • Deno.resolveDns

Using these APIs no longer requires passing --unstable flag on the CLI.

We plan to stabilize the Deno.signal() API in the upcoming release.

Stabilization of URLPattern API in sync with Chromium

Last release introduced a new unstable web platform API for matching URLs against patterns. URLPattern is a builtin alternative to the popular path-to-regexp library.

The pattern syntax is very similar to path-to-regexp. What sets URLPattern apart is that it is capable of matching more than just paths - it can match each part of a URL (protocol, hostname, pathname, query string, hash, etc.) individually.

This release stabilizes this API in sync with Chromium. It will be available today in Deno 1.15, and on October 19th in the 95 release of Google Chrome and Microsoft Edge.

Documentation and examples are available on MDN:

V8 9.5 update

Deno 1.15 includes V8 9.5. This release introduces the new "Intl.DisplayNames v2" JavaScript API, an extended timeZoneName option for Intl.DateTimeFormat, and support for the "WebAssembly Exception Handling (Wasm EH) proposal".

Details about each change are available in the V8 9.5 release notes.

Improving Node Compatibility

To make it easier to run Node programs in Deno, a new flag --compat was added (this flag requires --unstable). When this flag is present Deno will automatically set up Node global variables (like process) and provide all built-in modules available in Node. This means Deno can now handle code like this:

import { readFileSync } from "fs";
let passwd = readFileSync("/etc/passwd", "utf-8");

This release is just the first pass for --compat. We will be putting a lot of work into improving compat mode in coming releases, working towards Node emulation.

std/node is a module that provides a compatibility layer for Node APIs, making it possible to run a subset of Node programs in Deno. The release of std 0.111.0 brings a big update to this compatibility layer, including addition of some highly desired modules (dns, http and net), as well as many updates to the crypto module.