On this page
Web Platform APIs
One way Deno simplifies web and cloud development is by using standard Web
Platform APIs (like fetch, WebSockets and more) over proprietary APIs. This
means if you've ever built for the browser, you're likely already familiar with
Deno, and if you're learning Deno, you're also investing in your knowledge of
the web.
Below we'll highlight some of the standard Web APIs that Deno supports.
To check if a Web Platform API is available in Deno, you can click on the interface on MDN and refer to its Browser Compatibility table.
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
- The Deno user agent does not have a cookie jar. As such, the
set-cookieheader on a response is not processed, or filtered from the visible response headers. - Deno does not follow the same-origin policy, because the Deno user agent
currently does not have the concept of origins, and it does not have a cookie
jar. This means Deno does not need to protect against leaking authenticated
data cross origin. Because of this Deno does not implement the following
sections of the WHATWG
fetchspecification:- Section
3.1. 'Origin' header. - Section
3.2. CORS protocol. - Section
3.5. CORB. - Section
3.6. 'Cross-Origin-Resource-Policy' header. Atomic HTTP redirect handling.- The
opaqueredirectresponse type.
- Section
- A
fetchwith aredirectmode ofmanualwill return abasicresponse rather than anopaqueredirectresponse. - The specification is vague on how
file:URLs are to be handled. Firefox is the only mainstream browser that implements fetchingfile:URLs, and even then it doesn't work by default. As of Deno 1.16, Deno supports fetching local files. See the next section for details. - The
requestandresponseheader guards are implemented, but unlike browsers do not have any constraints on which header names are allowed. - The
referrer,referrerPolicy,mode,credentials,cache,integrity,keepalive, andwindowproperties and their relevant behaviours inRequestInitare not implemented. The relevant fields are not present on theRequestobject. - Request body upload streaming is supported (on HTTP/1.1 and HTTP/2). Unlike the current fetch proposal, the implementation supports duplex streaming.
- The
set-cookieheader is not concatenated when iterated over in theheadersiterator. This behaviour is in the process of being specified.
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:
- Permissions are applied to reading resources, so an appropriate
--allow-readpermission is needed to be able to read a local file. - Fetching locally only supports the
GETmethod, and will reject the promise with any other method. - A file that does not exist simply rejects the promise with a vague
TypeError. This is to avoid the potential of fingerprinting attacks. - No headers are set on the response. Therefore it is up to the consumer to determine things like the content type or content length.
- Response bodies are streamed from the Rust side, so large files are available in chunks, and can be cancelled.
Structured Clone & Transferable Objects Jump to heading
Deno supports structuredClone() and
postMessage() for cloning and transferring objects across
contexts (e.g. between the main thread and Web Workers).
Serializable types Jump to heading
These types can be cloned with structuredClone() and sent via postMessage():
| Type | Notes |
|---|---|
| Primitives | string, number, boolean, null, undefined, bigint |
Array, Object, Map, Set |
Including nested structures and circular references |
Date, RegExp |
|
ArrayBuffer, TypedArray, DataView |
Copied by default, or transferred (see below) |
Error types |
Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError |
Blob |
Requires Deno 2.8+ |
File |
Requires Deno 2.8+ |
DOMException |
|
CryptoKey |
Transferable types Jump to heading
These types can be transferred (not copied) via the transfer option in
structuredClone() or the transfer list in postMessage(). After transfer,
the original object becomes unusable:
| Type | Notes |
|---|---|
ArrayBuffer |
Moves the backing memory to the receiver |
MessagePort |
Transfers the port to another context |
ReadableStream |
Transfers the stream to another context |
WritableStream |
Transfers the stream to another context |
TransformStream |
Transfers the stream to another context |
// Clone a Blob
const blob = new Blob(["hello"], { type: "text/plain" });
const cloned = structuredClone(blob);
console.log(await cloned.text()); // "hello"
// Transfer an ArrayBuffer through a MessageChannel
const buffer = new ArrayBuffer(1024);
const ch = new MessageChannel();
ch.port1.postMessage(buffer, [buffer]);
// buffer.byteLength is now 0 (transferred)
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.
Spec deviations Jump to heading
- Events do not bubble, because Deno does not have a DOM hierarchy, so there is no tree for Events to bubble/capture through.
timeStampproperty is always set to0.
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".
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:
- When using the
--locationflag, the origin for the location is used to uniquely store the data. That means a location ofhttp://example.com/a.tsandhttp://example.com/b.tsandhttp://example.com:80/would all share the same storage, buthttps://example.com/would be different. - If there is no location specifier, but there is a
--configconfiguration file specified, the absolute path to that configuration file is used. That meansdeno run --config deno.jsonc a.tsanddeno run --config deno.jsonc b.tswould share the same storage, butdeno run --config tsconfig.json a.tswould be different. - If there is no configuration or location specifier, Deno uses the absolute
path to the main module to determine what storage is shared. The Deno REPL
generates a "synthetic" main module that is based off the current working
directory where
denois started from. This means that multiple invocations of the REPL from the same path will share the persistedlocalStoragedata.
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:
new Worker(import.meta.resolve("./worker.ts"), { type: "module" });
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:
new Worker("https://example.com/worker.ts", { type: "module" });
// 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
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
});
worker.postMessage({ filename: "./log.txt" });
self.onmessage = async (e) => {
const { filename } = e.data;
const text = await Deno.readTextFile(filename);
console.log(text);
self.close();
};
hello world
$ deno run --allow-read main.js
hello world
Specifying worker permissions Jump to heading
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 here.
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",
},
});
OffscreenCanvas Jump to heading
Starting in Deno 2.8, the
OffscreenCanvas
API is available. OffscreenCanvas is a canvas that lives outside any DOM and
can be used anywhere (including Web Workers) for off-thread rendering and image
generation.
Supported rendering contexts Jump to heading
OffscreenCanvas#getContext accepts two of the spec-defined context ids:
"bitmaprenderer": returns anImageBitmapRenderingContextfor displaying anImageBitmapproduced viacreateImageBitmap."webgpu": returns aGPUCanvasContextfor rendering with WebGPU.
Calling getContext with "2d", "webgl", or "webgl2" returns null; these
contexts are not implemented in Deno.
Example: encoding an image to PNG Jump to heading
Decode an image into an ImageBitmap, place it on an OffscreenCanvas via the
bitmaprenderer context, and write the result to disk:
const data = await Deno.readFile("./input.jpg");
const bitmap = await createImageBitmap(new Blob([data]));
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
const ctx = canvas.getContext("bitmaprenderer")!;
ctx.transferFromImageBitmap(bitmap);
const blob = await canvas.convertToBlob({ type: "image/png" });
await Deno.writeFile(
"./output.png",
new Uint8Array(await blob.arrayBuffer()),
);
Typical uses:
- producing thumbnails, format conversions, or social-card images at request time without spinning up a headless browser,
- running off-thread image work inside a Web Worker,
- driving WebGPU rendering targets that don't need a window.
Geometry Interfaces Jump to heading
Starting in Deno 2.8, the Geometry Interfaces Module Level 1 types are available as globals. These are the same types you'd find in a browser:
DOMMatrix/DOMMatrixReadOnly: 4×4 transform matrices for 2D and 3D operations.DOMPoint/DOMPointReadOnly: points in 2D / 3D space.DOMRect/DOMRectReadOnly: axis-aligned rectangles.DOMQuad: a quadrilateral defined by four points.
const m = new DOMMatrix().translateSelf(10, 20).scaleSelf(2);
const p = new DOMPoint(1, 1).matrixTransform(m);
console.log(p.x, p.y); // 12 22
These types are useful for graphics work; applying transforms to canvas drawings, computing layout math, or porting browser code that depends on geometry types.
navigator Jump to heading
Deno implements a subset of the
navigator
global. The following properties are available:
navigator.userAgent— always"Deno/<version>"navigator.platform— the underlying OS platform (e.g."Linux x86_64","MacIntel","Win32"). Added in Deno 2.7.navigator.hardwareConcurrency— number of logical CPU cores
console.log(navigator.userAgent); // "Deno/2.7.0"
console.log(navigator.platform); // e.g. "Linux x86_64", "MacIntel", "Win32"
console.log(navigator.hardwareConcurrency); // e.g. 8
Temporal Jump to heading
The Temporal API is a modern
date/time library that replaces Date for most use cases. It was stabilized in
Deno 2.7 and is available as a global without any flags.
// Current date/time in local timezone
const now = Temporal.Now.plainDateTimeISO();
console.log(now.toString()); // e.g. "2025-03-12T10:30:00"
// Parse a date
const date = Temporal.PlainDate.from("2025-03-12");
console.log(date.month); // 3
// Timezone-aware
const zonedNow = Temporal.Now.zonedDateTimeISO("America/New_York");
console.log(zonedNow.timeZoneId); // "America/New_York"
Prior to Deno 2.7, Temporal required the --unstable-temporal flag.
CompressionStream and DecompressionStream Jump to heading
Deno supports
CompressionStream
and
DecompressionStream
for streaming compression and decompression.
Supported formats Jump to heading
| Format | String | Notes |
|---|---|---|
| gzip | "gzip" |
RFC 1952 |
| deflate | "deflate" |
zlib (RFC 1950) |
| deflate-raw | "deflate-raw" |
raw DEFLATE (1951) |
| Brotli | "brotli" |
Added in Deno 2.7 |
// Compress with Brotli
const input = new TextEncoder().encode("Hello, Deno!");
const cs = new CompressionStream("brotli");
const writer = cs.writable.getWriter();
writer.write(input);
writer.close();
const compressed = await new Response(cs.readable).arrayBuffer();
// Decompress
const ds = new DecompressionStream("brotli");
const writer2 = ds.writable.getWriter();
writer2.write(new Uint8Array(compressed));
writer2.close();
const result = await new Response(ds.readable).text();
console.log(result); // "Hello, Deno!"
Web Crypto Jump to heading
Deno supports the
Web Crypto API
via crypto.subtle. Starting with Deno 2.7, SHA-3 hash algorithms are
supported:
SHA3-256SHA3-384SHA3-512
const data = new TextEncoder().encode("Hello, Deno!");
const hash = await crypto.subtle.digest("SHA3-256", data);
console.log(new Uint8Array(hash));
createImageBitmap Jump to heading
Deno supports
createImageBitmap()
for decoding images into ImageBitmap objects that can be used with
OffscreenCanvas.
Supported input formats Jump to heading
| Format | Notes |
|---|---|
| PNG | |
| JPEG | |
| BMP | |
| GIF | Added in Deno 2.7 |
| WebP | Added in Deno 2.7 |
const data = await Deno.readFile("./image.gif");
const bitmap = await createImageBitmap(new Blob([data]));
console.log(bitmap.width, bitmap.height);
File locking Jump to heading
Deno.FsFile supports advisory file locking to
coordinate access between processes:
lock(exclusive?)— acquires a lock. Shared (read) by default; passtruefor exclusive (write). Blocks if an incompatible lock is held.lockSync(exclusive?)— synchronous variant oflock().tryLock(exclusive?)— non-blocking. Returnstrueif the lock was acquired,falseotherwise. Added in Deno 2.7.tryLockSync(exclusive?)— synchronous variant oftryLock().
const file = await Deno.open("./data.txt", { read: true, write: true });
const locked = await file.tryLock(true); // exclusive
if (locked) {
await file.write(new TextEncoder().encode("hello"));
await file.unlock();
} else {
console.log("File is locked by another process, skipping.");
}
file.close();
Deviations of other APIs from spec Jump to heading
Cache API Jump to heading
Only the following APIs are implemented:
- CacheStorage::open()
- CacheStorage::has()
- CacheStorage::delete()
- CacheStorage::keys() (Deno 2.8+)
- Cache::match()
- Cache::put()
- Cache::delete()
- Cache::keys() (Deno 2.8+)
A few things that are different compared to browsers:
- You cannot pass relative paths to the APIs. The request can be an instance of Request or URL or a url string.
match()&delete()don't support query options yet.