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:
| Flag | Grants access to |
|---|---|
--allow-net | Network (fetch, listen) |
--allow-read | File system reads |
--allow-write | File system writes |
--allow-env | Environment variables |
--allow-run | Subprocess execution |
--allow-ffi | Native libraries |
--allow-all or -A | Everything (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-allin production. - Use
jsr:@std/*for standard library modules instead ofdeno.land/std. - Use
npm:prefix to import npm packages directly — no install step needed. - Declare imports in
deno.jsonimportsmap for cleaner code. - Use
deno fmtanddeno lintas part of CI — they have zero config. - Use
deno compileto 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.