On this page
Deno Namespace APIs
The globalDeno
namespace contains APIs that are not web standard, includingAPIs for reading from files, opening TCP sockets, serving HTTP, and executingsubprocesses, etc.
API DocumentationJump to heading
This section provides comprehensive documentation for all Deno-specific APIsavailable through the globalDeno
namespace. You canbrowse all symbols to view the complete list ofavailable APIs or search by category. Click on any function or interface to seedetailed documentation with examples
Below we highlight some of the most important Deno APIs to know.
File SystemJump to heading
The Deno runtime comes withvarious functions for working with files and directories.You will need to use --allow-read and --allow-write permissions to gain accessto the file system.
Refer to the links below for code examples of how to use the file systemfunctions.
- Reading files in streams
- Reading a text file (
Deno.readTextFile
) - Writing a text file (
Deno.writeTextFile
)
NetworkJump to heading
The Deno runtime comes withbuilt-in functions for dealing with connections to network ports.
Refer to the links below for code examples for common functions.
- Connect to the hostname and port (
Deno.connect
) - Announcing on the local transport address (
Deno.listen
)
For practical examples of networking functionality:
- HTTP Server: Hello world
- HTTP Server: Routing
- TCP Echo Server
- WebSockets example
- Build a chat app with WebSockets tutorial
SubprocessesJump to heading
The Deno runtime comes withbuilt-in functions for spinning up subprocesses.
Refer to the links below for code samples of how to create a subprocess.
ErrorsJump to heading
The Deno runtime comes with20 error classes that can beraised in response to a number of conditions.
Some examples are:
Deno.errors.NotFound;Deno.errors.WriteZero;
They can be used as below:
try{const file=await Deno.open("./some/file.txt");}catch(error){if(errorinstanceofDeno.errors.NotFound){console.error("the file was not found");}else{// otherwise re-throwthrow error;}}
HTTP ServerJump to heading
Deno has two HTTP Server APIs:
Deno.serve
: native,higher-level, supportsHTTP/1.1 and HTTP2, this is the preferred API to write HTTP servers in Deno.Deno.serveHttp
: native,low-level, supportsHTTP/1.1 and HTTP2.
To start an HTTP server on a given port, use theDeno.serve
function. Thisfunction takes a handler function that will be called for each incoming request,and is expected to return a response (or a promise resolving to a response). Forexample:
Deno.serve((_req)=>{returnnewResponse("Hello, World!");});
By defaultDeno.serve
will listen on port8000
, but this can be changed bypassing in a port number in options bag as the first or second argument.
You canread more about how to use the HTTP server APIs.
For practical examples of HTTP servers:
- Simple file server tutorial
- HTTP server serving files
- HTTP server with streaming
- HTTP server WebSockets
PermissionsJump to heading
Permissions are granted from the CLI when running thedeno
command. User codewill often assume its own set of required permissions, but there is no guaranteeduring execution that the set ofgranted permissions will align with this.
In some cases, ensuring a fault-tolerant program requires a way to interact withthe permission system at runtime.
Permission descriptorsJump to heading
On the CLI, read permission for/foo/bar
is represented as--allow-read=/foo/bar
. In runtime JS, it is represented as the following:
const desc={ name:"read", path:"/foo/bar"}asconst;
Other examples:
// Global write permission.const desc1={ name:"write"}asconst;// Write permission to `$PWD/foo/bar`.const desc2={ name:"write", path:"foo/bar"}asconst;// Global net permission.const desc3={ name:"net"}asconst;// Net permission to 127.0.0.1:8000.const desc4={ name:"net", host:"127.0.0.1:8000"}asconst;// High-resolution time permission.const desc5={ name:"hrtime"}asconst;
SeePermissionDescriptor
in APIreference for more details. Synchronous API counterparts (ex.Deno.permissions.querySync
) exist for all the APIs described below.
Query permissionsJump to heading
Check, by descriptor, if a permission is granted or not.
// deno run --allow-read=/foo main.tsconst desc1={ name:"read", path:"/foo"}asconst;console.log(await Deno.permissions.query(desc1));// PermissionStatus { state: "granted", partial: false }const desc2={ name:"read", path:"/foo/bar"}asconst;console.log(await Deno.permissions.query(desc2));// PermissionStatus { state: "granted", partial: false }const desc3={ name:"read", path:"/bar"}asconst;console.log(await Deno.permissions.query(desc3));// PermissionStatus { state: "prompt", partial: false }
If--deny-read
flag was used to restrict some of the filepaths, the resultwill containpartial: true
describing that not all subpaths have permissionsgranted:
// deno run --allow-read=/foo --deny-read=/foo/bar main.tsconst desc1={ name:"read", path:"/foo"}asconst;console.log(await Deno.permissions.query(desc1));// PermissionStatus { state: "granted", partial: true }const desc2={ name:"read", path:"/foo/bar"}asconst;console.log(await Deno.permissions.query(desc2));// PermissionStatus { state: "denied", partial: false }const desc3={ name:"read", path:"/bar"}asconst;console.log(await Deno.permissions.query(desc3));// PermissionStatus { state: "prompt", partial: false }
Permission statesJump to heading
A permission state can be either "granted", "prompt" or "denied". Permissionswhich have been granted from the CLI will query to{ state: "granted" }
. Thosewhich have not been granted query to{ state: "prompt" }
by default, while{ state: "denied" }
reserved for those which have been explicitly refused.This will come up inRequest permissions.
Permission strengthJump to heading
The intuitive understanding behind the result of the second query inQuery permissions is that read access was granted to/foo
and/foo/bar
is within/foo
so/foo/bar
is allowed to be read. Thishold true, unless the CLI-granted permission ispartial to the queriedpermissions (as an effect of using a--deny-*
flag).
We can also say thatdesc1
isstronger thandesc2
. This means that for any set of CLI-granted permissions:
- If
desc1
queries to{ state: "granted", partial: false }
then so mustdesc2
. - If
desc2
queries to{ state: "denied", partial: false }
then so mustdesc1
.
More examples:
const desc1={ name:"write"}asconst;// is stronger thanconst desc2={ name:"write", path:"/foo"}asconst;const desc3={ name:"net", host:"127.0.0.1"}asconst;// is stronger thanconst desc4={ name:"net", host:"127.0.0.1:8000"}asconst;
Request permissionsJump to heading
Request an ungranted permission from the user via CLI prompt.
// deno run main.tsconst desc1={ name:"read", path:"/foo"}asconst;const status1=await Deno.permissions.request(desc1);// ⚠️ Deno requests read access to "/foo". Grant? [y/n (y = yes allow, n = no deny)] yconsole.log(status1);// PermissionStatus { state: "granted", partial: false }const desc2={ name:"read", path:"/bar"}asconst;const status2=await Deno.permissions.request(desc2);// ⚠️ Deno requests read access to "/bar". Grant? [y/n (y = yes allow, n = no deny)] nconsole.log(status2);// PermissionStatus { state: "denied", partial: false }
If the current permission state is "prompt", a prompt will appear on the user'sterminal asking them if they would like to grant the request. The request fordesc1
was granted so its new status is returned and execution will continue asif--allow-read=/foo
was specified on the CLI. The request fordesc2
wasdenied so its permission state is downgraded from "prompt" to "denied".
If the current permission state is already either "granted" or "denied", therequest will behave like a query and just return the current status. Thisprevents prompts both for already granted permissions and previously deniedrequests.
Revoke permissionsJump to heading
Downgrade a permission from "granted" to "prompt".
// deno run --allow-read=/foo main.tsconst desc={ name:"read", path:"/foo"}asconst;console.log(await Deno.permissions.revoke(desc));// PermissionStatus { state: "prompt", partial: false }
What happens when you try to revoke a permission which ispartial to onegranted on the CLI?
// deno run --allow-read=/foo main.tsconst desc={ name:"read", path:"/foo/bar"}asconst;console.log(await Deno.permissions.revoke(desc));// PermissionStatus { state: "prompt", partial: false }const cliDesc={ name:"read", path:"/foo"}asconst;console.log(await Deno.permissions.revoke(cliDesc));// PermissionStatus { state: "prompt", partial: false }
The CLI-granted permission, which implies the revoked permission, was alsorevoked.
To understand this behavior, imagine that Deno stores an internal set ofexplicitly granted permission descriptors. Specifying--allow-read=/foo,/bar
on the CLI initializes this set to:
[{ name:"read", path:"/foo"},{ name:"read", path:"/bar"},];
Granting a runtime request for{ name: "write", path: "/foo" }
updates the setto:
[{ name:"read", path:"/foo"},{ name:"read", path:"/bar"},{ name:"write", path:"/foo"},];
Deno's permission revocation algorithm works by removing every element from thisset which isstronger than the argument permission descriptor.
Deno does not allow "fragmented" permission states, where some strong permissionis granted with exclusions of weak permissions implied by it. Such a systemwould prove increasingly complex and unpredictable as you factor in a widervariety of use cases and the"denied"
state. This is a calculated trade-off ofgranularity for security.
import.metaJump to heading
Deno supports a number of properties and methods on theimport.meta
API. It can be used to get information about the module, such as the module'sURL.
import.meta.urlJump to heading
Returns the URL of the current module.
console.log(import.meta.url);
$ deno run main.tsfile:///dev/main.ts$ deno run https:/example.com/main.tshttps://example.com/main.ts
import.meta.mainJump to heading
Returns whether the current module is the entry point to your program.
import"./other.ts";console.log(`Is${import.meta.url} the main module?`,import.meta.main);
console.log(`Is${import.meta.url} the main module?`,import.meta.main);
$ deno run main.tsIs file:///dev/other.ts the main module?falseIs file:///dev/main.ts the main module?true
import.meta.filenameJump to heading
This property is only available for local modules (module that havefile:///...
specifier) and returnsundefined
for remote modules.
Returns the fully resolved path to the current module. The value contains OSspecific path separators.
console.log(import.meta.filename);
On Unix:
$ deno run main.ts/dev/main.ts$ deno run https://example.com/main.tsundefined
On Windows:
$ deno run main.tsC:\dev\main.ts$ deno run https://example.com/main.tsundefined
import.meta.dirnameJump to heading
This property is only available for local modules (module that havefile:///...
specifier) and returnsundefined
for remote modules.
Returns the fully resolved path to the directory containing the current module.The value contains OS specific path separators.
console.log(import.meta.dirname);
On Unix:
$ deno run main.ts/dev/$ deno run https://example.com/main.tsundefined
On Windows:
$ deno run main.tsC:\dev\$ deno run https://example.com/main.tsundefined
import.meta.resolveJump to heading
Resolve specifiers relative to the current module.
const worker=newWorker(import.meta.resolve("./worker.ts"));
Theimport.meta.resolve
API takes into account the currently applied importmap, which gives you the ability to resolve "bare" specifiers as well.
With such import map loaded...
{"imports":{"fresh":"https://deno.land/x/fresh@1.0.1/dev.ts"}}
...you can now resolve:
console.log(import.meta.resolve("fresh"));
$ deno run resolve.jshttps://deno.land/x/fresh@1.0.1/dev.ts
FFIJump to heading
The FFI (foreign function interface) API allows users to call libraries writtenin native languages that support the C ABIs (C/C++, Rust, Zig, V, etc.) usingDeno.dlopen
.
Here's an example showing how to call a Rust function from Deno:
// add.rs#[no_mangle]pub extern "C" fn add(a: isize, b: isize) -> isize { a + b}
Compile it to a C dynamic library (libadd.so
on Linux):
rustc --crate-type cdylib add.rs
In C you can write it as:
// add.cint add(int a, int b) { return a + b;}
And compile it:
// unixcc-c-o add.o add.ccc-shared-W-o libadd.so add.o// Windowscl /LD add.c /link /EXPORT:add
Calling the library from Deno:
// ffi.ts// Determine library extension based on// your OS.let libSuffix="";switch(Deno.build.os){case"windows": libSuffix="dll";break;case"darwin": libSuffix="dylib";break;default: libSuffix="so";break;}const libName=`./libadd.${libSuffix}`;// Open library and define exported symbolsconst dylib= Deno.dlopen( libName,{"add":{ parameters:["isize","isize"], result:"isize"},}asconst,);// Call the symbol `add`const result= dylib.symbols.add(35,34);// 69console.log(`Result from external addition of 35 and 34:${result}`);
Run with--allow-ffi
and--unstable
flag:
deno run --allow-ffi--unstable ffi.ts
Non-blocking FFIJump to heading
There are many use cases where users might want to run CPU-bound FFI functionsin the background without blocking other tasks on the main thread.
As of Deno 1.15, symbols can be markednonblocking
inDeno.dlopen
. Thesefunction calls will run on a dedicated blocking thread and will return aPromise
resolving to the desiredresult
.
Example of executing expensive FFI calls with Deno:
// sleep.c#ifdef _WIN32#include#else#include#endifint sleep(unsigned int ms) { #ifdef _WIN32 Sleep(ms); #else struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; nanosleep(&ts, NULL); #endif}
Calling it from Deno:
// nonblocking_ffi.tsconst library= Deno.dlopen("./sleep.so",{ sleep:{ parameters:["usize"], result:"void", nonblocking:true,},}asconst,);library.symbols.sleep(500).then(()=>console.log("After"));console.log("Before");
Result:
$ deno run --allow-ffi--unstable unblocking_ffi.tsBeforeAfter
CallbacksJump to heading
Deno FFI API supports creating C callbacks from JavaScript functions for callingback into Deno from dynamic libraries. An example of how callbacks are createdand used is as follows:
// callback_ffi.tsconst library= Deno.dlopen("./callback.so",{ set_status_callback:{ parameters:["function"], result:"void",}, start_long_operation:{ parameters:[], result:"void",}, check_status:{ parameters:[], result:"void",},}asconst,);const callback=newDeno.UnsafeCallback({ parameters:["u8"], result:"void",}asconst,(success:number)=>{},);// Pass the callback pointer to dynamic librarylibrary.symbols.set_status_callback(callback.pointer);// Start some long operation that does not block the threadlibrary.symbols.start_long_operation();// Later, trigger the library to check if the operation is done.// If it is, this call will trigger the callback.library.symbols.check_status();
If anUnsafeCallback
's callback function throws an error, the error will getpropagated up to the function that triggered the callback to be called (above,that would becheck_status()
) and can be caught there. If a callback returninga value throws then Deno will return 0 (null pointer for pointers) as theresult.
UnsafeCallback
is not deallocated by default as it can cause use-after-freebugs. To properly dispose of anUnsafeCallback
itsclose()
method must becalled.
const callback=newDeno.UnsafeCallback({ parameters:[], result:"void"}asconst,()=>{},);// After callback is no longer neededcallback.close();// It is no longer safe to pass the callback as a parameter.
It is also possible for native libraries to setup interrupt handlers and to havethose directly trigger the callback. However, this is not recommended and maycause unexpected side-effects and undefined behaviour. Preferably any interrupthandlers would only set a flag that can later be polled similarly to howcheck_status()
is used above.
Supported typesJump to heading
Here's a list of types supported currently by the Deno FFI API.
FFI Type | Deno | C | Rust |
---|---|---|---|
i8 | number | char /signed char | i8 |
u8 | number | unsigned char | u8 |
i16 | number | short int | i16 |
u16 | number | unsigned short int | u16 |
i32 | number | int /signed int | i32 |
u32 | number | unsigned int | u32 |
i64 | bigint | long long int | i64 |
u64 | bigint | unsigned long long int | u64 |
usize | bigint | size_t | usize |
isize | bigint | size_t | isize |
f32 | number | float | f32 |
f64 | number | double | f64 |
void [1] | undefined | void | () |
pointer | {} | null | void * | *mut c_void |
buffer [2] | TypedArray | null | uint8_t * | *mut u8 |
function [3] | {} | null | void (*fun)() | Option<extern "C" fn()> |
{ struct: [...] } [4] | TypedArray | struct MyStruct | MyStruct |
As of Deno 1.25, thepointer
type has been split into apointer
and abuffer
type to ensure users take advantage of optimizations for Typed Arrays,and as of Deno 1.31 the JavaScript representation ofpointer
has become anopaque pointer object ornull
for null pointers.
- [1]
void
type can only be used as a result type. - [2]
buffer
type accepts TypedArrays as parameter, but it always returns apointer object ornull
when used as result type like thepointer
type. - [3]
function
type works exactly the same as thepointer
type as aparameter and result type. - [4]
struct
type is for passing and returning C structs by value (copy). Thestruct
array must enumerate each of the struct's fields' type in order. Thestructs are padded automatically: Packed structs can be defined by using anappropriate amount ofu8
fields to avoid padding. Only TypedArrays aresupported as structs, and structs are always returned asUint8Array
s.
deno_bindgenJump to heading
deno_bindgen
is the official toolto simplify glue code generation of Deno FFI libraries written in Rust.
It is similar towasm-bindgen
inthe Rust Wasm ecosystem.
Here's an example showing its usage:
// mul.rsuse deno_bindgen::deno_bindgen;#[deno_bindgen]struct Input { a: i32, b: i32,}#[deno_bindgen]fn mul(input: Input) -> i32 { input.a * input.b}
Rundeno_bindgen
to generate bindings. You can now directly import them intoDeno:
// mul.tsimport{ mul}from"./bindings/bindings.ts";mul({ a:10, b:2});// 20
Any issues related todeno_bindgen
should be reported athttps://github.com/denoland/deno_bindgen/issues
Program LifecycleJump to heading
Deno supports browser compatible lifecycle events:
load
:fired when the whole page has loaded, including all dependent resources suchas stylesheets and images.beforeunload
:fired when the event loop has no more work to do and is about to exit.Scheduling more asynchronous work (like timers or network requests) will causethe program to continue.unload
:fired when the document or a child resource is being unloaded.unhandledrejection
:fired when a promise that has no rejection handler is rejected, ie. a promisethat has no.catch()
handler or a second argument to.then()
.rejectionhandled
:fired when a.catch()
handler is added to a a promise that has alreadyrejected. This event is fired only if there'sunhandledrejection
listenerinstalled that prevents propagation of the event (which would result in theprogram terminating with an error).
You can use these events to provide setup and cleanup code in your program.
Listeners forload
events can be asynchronous and will be awaited, this eventcannot be canceled. Listeners forbeforeunload
need to be synchronous and canbe cancelled to keep the program running. Listeners forunload
events need tobe synchronous and cannot be cancelled.
main.tsJump to heading
import"./imported.ts";const handler=(e: Event):void=>{console.log(`got${e.type} event in event handler (main)`);};globalThis.addEventListener("load", handler);globalThis.addEventListener("beforeunload", handler);globalThis.addEventListener("unload", handler);globalThis.onload=(e: Event):void=>{console.log(`got${e.type} event in onload function (main)`);};globalThis.onbeforeunload=(e: Event):void=>{console.log(`got${e.type} event in onbeforeunload function (main)`);};globalThis.onunload=(e: Event):void=>{console.log(`got${e.type} event in onunload function (main)`);};console.log("log from main script");
const handler=(e: Event):void=>{console.log(`got${e.type} event in event handler (imported)`);};globalThis.addEventListener("load", handler);globalThis.addEventListener("beforeunload", handler);globalThis.addEventListener("unload", handler);globalThis.onload=(e: Event):void=>{console.log(`got${e.type} event in onload function (imported)`);};globalThis.onbeforeunload=(e: Event):void=>{console.log(`got${e.type} event in onbeforeunload function (imported)`);};globalThis.onunload=(e: Event):void=>{console.log(`got${e.type} event in onunload function (imported)`);};console.log("log from imported script");
A couple notes on this example:
addEventListener
andonload
/onunload
are prefixed withglobalThis
, butyou could also useself
or no prefix at all.It is not recommended to usewindow
as a prefix.- You can use
addEventListener
and/oronload
/onunload
to define handlersfor events. There is a major difference between them, let's run the example:
$ deno run main.tslog from imported scriptlog from main scriptgot load eventin event handler(imported)got load eventin event handler(main)got load eventin onloadfunction(main)got onbeforeunload eventin event handler(imported)got onbeforeunload eventin event handler(main)got onbeforeunload eventin onbeforeunloadfunction(main)got unload eventin event handler(imported)got unload eventin event handler(main)got unload eventin onunloadfunction(main)
All listeners added usingaddEventListener
were run, butonload
,onbeforeunload
andonunload
defined inmain.ts
overrode handlers definedinimported.ts
.
In other words, you can useaddEventListener
to register multiple"load"
or"unload"
event handlers, but only the last definedonload
,onbeforeunload
,onunload
event handlers will be executed. It is preferable to useaddEventListener
when possible for this reason.
beforeunloadJump to heading
// beforeunload.jslet count=0;console.log(count);globalThis.addEventListener("beforeunload",(e)=>{ console.log("About to exit..."); count++;});globalThis.addEventListener("unload",(e)=>{ console.log("Exiting");});count++;