Skip to content

Instantly share code, notes, and snippets.

@mosioc
Created January 23, 2026 15:29
Show Gist options
  • Select an option

  • Save mosioc/6309d64ba85f277ba48498f673a1ed99 to your computer and use it in GitHub Desktop.

Select an option

Save mosioc/6309d64ba85f277ba48498f673a1ed99 to your computer and use it in GitHub Desktop.
Deno cheat sheet

Deno Cheat Sheet

A modern, secure runtime for JavaScript and TypeScript built on V8, Rust, and Tokio.


Core Concepts

What is Deno?

  • Secure by default: No file, network, or environment access without explicit permission
  • TypeScript native: First-class TypeScript support without configuration
  • Modern standards: Built around web platform APIs (fetch, WebSocket, etc.)
  • Single executable: No package manager required, dependencies loaded via URLs
  • Tooling included: Built-in formatter, linter, test runner, bundler, and LSP

Key Differences from Node.js

  • No node_modules, no package.json by default
  • Permissions are explicit and granular
  • ES modules only (no CommonJS)
  • Top-level await supported everywhere
  • Uses URLs for imports instead of bare specifiers

Installation & Setup

Installation

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

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

# Homebrew
brew install deno

# Cargo
cargo install deno --locked

Version Management

deno upgrade              # Upgrade to latest
deno upgrade --version 1.40.0  # Specific version
deno --version            # Check current version

Permissions System

Permission Flags

Deno is secure by default. All permissions are denied unless explicitly granted.

Flag Description Example
--allow-read File system read access --allow-read=/tmp
--allow-write File system write access --allow-write=./data
--allow-net Network access --allow-net=api.github.com
--allow-env Environment variable access --allow-env=HOME,PATH
--allow-run Subprocess execution --allow-run=git,npm
--allow-ffi Foreign Function Interface --allow-ffi
--allow-hrtime High-resolution time measurement --allow-hrtime
--allow-all or -A Grant all permissions -A (use cautiously)

Permission Scope

# Grant specific access
deno run --allow-read=/home/user --allow-net=deno.land script.ts

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

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

Permissions can be global (--allow-net) or scoped to specific resources (--allow-net=api.com). This prevents malicious code from accessing sensitive resources.


Module System

Import Syntax

Deno uses ES modules exclusively with URL-based imports.

// Remote imports (from URL)
import { serve } from "https://deno.land/std@0.200.0/http/server.ts";

// Local imports (relative path)
import { helper } from "./utils.ts";
import { config } from "../config.ts";

// Import maps (for cleaner imports)
import { oak } from "oak";  // Requires deno.json configuration

Import Maps

Define in deno.json to use bare specifiers:

{
  "imports": {
    "oak": "https://deno.land/x/oak@v12.6.0/mod.ts",
    "std/": "https://deno.land/std@0.200.0/",
    "@/": "./src/"
  }
}
// Now you can use clean imports
import { Application } from "oak";
import { assertEquals } from "std/assert/mod.ts";
import { utils } from "@/utils.ts";

Import maps translate bare specifiers to full URLs, making code cleaner and dependencies centralized.

Dependency Management

# Cache dependencies
deno cache deps.ts

# View dependency tree
deno info main.ts

# Reload and bypass cache
deno run --reload main.ts

# Lock dependencies
deno cache --lock=deno.lock --lock-write deps.ts

Deno caches remote modules locally. The lock file ensures reproducible builds by pinning exact versions.


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 on changes)
deno run --watch server.ts

# With permissions
deno run --allow-net --allow-read server.ts

REPL (Read-Eval-Print Loop)

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

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

Deno.test({
  name: "async test",
  async fn() {
    const result = await Promise.resolve(42);
    assertEquals(result, 42);
  },
});

bash

deno test                 # Run all tests
deno test file_test.ts    # Run specific test
deno test --coverage      # Generate coverage

Built-in test runner with no external dependencies. Tests use Deno.test() API.

Benchmarking

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

Documentation

bash

deno doc mod.ts           # Generate docs
deno doc --json mod.ts    # JSON output

Standard Library

The Deno standard library provides audited, high-quality modules.

Common Modules

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

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

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

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

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

// Datetime
import { format, parse } from "https://deno.land/std@0.200.0/datetime/mod.ts";

Standard library is versioned separately from runtime. Always pin versions (@0.200.0) for stability.


File System Operations

Reading Files

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

// Read binary file
const data = await Deno.readFile("./image.png");

// Read directory
for await (const entry of Deno.readDir("./dir")) {
  console.log(entry.name, entry.isDirectory);
}

Writing Files

// Write text
await Deno.writeTextFile("./output.txt", "Hello Deno");

// Write binary
await Deno.writeFile("./data.bin", new Uint8Array([1, 2, 3]));

// Append to file
await Deno.writeTextFile("./log.txt", "New entry\n", { append: true });

File Operations

// Copy file
await Deno.copyFile("source.txt", "dest.txt");

// Remove file/directory
await Deno.remove("./file.txt");
await Deno.remove("./dir", { recursive: true });

// Rename/Move
await Deno.rename("old.txt", "new.txt");

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

All file operations are async and require --allow-read and/or --allow-write permissions.


Network Operations

HTTP Server

// Simple server
Deno.serve((_req) => new Response("Hello World"));

// With options
Deno.serve({ port: 8080 }, (req) => {
  return new Response(`You requested: ${req.url}`);
});

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

serve((req) => new Response("Hello"), { port: 3000 });

Fetch API

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

// POST request
const response = await fetch("https://api.example.com/data", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Deno" }),
});

Deno implements standard Web APIs like fetch, making browser code portable to server-side.

WebSocket

// Server
Deno.serve((req) => {
  if (req.headers.get("upgrade") === "websocket") {
    const { socket, response } = Deno.upgradeWebSocket(req);
    socket.onmessage = (e) => socket.send(`Echo: ${e.data}`);
    return response;
  }
  return new Response("Not a WebSocket request");
});

// Client
const ws = new WebSocket("ws://localhost:8080");
ws.onmessage = (e) => console.log(e.data);
ws.send("Hello");

Environment & Process

Environment Variables

{
  "compilerOptions": {
    "strict": true,
    "lib": ["deno.window", "deno.unstable"],
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  },
  "lint": {
    "rules": {
      "tags": ["recommended"],
      "exclude": ["no-unused-vars"]
    }
  },
  "fmt": {
    "useTabs": false,
    "lineWidth": 100,
    "semiColons": true
  },
  "tasks": {
    "dev": "deno run --watch main.ts"
  },
  "imports": {
    "react": "https://esm.sh/react@18"
  }
}

deno.json is the configuration file for TypeScript options, linting, formatting, import maps, and tasks.

Type Checking

# Type check without running
deno check main.ts

# Skip type checking (faster execution)
deno run --no-check main.ts

Web APIs in Deno

Deno implements many browser-standard Web APIs:

API Description
fetch HTTP requests
WebSocket WebSocket connections
localStorage Not available (security)
setTimeout/setInterval Timers
console Logging
TextEncoder/TextDecoder Text encoding
URL/URLSearchParams URL manipulation
FormData Form data handling
ReadableStream/WritableStream Streaming APIs
crypto Cryptographic operations
Blob/File Binary data

Using standard Web APIs makes code portable between browser and server environments.


Working with npm Packages

Deno can import npm packages directly:

// Using npm: specifier
import express from "npm:express@4";
import { dirname } from "npm:path";

// Using CDN (esm.sh)
import React from "https://esm.sh/react@18";

npm: specifier loads packages from npm registry. CDNs like esm.sh provide ESM-compatible versions.

Node Compatibility

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

node: prefix imports Node.js built-in modules with compatibility layer.


Examples

Simple HTTP Server

// server.ts
Deno.serve({ port: 8000 }, (req: Request) => {
  const url = new URL(req.url);
  
  if (url.pathname === "/") {
    return new Response("Home Page");
  }
  
  if (url.pathname === "/api") {
    return Response.json({ message: "API response" });
  }
  
  return new Response("Not Found", { status: 404 });
});

// Run: deno run --allow-net server.ts

File Upload Handler

Deno.serve(async (req) => {
  if (req.method === "POST") {
    const formData = await req.formData();
    const file = formData.get("file") as File;
    
    if (file) {
      const bytes = await file.arrayBuffer();
      await Deno.writeFile(`./uploads/${file.name}`, new Uint8Array(bytes));
      return new Response("File uploaded");
    }
  }
  
  return new Response("Send POST with file");
});

// Run: deno run --allow-net --allow-write server.ts

Reading JSON Configuration

// config.json
// { "apiKey": "secret", "port": 3000 }

const configText = await Deno.readTextFile("./config.json");
const config = JSON.parse(configText);

console.log(config.apiKey);

// Run: deno run --allow-read script.ts

Environment-based Configuration

const isProd = Deno.env.get("ENVIRONMENT") === "production";
const port = parseInt(Deno.env.get("PORT") || "8000");

Deno.serve({ port }, () => {
  return new Response(isProd ? "Production" : "Development");
});

// Run: ENVIRONMENT=production PORT=3000 deno run --allow-net --allow-env script.ts

Testing with Mocks

// math.ts
export function add(a: number, b: number): number {
  return a + b;
}

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

Deno.test("add function", () => {
  assertEquals(add(2, 3), 5);
  assertEquals(add(-1, 1), 0);
});

// Run: deno test

Best Practices

Dependency Management

  • Pin versions: Always specify exact versions in imports (@0.200.0)
  • Use import maps: Centralize dependencies in deno.json for maintainability
  • Lock dependencies: Use deno.lock for reproducible builds
  • Vendor dependencies: Cache all deps with deno cache before deployment

Security

  • Principle of least privilege: Grant only necessary permissions
  • Scope permissions: Use --allow-net=api.com instead of --allow-net
  • Review remote code: Check imported URLs before running
  • Use lock files: Prevent supply chain attacks with deno.lock

Code Organization

  • Use meaningful names: server.ts, utils.ts, not index.ts
  • Separate concerns: Split HTTP handlers, business logic, and data access
  • Export explicitly: Use named exports for clarity
  • Type everything: Leverage TypeScript's type system fully

Performance

  • Minimize permission checks: Grant permissions once per run, not per operation
  • Use streaming: Prefer ReadableStream for large data
  • Cache wisely: Use --reload selectively to update specific modules
  • Avoid dynamic imports: Static imports enable better optimization

Testing

  • Test file naming: Use _test.ts or .test.ts suffix
  • Isolate tests: Each test should be independent
  • Use assertions: Import from std/assert for consistent testing
  • Mock carefully: Use dependency injection for easier mocking

Common Mistakes

Permission Errors

// ❌ Forgot permission flag
await Deno.readFile("./data.txt");
// Run without --allow-read → PermissionDenied error

// ✅ Include required permission
// deno run --allow-read script.ts

Import Path Issues

// ❌ Node-style imports don't work
import { helper } from "./utils";  // Missing .ts extension

// ✅ Always include file extension
import { helper } from "./utils.ts";

Async/Await Confusion

// ❌ Forgot await
const text = Deno.readTextFile("file.txt");
console.log(text);  // Prints Promise object

// ✅ Await async operations
const text = await Deno.readTextFile("file.txt");
console.log(text);

Version Mismatches

// ❌ Different std versions
import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
import { assertEquals } from "https://deno.land/std@0.180.0/assert/mod.ts";

// ✅ Consistent versions
import { serve } from "https://deno.land/std@0.200.0/http/server.ts";
import { assertEquals } from "https://deno.land/std@0.200.0/assert/mod.ts";

Blocking Operations

// ❌ Synchronous file reading (deprecated)
const text = Deno.readTextFileSync("file.txt");

// ✅ Use async version
const text = await Deno.readTextFile("file.txt");

Over-permissioning

# ❌ Granting all permissions unnecessarily
deno run --allow-all script.ts

# ✅ Grant only what's needed
deno run --allow-read=./data --allow-net=api.example.com script.ts

Missing Type Declarations

// ❌ Implicit any types
function process(data) {
  return data.value;
}

// ✅ Explicit types
function process(data: { value: number }): number {
  return data.value;
}

Quick Reference / TL;DR

Essential Commands

deno run script.ts                    # Run script
deno run -A script.ts                 # Run with all permissions
deno run --watch script.ts            # Watch mode
deno test                             # Run tests
deno fmt                              # Format code
deno lint                             # Lint code
deno cache deps.ts                    # Cache dependencies
deno info                             # Show Deno info
deno upgrade                          # Update Deno

Permission Flags Quick Reference

-A, --allow-all          # All permissions
--allow-read[=<PATH>]    # File system read
--allow-write[=<PATH>]   # File system write
--allow-net[=<HOST>]     # Network access
--allow-env[=<VAR>]      # Environment variables
--allow-run[=<CMD>]      # Run subprocesses

Import Patterns

// Remote
import { x } from "https://deno.land/std@0.200.0/mod.ts";

// Local
import { y } from "./local.ts";

// npm
import { z } from "npm:package@1.0.0";

// Node built-ins
import { a } from "node:fs/promises";

Core APIs

// File I/O
await Deno.readTextFile(path);
await Deno.writeTextFile(path, data);

// HTTP
Deno.serve((req) => new Response("OK"));
await fetch(url);

// Environment
Deno.env.get("VAR");
Deno.args;

// Process
const cmd = new Deno.Command("ls");
await cmd.output();

Configuration (deno.json)

{
  "imports": { "alias": "https://url.com/mod.ts" },
  "tasks": { "dev": "deno run --watch main.ts" },
  "compilerOptions": { "strict": true }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment