Skip to main content

Deno implements many of the standard Web Platform APIs that are available in modern browsers. This means if you've built for the web, you're likely already familiar with many of Deno's APIs. Learning Deno also means investing in your knowledge of the web platform.

Core Web APIs Jump to heading

Deno supports a comprehensive set of web standard APIs:

Network & Communication Jump to heading

Storage & State Jump to heading

Workers & Concurrency Jump to heading

Streams & Data Jump to heading

Events & DOM Jump to heading

Encoding & Crypto Jump to heading

Timers & Performance Jump to heading

Key Benefits Jump to heading

Usage Examples Jump to heading

Making HTTP Requests Jump to heading

const response = await fetch("https://api.github.com/users/denoland");
const data = await response.json();
console.log(data);

Using Web Workers Jump to heading

// main.js
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
});

worker.postMessage({ task: "process_data", data: [1, 2, 3] });
worker.onmessage = (e) => console.log("Result:", e.data);
// worker.js
self.onmessage = (e) => {
  const { task, data } = e.data;
  const result = data.map((x) => x * 2);
  self.postMessage(result);
};

Local Storage Jump to heading

// Persist data across sessions
localStorage.setItem("user_preference", "dark_mode");
const preference = localStorage.getItem("user_preference");

Compatibility Notes Jump to heading

To check if a Web Platform API is available in Deno, refer to the interface documentation on MDN and its Browser Compatibility table.

Most APIs are implemented according to their specifications, with some Deno-specific adaptations for server environments. Notable differences are documented below.

fetch Jump to heading

The fetch API can be used to make HTTP requests. It is implemented as specified in the WHATWG fetch spec.

Spec deviations Jump to heading

Fetching local files Jump to heading

Deno supports fetching file: URLs. This makes it easier to write code that uses the same code path on a server as local, as well as easier to author code that works both with the Deno CLI and Deno Deploy.

Deno only supports absolute file URLs, this means that fetch("./some.json") will not work. It should be noted though that if --location is specified, relative URLs use the --location as the base, but a file: URL cannot be passed as the --location.

To be able to fetch a resource, relative to the current module, which would work if the module is local or remote, you should to use import.meta.url as the base. For example:

const response = await fetch(new URL("./config.json", import.meta.url));
const config = await response.json();

Notes on fetching local files:

CustomEvent and EventTarget Jump to heading

The DOM Event API can be used to dispatch and listen to events happening in an application. It is implemented as specified in the WHATWG DOM spec.

Event API Deviations Jump to heading

Typings Jump to heading

The TypeScript definitions for the implemented web APIs can be found in the lib.deno.shared_globals.d.ts and lib.deno.window.d.ts files.

Definitions that are specific to workers can be found in the lib.deno.worker.d.ts file.

Location Jump to heading

Deno supports the location global from the web.

Location flag Jump to heading

There is no "web page" whose URL we can use for a location in a Deno process. We instead allow users to emulate a document location by specifying one on the CLI using the --location flag. It can be a http or https URL.

// deno run --location https://example.com/path main.ts

console.log(location.href);
// "https://example.com/path"

You must pass --location <href> for this to work. If you don't, any access to the location global will throw an error.

// deno run main.ts

console.log(location.href);
// error: Uncaught ReferenceError: Access to "location", run again with --location <href>.

Setting location or any of its fields will normally cause navigation in browsers. This is not applicable in Deno, so it will throw in this situation.

// deno run --location https://example.com/path main.ts

location.pathname = "./foo";
// error: Uncaught NotSupportedError: Cannot set "location.pathname".

Extended usage Jump to heading

On the web, resource resolution (excluding modules) typically uses the value of location.href as the root on which to base any relative URLs. This affects some web APIs adopted by Deno.

Fetch API Jump to heading

// deno run --location https://api.github.com/ --allow-net main.ts

const response = await fetch("./orgs/denoland");
// Fetches "https://api.github.com/orgs/denoland".

The fetch() call above would throw if the --location flag was not passed, since there is no web-analogous location to base it onto.

Worker modules Jump to heading

// deno run --location https://example.com/index.html --allow-net main.ts

const worker = new Worker("./workers/hello.ts", { type: "module" });
// Fetches worker module at "https://example.com/workers/hello.ts".

Note

For the above use cases, it is preferable to pass URLs in full rather than relying on --location. You can manually base a relative URL using the URL constructor if needed.

The --location flag is intended for those who have a specific purpose in mind for emulating a document location and are aware that this will only work at application-level. However, you may also use it to silence errors from a dependency which is frivolously accessing the location global.

Web Storage Jump to heading

The Web Storage API provides an API for storing string keys and values. Persisting data works similar to a browser, and has a 10MB storage limit. The global sessionStorage object only persists data for the current execution context, while localStorage persists data from execution to execution.

In a browser, localStorage persists data uniquely per origin (effectively the protocol plus hostname plus port). As of Deno 1.16, Deno has a set of rules to determine what is a unique storage location:

To set, get and remove items from localStorage, you can use the following:

// Set an item in localStorage
localStorage.setItem("myDemo", "Deno App");

// Read an item from localStorage
const cat = localStorage.getItem("myDemo");

// Remove an item from localStorage
localStorage.removeItem("myDemo");

// Remove all items from localStorage
localStorage.clear();

Web Workers Jump to heading

Deno supports the Web Worker API.

Workers can be used to run code on multiple threads. Each instance of Worker is run on a separate thread, dedicated only to that worker.

Currently Deno supports only module type workers; thus it's essential to pass the type: "module" option when creating a new worker.

Use of relative module specifiers in the main worker are only supported with --location <href> passed on the CLI. This is not recommended for portability. You can instead use the URL constructor and import.meta.url to easily create a specifier for some nearby script. Dedicated workers, however, have a location and this capability by default.

// Good
new Worker(import.meta.resolve("./worker.js"), { type: "module" });

// Bad
new Worker(import.meta.resolve("./worker.js"));
new Worker(import.meta.resolve("./worker.js"), { type: "classic" });
new Worker("./worker.js", { type: "module" });

As with regular modules, you can use top-level await in worker modules. However, you should be careful to always register the message handler before the first await, since messages can be lost otherwise. This is not a bug in Deno, it's just an unfortunate interaction of features, and it also happens in all browsers that support module workers.

import { delay } from "jsr:@std/async@1/delay";

// First await: waits for a second, then continues running the module.
await delay(1000);

// The message handler is only set after that 1s delay, so some of the messages
// that reached the worker during that second might have been fired when no
// handler was registered.
self.onmessage = (evt) => {
  console.log(evt.data);
};

Instantiation permissions Jump to heading

Creating a new Worker instance is similar to a dynamic import; therefore Deno requires appropriate permission for this action.

For workers using local modules; --allow-read permission is required:

main.ts
new Worker(import.meta.resolve("./worker.ts"), { type: "module" });
worker.ts
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: read access to "./worker.ts", run again with the --allow-read flag

$ deno run --allow-read main.ts
hello world

For workers using remote modules; --allow-net permission is required:

main.ts
new Worker("https://example.com/worker.ts", { type: "module" });
worker.ts
// This file is hosted at https://example.com/worker.ts
console.log("hello world");
self.close();
$ deno run main.ts
error: Uncaught PermissionDenied: net access to "https://example.com/worker.ts", run again with the --allow-net flag

$ deno run --allow-net main.ts
hello world

Using Deno in a worker Jump to heading

main.js
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
});

worker.postMessage({ filename: "./log.txt" });
worker.js
self.onmessage = async (e) => {
  const { filename } = e.data;
  const text = await Deno.readTextFile(filename);
  console.log(text);
  self.close();
};
log.txt
hello world
$ deno run --allow-read main.js
hello world

Specifying worker permissions Jump to heading

Caution

This is an unstable Deno feature. Learn more about unstable features.

The permissions available for the worker are analogous to the CLI permission flags, meaning every permission enabled there can be disabled at the level of the Worker API. You can find a more detailed description of each of the permission options in the security documentation.

By default a worker will inherit permissions from the thread it was created in, however in order to allow users to limit the access of this worker we provide the deno.permissions option in the worker API.

For permissions that support granular access you can pass in a list of the desired resources the worker will have access to, and for those who only have the on/off option you can pass true/false respectively:

const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      net: [
        "deno.land",
      ],
      read: [
        new URL("./file_1.txt", import.meta.url),
        new URL("./file_2.txt", import.meta.url),
      ],
      write: false,
    },
  },
});

Granular access permissions receive both absolute and relative routes as arguments, however take into account that relative routes will be resolved relative to the file the worker is instantiated in, not the path the worker file is currently in:

const worker = new Worker(
  new URL("./worker/worker.js", import.meta.url).href,
  {
    type: "module",
    deno: {
      permissions: {
        read: [
          "/home/user/Documents/deno/worker/file_1.txt",
          "./worker/file_2.txt",
        ],
      },
    },
  },
);

Both deno.permissions and its children support the option "inherit", which implies it will borrow its parent permissions:

// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: "inherit",
  },
});
// This worker will inherit only the net permissions of its parent
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      env: false,
      hrtime: false,
      net: "inherit",
      ffi: false,
      read: false,
      run: false,
      write: false,
    },
  },
});

Not specifying the deno.permissions option or one of its children will cause the worker to inherit by default:

// This worker will inherit its parent permissions
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
});
// This worker will inherit all the permissions of its parent BUT net
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: {
      net: false,
    },
  },
});

You can disable the permissions of the worker all together by passing "none" to the deno.permissions option:

// This worker will not have any permissions enabled
const worker = new Worker(import.meta.resolve("./worker.js"), {
  type: "module",
  deno: {
    permissions: "none",
  },
});

Deviations of other APIs from spec Jump to heading

Cache API Jump to heading

Only the following APIs are implemented:

A few things that are different compared to browsers:

  1. You cannot pass relative paths to the APIs. The request can be an instance of Request or URL or a url string.
  2. match() & delete() don't support query options yet.