> deno-2

Deno 2 — secure JavaScript/TypeScript runtime with npm compatibility. Use when building secure server-side apps, using TypeScript without config, deploying to Deno Deploy for serverless edge, or using npm packages inside Deno. Covers permissions model, npm imports, JSR registry, and built-in tooling (test, lint, fmt, compile).

fetch
$curl "https://skillshub.wtf/TerminalSkills/skills/deno-2?format=md"
SKILL.mddeno-2

Deno 2

Overview

Deno 2 is a secure JavaScript and TypeScript runtime built on V8. It is TypeScript-native, secure by default (explicit permissions required), and fully compatible with npm packages. Deno 2 adds backwards compatibility with Node.js APIs and package.json, making it a viable drop-in replacement for many Node.js projects.

Installation

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

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

# Homebrew
brew install deno

# Verify
deno --version

Running Code

deno run main.ts                     # Run TypeScript directly
deno run --allow-net main.ts         # With network permission
deno run --allow-all main.ts         # All permissions (dev only)
deno run https://example.com/mod.ts  # Run remote script

Permissions Model

Deno is secure by default — all external access must be explicitly granted:

FlagGrants access to
--allow-netNetwork (fetch, listen)
--allow-readFile system reads
--allow-writeFile system writes
--allow-envEnvironment variables
--allow-runSubprocess execution
--allow-ffiNative libraries
--allow-all or -AEverything (avoid in prod)

Fine-grained permissions:

deno run --allow-net=api.stripe.com --allow-read=./data main.ts

Use deno.json to set default permissions:

{
  "tasks": {
    "dev": "deno run --allow-net --allow-read --allow-env src/main.ts"
  }
}

TypeScript — No Config Needed

Deno runs TypeScript natively without tsconfig.json:

// main.ts — just works
interface User {
  id: number;
  name: string;
}

function greet(user: User): string {
  return `Hello, ${user.name}!`;
}

const user: User = { id: 1, name: "Deno" };
console.log(greet(user));

npm Compatibility

Import npm packages directly with the npm: prefix:

import express from "npm:express";
import { z } from "npm:zod";
import axios from "npm:axios@1.6";

const app = express();

app.get("/", (_req, res) => {
  res.json({ message: "Hello from Deno + Express!" });
});

app.listen(3000, () => console.log("Server running on port 3000"));

Or declare in deno.json:

{
  "imports": {
    "express": "npm:express@^4",
    "zod": "npm:zod@^3"
  }
}

JSR — JavaScript Registry

JSR is the modern package registry designed for Deno and TypeScript:

// Import from JSR
import { encodeBase64 } from "jsr:@std/encoding/base64";
import { Hono } from "jsr:@hono/hono";
// deno.json imports map
{
  "imports": {
    "@std/encoding": "jsr:@std/encoding@^1",
    "@hono/hono": "jsr:@hono/hono@^4"
  }
}

HTTP Server

// Built-in Deno.serve — no imports needed
Deno.serve({ port: 3000 }, async (req: Request) => {
  const url = new URL(req.url);

  if (url.pathname === "/health") {
    return Response.json({ status: "ok" });
  }

  if (req.method === "POST" && url.pathname === "/echo") {
    const body = await req.json();
    return Response.json(body);
  }

  return new Response("Not Found", { status: 404 });
});

console.log("Listening on http://localhost:3000");

File System

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

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

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

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

Built-in Test Runner

// math_test.ts
import { assertEquals, assertThrows } from "jsr:@std/assert";

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

Deno.test("division by zero throws", () => {
  assertThrows(() => {
    if (0 === 0) throw new Error("Division by zero");
  });
});

Deno.test({
  name: "async fetch test",
  permissions: { net: true },
  async fn() {
    const res = await fetch("https://httpbin.org/get");
    assertEquals(res.status, 200);
  },
});
deno test                        # Run all tests
deno test --watch                # Watch mode
deno test --coverage=coverage/   # With coverage
deno test math_test.ts           # Specific file

Built-in Tooling

deno fmt                 # Format code (Prettier-compatible)
deno lint                # Lint code
deno check main.ts       # Type-check without running
deno doc main.ts         # Generate documentation
deno compile main.ts     # Compile to standalone binary
deno bundle main.ts      # Bundle to single JS file (deprecated, use build)
deno info main.ts        # Show module dependency tree
deno repl                # Interactive REPL

deno.json Configuration

{
  "name": "my-app",
  "version": "1.0.0",
  "exports": "./mod.ts",
  "tasks": {
    "dev": "deno run --allow-net --allow-read --allow-env --watch src/main.ts",
    "test": "deno test --allow-net",
    "fmt": "deno fmt",
    "lint": "deno lint",
    "build": "deno compile --allow-net --allow-read src/main.ts"
  },
  "imports": {
    "zod": "npm:zod@^3",
    "@std/assert": "jsr:@std/assert@^1"
  },
  "lint": {
    "rules": {
      "include": ["no-unused-vars", "no-explicit-any"]
    }
  },
  "fmt": {
    "useTabs": false,
    "lineWidth": 100
  }
}

Deno Deploy

Deploy serverless edge functions globally:

// main.ts — deploy to Deno Deploy
Deno.serve((req) => {
  const { pathname } = new URL(req.url);

  if (pathname === "/") {
    return new Response("Hello from the edge!", {
      headers: { "Content-Type": "text/plain" },
    });
  }

  return new Response("Not Found", { status: 404 });
});
# Install deployctl
deno install -A jsr:@deno/deployctl

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

Environment Variables

// Access env vars (requires --allow-env)
const port = Deno.env.get("PORT") ?? "3000";
const apiKey = Deno.env.get("API_KEY");

if (!apiKey) {
  throw new Error("API_KEY environment variable is required");
}

Load .env file:

import { load } from "jsr:@std/dotenv";
const env = await load();
const dbUrl = env.DATABASE_URL;

Guidelines

  • Always specify permissions explicitly — avoid --allow-all in production.
  • Use jsr:@std/* for standard library modules instead of deno.land/std.
  • Use npm: prefix to import npm packages directly — no install step needed.
  • Declare imports in deno.json imports map for cleaner code.
  • Use deno fmt and deno lint as part of CI — they have zero config.
  • Use deno compile to produce portable standalone executables.
  • Deno 2 is backward-compatible with package.json — Node.js projects often work without changes.
  • Use Deno Deploy for serverless edge deployment with zero infrastructure.

┌ stats

installs/wk0
░░░░░░░░░░
github stars21
████░░░░░░
first seenMar 23, 2026
└────────────

┌ repo

TerminalSkills/skills
by TerminalSkills
└────────────