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.
set-cookie
header on a response is not processed, or filtered from the visible response headers.fetch
specification:
3.1. 'Origin' header
.3.2. CORS protocol
.3.5. CORB
.3.6. 'Cross-Origin-Resource-Policy' header
.Atomic HTTP redirect handling
.opaqueredirect
response type.fetch
with a redirect
mode of manual
will return a basic
response rather than an opaqueredirect
response.file:
URLs are to be handled. Firefox is the only mainstream browser that implements fetching file:
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.request
and response
header guards are implemented, but unlike browsers do not have any constraints on which header names are allowed.referrer
, referrerPolicy
, mode
, credentials
, cache
, integrity
, keepalive
, and window
properties and their relevant behaviours in RequestInit
are not implemented. The relevant fields are not present on the Request
object.set-cookie
header is not concatenated when iterated over in the headers
iterator. This behaviour is in the process of being specified.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:
--allow-read
permission is needed to be able to read a local file.GET
method, and will reject the promise with any other method.TypeError
. This is to avoid the potential of fingerprinting attacks.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#timeStamp
property is always set to 0
.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.
Deno supports the location
global from the web.
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.
console.log(location.href);
You must pass --location <href>
for this to work. If you don't, any access to the location
global will throw an error.
console.log(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.
location.pathname = "./foo";
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.
const response = await fetch("./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.
const worker = new Worker("./workers/hello.ts", { type: "module" });
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.
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:
--location
flag, the origin for the location is used to uniquely store the data. That means a location of http://example.com/a.ts
and http://example.com/b.ts
and http://example.com:80/
would all share the same storage, but https://example.com/
would be different.--config
configuration file specified, the absolute path to that configuration file is used. That means deno run --config deno.jsonc a.ts
and deno run --config deno.jsonc b.ts
would share the same storage, but deno run --config tsconfig.json a.ts
would be different.deno
is started from. This means that multiple invocations of the REPL from the same path will share the persisted localStorage
data.To set, get and remove items from localStorage
, you can use the following:
localStorage.setItem("myDemo", "Deno App");
const cat = localStorage.getItem("myDemo");
localStorage.removeItem("myDemo");
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.
new Worker(import.meta.resolve("./worker.js"), { type: "module" });
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";
await delay(1000);
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
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();
};
$ 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:
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
deno: {
permissions: "inherit",
},
});
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:
const worker = new Worker(import.meta.resolve("./worker.js"), {
type: "module",
});
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:
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:
match()
& delete()
don't support query options yet.RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4