NexusCS

Deno

JavaScript Runtimes
Quick reference for Deno - a modern JavaScript/TypeScript runtime with secure defaults, built-in tooling, and native TypeScript support.
deno
typescript
runtime
javascript

Getting started

Installation

# macOS/Linux (Shell)
curl -fsSL https://deno.land/install.sh | sh

# macOS (Homebrew)
brew install deno

# Windows (PowerShell)
irm https://deno.land/install.ps1 | iex

# Cargo (Rust)
cargo install deno --locked

# Verify installation
deno --version

Hello World

// hello.ts
console.log("Hello from Deno!");

// Run it
deno run hello.ts

Quick Example

// fetch.ts
const response = await fetch("https://api.github.com/users/denoland");
const data = await response.json();
console.log(data.name);
# Requires network permission
deno run --allow-net fetch.ts

First HTTP Server

// server.ts
Deno.serve({ port: 8000 }, (_req) => {
  return new Response("Hello, World!");
});
# Run with network permission
deno run --allow-net server.ts

Running Code

Basic Execution

# Run TypeScript/JavaScript
deno run script.ts
deno run script.js

# Run from URL
deno run https://deno.land/std/examples/welcome.ts

# Watch mode (auto-restart)
deno run --watch script.ts

# Check syntax without running
deno check script.ts

REPL (Interactive Shell)

# Start REPL
deno

# REPL with imports
deno repl

# Evaluate expression
deno eval "console.log(1 + 2)"

Script Arguments

// args.ts
console.log(Deno.args);
deno run args.ts arg1 arg2
# Output: ["arg1", "arg2"]

Environment Variables

// Get env var
const home = Deno.env.get("HOME");

// Set env var
Deno.env.set("MY_VAR", "value");

// Delete env var
Deno.env.delete("MY_VAR");
# Run with env permission
deno run --allow-env script.ts

# Run with specific env access
deno run --allow-env=HOME script.ts

Permissions

Security Model

Deno is secure by default. All permissions must be explicitly granted.

Permission Flag Description
Network --allow-net Network access
Read --allow-read File system read
Write --allow-write File system write
Environment --allow-env Environment variables
Run --allow-run Subprocess execution
FFI --allow-ffi Foreign Function Interface
HRTime --allow-hrtime High-resolution time
All --allow-all or -A Grant all permissions

Permission Examples

# Network access to specific domain
deno run --allow-net=api.github.com fetch.ts

# Read from specific directory
deno run --allow-read=/tmp script.ts

# Multiple permissions
deno run --allow-net --allow-read --allow-write server.ts

# All permissions (unsafe!)
deno run -A script.ts

# Prompt for permissions
deno run --prompt script.ts

Runtime Permission Checks

// Request permission
const status = await Deno.permissions.request({ name: "read" });
if (status.state === "granted") {
  const data = await Deno.readTextFile("file.txt");
}

// Query permission status
const netStatus = await Deno.permissions.query({ name: "net" });
console.log(netStatus.state); // "granted" | "denied" | "prompt"

// Revoke permission
await Deno.permissions.revoke({ name: "read" });

Modules & Imports

ESM Imports

// URL imports (no package.json)
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

// Relative imports
import { helper } from "./utils.ts";
import { config } from "../config.ts";

// Standard library
import { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts";

Import Maps

// import_map.json
{
  "imports": {
    "std/": "https://deno.land/std@0.208.0/",
    "oak": "https://deno.land/x/oak@v12.6.1/mod.ts",
    "~/": "./src/"
  }
}
// Use with import map
import { serve } from "std/http/server.ts";
import { Application } from "oak";
import { helper } from "~/utils.ts";
# Run with import map
deno run --import-map=import_map.json script.ts

deno.json Configuration

{
  "imports": {
    "std/": "https://deno.land/std@0.208.0/",
    "~/": "./src/"
  },
  "tasks": {
    "dev": "deno run --watch main.ts"
  },
  "lint": {
    "include": ["src/"]
  },
  "fmt": {
    "useTabs": false,
    "indentWidth": 2
  }
}

npm Compatibility

// Import npm packages with npm: specifier
import express from "npm:express@4";
import chalk from "npm:chalk@5";

// Node built-ins with node: specifier
import { readFile } from "node:fs/promises";
import { Buffer } from "node:buffer";

Standard Library

Common Modules

// HTTP server
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

// File system utilities
import { ensureDir, copy } from "https://deno.land/std@0.208.0/fs/mod.ts";

// Path manipulation
import { join, dirname } from "https://deno.land/std@0.208.0/path/mod.ts";

// Assertions (testing)
import {
  assertEquals,
  assertExists,
} from "https://deno.land/std@0.208.0/assert/mod.ts";

// Encoding/Decoding
import {
  encode,
  decode,
} from "https://deno.land/std@0.208.0/encoding/base64.ts";

File System

// Read file
const text = await Deno.readTextFile("file.txt");
const bytes = await Deno.readFile("file.bin");

// Write file
await Deno.writeTextFile("output.txt", "content");
await Deno.writeFile("output.bin", new Uint8Array([1, 2, 3]));

// Directory operations
for await (const entry of Deno.readDir(".")) {
  console.log(entry.name);
}

// File info
const info = await Deno.stat("file.txt");
console.log(info.size, info.isFile, info.mtime);

HTTP Client

// Fetch API (built-in)
const response = await fetch("https://api.example.com/data");
const json = await response.json();

// With headers
const response2 = await fetch("https://api.example.com/", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ key: "value" }),
});

HTTP Server

import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

// Simple server
serve((_req) => new Response("Hello!"), { port: 8000 });

// With routing
serve((req) => {
  const url = new URL(req.url);
  if (url.pathname === "/") {
    return new Response("Home");
  }
  return new Response("Not Found", { status: 404 });
});

// Modern Deno.serve (v1.35+)
Deno.serve({ port: 8000 }, (req) => {
  return new Response("Hello from Deno.serve!");
});

Testing

Basic Tests

// math_test.ts
import { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts";

Deno.test("addition works", () => {
  assertEquals(1 + 1, 2);
});

Deno.test("async test", async () => {
  const response = await Promise.resolve(42);
  assertEquals(response, 42);
});
# Run tests
deno test

# Run specific test file
deno test math_test.ts

# Watch mode
deno test --watch

# Filter tests
deno test --filter "addition"

Assertions

import {
  assertEquals,
  assertNotEquals,
  assertExists,
  assertStringIncludes,
  assertMatch,
  assertThrows,
  assertRejects,
} from "https://deno.land/std@0.208.0/assert/mod.ts";

// Value assertions
assertEquals(actual, expected);
assertNotEquals(actual, expected);
assertExists(value);

// String assertions
assertStringIncludes("hello world", "world");
assertMatch("deno", /de[no]+/);

// Error assertions
assertThrows(() => {
  throw new Error();
});
await assertRejects(() => Promise.reject(new Error()));

Test Steps

Deno.test("multi-step test", async (t) => {
  await t.step("setup", () => {
    // Setup code
  });

  await t.step("execute", () => {
    // Test code
  });

  await t.step("teardown", () => {
    // Cleanup code
  });
});

Mocking & Spying

import {
  spy,
  assertSpyCalls,
} from "https://deno.land/std@0.208.0/testing/mock.ts";

Deno.test("spy example", () => {
  const func = spy();
  func("arg1", "arg2");

  assertSpyCalls(func, 1);
});

Formatting & Linting

deno fmt (Formatter)

# Format files
deno fmt

# Format specific files
deno fmt src/

# Check without writing
deno fmt --check

# Ignore files
deno fmt --ignore=dist/

# Format with options
deno fmt --use-tabs --line-width=100

Configuration (deno.json)

{
  "fmt": {
    "useTabs": false,
    "lineWidth": 80,
    "indentWidth": 2,
    "semiColons": true,
    "singleQuote": false,
    "proseWrap": "preserve",
    "include": ["src/"],
    "exclude": ["dist/", "coverage/"]
  }
}

deno lint (Linter)

# Lint files
deno lint

# Lint specific files
deno lint src/

# Lint with JSON output
deno lint --json

# List available rules
deno lint --rules

# Ignore specific rules
deno lint --ignore=no-unused-vars

Lint Configuration

{
  "lint": {
    "include": ["src/"],
    "exclude": ["dist/"],
    "rules": {
      "tags": ["recommended"],
      "include": ["ban-untagged-todo"],
      "exclude": ["no-unused-vars"]
    }
  }
}

Inline Lint Directives

// Ignore next line
// deno-lint-ignore no-explicit-any
const data: any = {};

// Ignore entire file
// deno-lint-ignore-file no-unused-vars

Task Runner

deno.json Tasks

{
  "tasks": {
    "dev": "deno run --watch --allow-net main.ts",
    "start": "deno run --allow-net --allow-read main.ts",
    "test": "deno test --allow-read",
    "bench": "deno bench",
    "bundle": "deno bundle main.ts dist/bundle.js"
  }
}
# Run tasks
deno task dev
deno task test
deno task start

Task Composition

{
  "tasks": {
    "lint": "deno lint",
    "fmt": "deno fmt",
    "test": "deno test",
    "check": "deno task lint && deno task fmt --check && deno task test"
  }
}

Shell Commands

{
  "tasks": {
    "clean": "rm -rf dist/",
    "build": "deno task clean && deno compile main.ts"
  }
}

Node.js Compatibility

npm Packages

// Direct npm imports
import express from "npm:express@4.18.2";
import lodash from "npm:lodash@4.17.21";

// With types
import React from "npm:react@18";
import type { ReactNode } from "npm:@types/react@18";

// CommonJS modules
const _ = require("npm:lodash");

Node Built-ins

// Node.js built-in modules with node: specifier
import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { Buffer } from "node:buffer";
import { EventEmitter } from "node:events";
import path from "node:path";

package.json Support

// package.json
{
  "dependencies": {
    "express": "^4.18.2",
    "lodash": "^4.17.21"
  }
}
// Import from package.json dependencies
import express from "express";
# Run with package.json
deno run --allow-read --allow-net main.ts

Compatibility Mode

# Run Node.js scripts
deno run --compat --unstable main.js

# Use npm install (creates node_modules)
deno run --allow-read --allow-write npm:npm install

Deployment

Compile to Binary

# Compile to executable
deno compile --output=myapp main.ts

# Cross-compile
deno compile --target=x86_64-unknown-linux-gnu main.ts
deno compile --target=x86_64-pc-windows-msvc main.ts
deno compile --target=x86_64-apple-darwin main.ts
deno compile --target=aarch64-apple-darwin main.ts

# With permissions
deno compile --allow-net --allow-read main.ts

Deno Deploy

// main.ts for Deno Deploy
Deno.serve((_req) => {
  return new Response("Hello from Deno Deploy!");
});
# Deploy with deployctl
deno install --allow-all --no-check -r -f https://deno.land/x/deploy/deployctl.ts

deployctl deploy --project=my-project main.ts

GitHub Actions

# .github/workflows/deno.yml
name: Deno CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: denoland/setup-deno@v1
        with:
          deno-version: v1.x
      - run: deno lint
      - run: deno fmt --check
      - run: deno test --allow-read

Docker

FROM denoland/deno:1.39.0

WORKDIR /app
COPY . .
RUN deno cache main.ts

EXPOSE 8000
CMD ["run", "--allow-net", "main.ts"]

Advanced Features

Workers

// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url).href, {
  type: "module",
  deno: {
    permissions: {
      net: true,
    },
  },
});

worker.postMessage({ cmd: "fetch" });
worker.onmessage = (e) => console.log(e.data);
// worker.ts
self.onmessage = async (e) => {
  if (e.data.cmd === "fetch") {
    const response = await fetch("https://api.example.com");
    self.postMessage(await response.json());
  }
};

WebAssembly

// Load and run WASM
const wasmCode = await Deno.readFile("./module.wasm");
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);

const result = wasmInstance.exports.add(5, 3);
console.log(result);

FFI (Foreign Function Interface)

// Call native libraries
const dylib = Deno.dlopen("./libexample.so", {
  add: { parameters: ["i32", "i32"], result: "i32" },
});

const result = dylib.symbols.add(5, 3);
console.log(result);
dylib.close();
# Run with FFI permission
deno run --allow-ffi --unstable script.ts

WebSocket

// WebSocket client
const ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = () => {
  ws.send("Hello Server!");
};

ws.onmessage = (e) => {
  console.log("Message:", e.data);
};

ws.onclose = () => console.log("Disconnected");

Common Tasks

Dependency Management

# Cache dependencies
deno cache deps.ts

# Reload dependencies
deno cache --reload deps.ts

# Show dependency tree
deno info main.ts

# Update dependencies
deno cache --reload=https://deno.land/std main.ts

Vendoring Dependencies

# Vendor remote dependencies
deno vendor main.ts

# Creates vendor/ directory with all deps
deno run --import-map=vendor/import_map.json main.ts

Lock File

# Generate lock file
deno cache --lock=deno.lock --lock-write deps.ts

# Check integrity
deno cache --lock=deno.lock deps.ts

Documentation

# Generate docs
deno doc main.ts

# Export as JSON
deno doc --json main.ts > docs.json

# Serve docs
deno doc --html --name="My Project" main.ts

Benchmarking

// bench.ts
Deno.bench("string concat", () => {
  let str = "";
  for (let i = 0; i < 100; i++) {
    str += "a";
  }
});

Deno.bench("array join", () => {
  const arr = [];
  for (let i = 0; i < 100; i++) {
    arr.push("a");
  }
  arr.join("");
});
# Run benchmarks
deno bench

Migration from Node.js

Key Differences

Feature Node.js Deno
Package manager npm/yarn/pnpm Built-in (URL imports)
Module system CommonJS/ESM ESM only
TypeScript Requires setup Native support
Security No restrictions Secure by default
Tooling External tools Built-in fmt/lint/test
Configuration package.json deno.json

Import Changes

// Node.js
const express = require("express");
import express from "express";

// Deno
import express from "npm:express@4";

// Node.js built-ins
// Node: import { readFile } from "fs/promises";
// Deno: import { readFile } from "node:fs/promises";

Common Replacements

// __dirname and __filename
// Node.js: __dirname, __filename
// Deno:
const __filename = new URL("", import.meta.url).pathname;
const __dirname = new URL(".", import.meta.url).pathname;

// process.env
// Node.js: process.env.VAR
// Deno: Deno.env.get("VAR")

// process.exit()
// Node.js: process.exit(1)
// Deno: Deno.exit(1)

// Buffer
// Node.js: Buffer.from("hello")
// Deno: new TextEncoder().encode("hello")

Express Server Example

// Node.js
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("Hello!"));
app.listen(3000);

// Deno
import express from "npm:express@4";
const app = express();
app.get("/", (_req, res) => res.send("Hello!"));
app.listen(3000);

Gotchas & Tips

Common Mistakes

Forgetting Permissions

# ❌ Error: Requires network access
deno run fetch.ts

# ✅ Grant network permission
deno run --allow-net fetch.ts

URL Import Versioning

// ❌ No version pinning
import { serve } from "https://deno.land/std/http/server.ts";

// ✅ Pin versions
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";

Missing File Extensions

// ❌ No extension
import { helper } from "./utils";

// ✅ Include extension
import { helper } from "./utils.ts";

Performance Tips

// Use Deno.serve (faster than std/http)
Deno.serve({ port: 8000 }, handler);

// Cache dependencies
// deno cache --lock=deno.lock deps.ts

// Compile for production
// deno compile --allow-net main.ts

Best Practices

  • Pin versions in imports: std@0.208.0
  • Use deno.json for configuration
  • Enable --watch during development
  • Leverage built-in tools (fmt, lint, test)
  • Use import maps for cleaner imports
  • Request minimum permissions needed
  • Cache deps in CI/CD pipelines

Also see