Deno is a modern, secure runtime for JavaScript and TypeScript built on V8 and written in Rust. Created by Ryan Dahl (the original creator of Node.js), Deno addresses the design regrets of Node.js by providing first-class TypeScript support, a security-by-default permission system, built-in tooling (formatter, linter, test runner, bundler), web-standard APIs, and native npm compatibility. Whether you are starting a new project or evaluating alternatives to Node.js, this guide covers everything from Deno basics through production deployment.
Deno is a secure JavaScript/TypeScript runtime written in Rust with V8. It runs TypeScript natively without configuration, enforces security through a granular permission system (--allow-read, --allow-net, etc.), includes a rich standard library via jsr:@std, supports npm packages natively, provides built-in tools (deno fmt, deno lint, deno test, deno bench), uses deno.json for project configuration and tasks, ships with Deno.serve() for high-performance HTTP servers, and deploys to the edge via Deno Deploy. The Fresh framework offers islands-based SSR. Deno is production-ready and used by Slack, Netlify, and Supabase.
- Deno runs TypeScript and JSX/TSX natively without any build step, transpiler configuration, or tsconfig.json setup.
- The permission system is secure by default: scripts cannot access the file system, network, or environment unless you explicitly grant permission flags.
- Deno standard library (jsr:@std) provides stable, reviewed modules for HTTP, file system, testing, cryptography, and async utilities.
- Native npm compatibility via npm: specifiers means the vast majority of the npm ecosystem works in Deno without modification.
- Built-in tooling replaces entire categories of third-party packages: deno fmt (Prettier), deno lint (ESLint), deno test (Jest), deno bench (benchmarking).
- Deno Deploy provides globally distributed edge hosting with sub-millisecond cold starts, automatic HTTPS, and zero-config deployment from GitHub.
Deno Basics: Installation and First Steps
Deno ships as a single binary executable with no external dependencies. Installation takes seconds and gives you a complete development environment including TypeScript compiler, formatter, linter, and test runner.
Install Deno on any platform:
# Install Deno (macOS / Linux)
curl -fsSL https://deno.land/install.sh | sh
# Install Deno (Windows PowerShell)
irm https://deno.land/install.ps1 | iex
# Install via Homebrew (macOS)
brew install deno
# Verify installation
deno --version
# deno 2.x.x
# v8 12.x.x
# typescript 5.x.x
# Initialize a new project
deno init my-app
cd my-app
# Creates: main.ts, main_test.ts, deno.json
# Useful commands
deno run main.ts # Run a script
deno run --watch main.ts # Run with auto-reload
deno fmt # Format code
deno lint # Lint code
deno test # Run tests
deno bench # Run benchmarks
deno compile main.ts # Compile to binaryYour first Deno script runs TypeScript directly:
// main.ts β runs directly with: deno run main.ts
interface User {
name: string;
age: number;
email: string;
}
function greet(user: User): string {
return "Hello, " + user.name + "! You are " + user.age + " years old.";
}
const user: User = {
name: "Alice",
age: 30,
email: "alice@example.com",
};
console.log(greet(user));
// Fetch data from a URL (web-standard fetch)
const response = await fetch("https://api.github.com/zen");
const wisdom = await response.text();
console.log("GitHub Zen:", wisdom);Security and the Permission System
Deno is secure by default. Unlike Node.js, a Deno script cannot read files, access the network, or read environment variables unless you explicitly grant permission. This sandboxed execution model prevents supply-chain attacks and limits the damage from compromised dependencies.
Permission flags explained:
# Permission flags
--allow-read Read file system
--allow-write Write file system
--allow-net Network access
--allow-env Environment variables
--allow-run Run subprocesses
--allow-ffi Foreign function interface
--allow-sys System information
--allow-all / -A All permissions (dev only)
# No permissions β script is fully sandboxed
deno run sandboxed.ts
# Runtime will prompt for permission if not granted:
# Deno requests read access to "./config.json".
# Allow? [y/n/A] (y = allow, n = deny, A = allow all)You can combine flags and scope them to specific resources:
# Scoped permissions β only allow what is needed
deno run \
--allow-read=./data,./config \
--allow-write=./output \
--allow-net=api.example.com:443,cdn.example.com \
--allow-env=DATABASE_URL,API_KEY \
server.ts
# Deny specific permissions (blocklist)
deno run \
--allow-net --deny-net=evil.com \
--allow-read --deny-read=/etc/passwd \
app.ts
# Programmatic permission check
# const status = await Deno.permissions.query(
# { name: "read", path: "./secrets" }
# );
# console.log(status.state); // "granted" | "denied" | "prompt"First-Class TypeScript Support
Deno supports TypeScript out of the box with zero configuration. You can run .ts and .tsx files directly. The TypeScript compiler is built into the Deno binary and uses SWC for fast transpilation. Type checking is available on demand with the --check flag, allowing you to choose between speed and safety.
TypeScript configuration through deno.json:
// deno.json β TypeScript configuration
{
"compilerOptions": {
"strict": true,
"jsx": "react-jsx",
"jsxImportSource": "react",
"lib": ["deno.window", "dom"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
# Type-check your code (not done by default for speed)
# deno check main.ts
# deno run --check main.tsTypeScript features that work immediately:
// Advanced TypeScript β works out of the box
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
function safeParse<T>(json: string): Result<T> {
try {
return { ok: true, value: JSON.parse(json) as T };
} catch (e) {
return { ok: false, error: e as Error };
}
}
interface Config {
port: number;
host: string;
debug: boolean;
}
const result = safeParse<Config>('{"port":3000,"host":"localhost","debug":true}');
if (result.ok) {
console.log("Server port:", result.value.port);
}
// Enums, generics, utility types β all native
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
const partial: DeepPartial<Config> = { port: 8080 };The Deno Standard Library
The Deno standard library (@std) is a collection of high-quality, reviewed modules maintained by the Deno team. Distributed through JSR (JavaScript Registry), it provides stable APIs for common tasks without requiring third-party packages. Each module is independently versioned and audited.
Core standard library modules:
// Deno Standard Library β import from jsr:@std
import { ensureDir, copy, walk } from "jsr:@std/fs";
import { join, resolve, basename } from "jsr:@std/path";
import { assertEquals, assertThrows } from "jsr:@std/assert";
import { delay } from "jsr:@std/async";
import { parse as parseFlags } from "jsr:@std/cli/parse-args";
import { encodeBase64, decodeBase64 } from "jsr:@std/encoding/base64";
import { crypto } from "jsr:@std/crypto";
import { serveDir } from "jsr:@std/http/file-server";
// File system utilities
await ensureDir("./output/logs");
await copy("./src", "./backup/src", { overwrite: true });
// Walk a directory tree
for await (const entry of walk("./src", { exts: [".ts"] })) {
console.log("Found:", entry.path);
}
// Parse CLI flags
const args = parseFlags(Deno.args, {
string: ["port", "host"],
boolean: ["verbose"],
default: { port: "3000", host: "localhost" },
});
// Encoding
const encoded = encodeBase64("Hello Deno");
console.log(encoded); // "SGVsbG8gRGVubw=="Building HTTP Servers with Deno.serve()
Deno provides Deno.serve() as the primary API for creating HTTP servers. Built on hyper (a Rust HTTP library), it uses web-standard Request and Response objects, supports streaming, WebSockets, and handles thousands of concurrent connections efficiently.
A basic HTTP server with routing:
// server.ts β Basic HTTP server with routing
// Run: deno run --allow-net server.ts
Deno.serve({ port: 3000 }, (req: Request): Response => {
const url = new URL(req.url);
// JSON API endpoint
if (url.pathname === "/api/health") {
return Response.json({ status: "ok", uptime: Deno.uptime() });
}
// Dynamic route with URL params
if (url.pathname.startsWith("/api/users/")) {
const id = url.pathname.split("/").pop();
return Response.json({ id, name: "User " + id });
}
// Query parameters
if (url.pathname === "/api/search") {
const query = url.searchParams.get("q") || "";
return Response.json({ query, results: [] });
}
// HTML response
if (url.pathname === "/") {
return new Response("<h1>Welcome to Deno</h1>", {
headers: { "Content-Type": "text/html" },
});
}
return new Response("Not Found", { status: 404 });
});
console.log("Server running at http://localhost:3000");A more complete server with middleware pattern:
// Middleware pattern with Deno.serve()
type Handler = (req: Request) => Response | Promise<Response>;
type Middleware = (req: Request, next: Handler) => Response | Promise<Response>;
const logger: Middleware = async (req, next) => {
const start = performance.now();
const res = await next(req);
const ms = (performance.now() - start).toFixed(1);
console.log(req.method + " " + new URL(req.url).pathname + " " + res.status + " " + ms + "ms");
return res;
};
const cors: Middleware = async (req, next) => {
const res = await next(req);
res.headers.set("Access-Control-Allow-Origin", "*");
return res;
};
function compose(mws: Middleware[], h: Handler): Handler {
return mws.reduceRight((next, mw) => (req) => mw(req, next), h);
}
const app = compose([logger, cors], () => {
return Response.json({ message: "Hello from Deno!" });
});
Deno.serve({ port: 3000 }, app);File System Operations
Deno provides both synchronous and asynchronous file system APIs through the Deno namespace. These APIs require --allow-read and --allow-write permissions. Deno also supports the Node.js node:fs module for compatibility.
Common file system operations:
// File system operations
// Run: deno run --allow-read --allow-write fs-demo.ts
// Read a text file
const content = await Deno.readTextFile("./config.json");
console.log(content);
// Write a text file
await Deno.writeTextFile("./output.txt", "Hello from Deno!");
// Read/write binary files
const bytes = await Deno.readFile("./image.png");
await Deno.writeFile("./copy.png", bytes);
// Create directory (recursive)
await Deno.mkdir("./output/nested/dir", { recursive: true });
// List directory contents
for await (const entry of Deno.readDir("./src")) {
console.log(entry.name, entry.isFile ? "file" : "dir");
}
// File info (stat)
const info = await Deno.stat("./config.json");
console.log("Size:", info.size, "bytes");
console.log("Modified:", info.mtime);
// Remove files and directories
await Deno.remove("./temp.txt");
await Deno.remove("./temp-dir", { recursive: true });
// Rename / move
await Deno.rename("./old.txt", "./new.txt");
// Check if file exists
try {
await Deno.stat("./maybe.txt");
console.log("File exists");
} catch {
console.log("File not found");
}Streaming file operations for large files:
// Streaming with ReadableStream (web standard)
const bigFile = await Deno.open("./data.csv");
for await (const chunk of bigFile.readable) {
console.log("Received", chunk.length, "bytes");
}
// Watch for file changes
const watcher = Deno.watchFs("./src");
for await (const event of watcher) {
console.log("Event:", event.kind, event.paths);
}Testing with Deno
Deno includes a built-in test runner that supports test definitions, assertions, async tests, test steps, mocking, snapshot testing, and code coverage. No additional packages or configuration are needed. Run tests with deno test and get coverage with --coverage.
Writing and organizing tests:
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export function divide(a: number, b: number): number {
if (b === 0) throw new Error("Division by zero");
return a / b;
}
// math_test.ts β Run: deno test math_test.ts
import { assertEquals, assertThrows } from "jsr:@std/assert";
import { add, divide } from "./math.ts";
Deno.test("add positive numbers", () => {
assertEquals(add(2, 3), 5);
});
Deno.test("add negative numbers", () => {
assertEquals(add(-1, -2), -3);
});
Deno.test("divide numbers", () => {
assertEquals(divide(10, 2), 5);
});
Deno.test("divide by zero throws", () => {
assertThrows(
() => divide(10, 0),
Error,
"Division by zero"
);
});
# Run tests with coverage
# deno test --coverage=./cov
# deno coverage ./cov --lcov > coverage.lcovAdvanced testing with mocking and steps:
// Advanced testing with steps, mocking, and async
import { assertEquals } from "jsr:@std/assert";
import { stub, returnsNext } from "jsr:@std/testing/mock";
// Test steps β group related assertions
Deno.test("user CRUD operations", async (t) => {
let userId: string;
await t.step("create user", () => {
userId = "user-123";
assertEquals(typeof userId, "string");
});
await t.step("read user", () => {
assertEquals(userId, "user-123");
});
await t.step("delete user", () => {
userId = "";
assertEquals(userId, "");
});
});
// Mocking with stubs
Deno.test("mocking fetch", async () => {
const fetchStub = stub(
globalThis,
"fetch",
returnsNext([
Promise.resolve(new Response('{"id":1}', { status: 200 })),
])
);
try {
const res = await fetch("https://api.example.com/data");
const data = await res.json();
assertEquals(data.id, 1);
} finally {
fetchStub.restore();
}
});
// Async test with resource sanitizers
Deno.test({
name: "async database test",
sanitizeResources: false,
sanitizeOps: false,
async fn() {
// Test with open resources...
},
});Tasks and Configuration with deno.json
The deno.json (or deno.jsonc) file is the central configuration for Deno projects. It replaces package.json scripts (via tasks), tsconfig.json (via compilerOptions), and import maps (via imports). Tasks support cross-platform shell syntax and can chain commands.
A complete deno.json configuration:
// deno.json β Complete project configuration
{
"tasks": {
"dev": "deno run --watch --allow-all src/main.ts",
"start": "deno run --allow-net --allow-read --allow-env src/main.ts",
"test": "deno test --allow-all --coverage=./coverage",
"test:watch": "deno test --allow-all --watch",
"check": "deno check src/main.ts",
"lint": "deno lint",
"fmt": "deno fmt",
"build": "deno compile --output=dist/app src/main.ts",
"bench": "deno bench --allow-all",
"ci": "deno fmt --check && deno lint && deno check src/main.ts && deno test --allow-all"
},
"compilerOptions": {
"strict": true,
"jsx": "react-jsx",
"jsxImportSource": "preact"
},
"fmt": {
"semiColons": true,
"singleQuote": true,
"lineWidth": 100,
"indentWidth": 2,
"exclude": ["dist/", "coverage/"]
},
"lint": {
"rules": {
"tags": ["recommended"],
"exclude": ["no-unused-vars"]
},
"exclude": ["dist/"]
},
"test": {
"include": ["src/", "tests/"]
},
"lock": true
}Import maps centralize dependency management:
// deno.json β Import map section
{
"imports": {
"@std/http": "jsr:@std/http@^1.0.0",
"@std/assert": "jsr:@std/assert@^1.0.0",
"@std/fs": "jsr:@std/fs@^1.0.0",
"@std/path": "jsr:@std/path@^1.0.0",
"@std/async": "jsr:@std/async@^1.0.0",
"express": "npm:express@4",
"zod": "npm:zod@^3.22",
"drizzle-orm": "npm:drizzle-orm@^0.30",
"@/": "./src/"
}
}
// Now import with clean paths:
// import { assertEquals } from "@std/assert";
// import { z } from "zod";
// import { db } from "@/lib/database.ts";npm Compatibility
Deno supports npm packages natively through the npm: specifier. Packages are downloaded and cached globally without creating a node_modules directory by default. Most popular npm packages including Express, React, Prisma, and thousands of others work without modification.
Using npm packages in Deno:
// Using npm packages in Deno
import express from "npm:express@4";
import { z } from "npm:zod";
import chalk from "npm:chalk@5";
import _ from "npm:lodash";
// Zod schema validation
const UserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().int().positive(),
});
type User = z.infer<typeof UserSchema>;
const result = UserSchema.safeParse({ name: "Bob", email: "bob@test.com", age: 25 });
if (result.success) {
console.log(chalk.green("Valid user: " + result.data.name));
}
// Express server in Deno
const app = express();
app.get("/", (_req, res) => {
res.json({ runtime: "deno", framework: "express" });
});
app.listen(3000, () => {
console.log("Express on Deno at http://localhost:3000");
});
// Run: deno run --allow-net --allow-read --allow-env server.tsNode.js built-in modules via node: specifier:
// Node.js built-in modules via node: specifier
import { readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { createHash, randomUUID } from "node:crypto";
import { EventEmitter } from "node:events";
import { Buffer } from "node:buffer";
import { setTimeout } from "node:timers/promises";
// These work identically to Node.js
const configPath = join(".", "config.json");
const data = readFileSync(configPath, "utf-8");
const hash = createHash("sha256").update(data).digest("hex");
console.log("Hash:", hash);
// EventEmitter
const emitter = new EventEmitter();
emitter.on("data", (msg: string) => console.log("Received:", msg));
emitter.emit("data", "hello from node:events");
// Buffer operations
const buf = Buffer.from("Hello Deno", "utf-8");
console.log(buf.toString("base64")); // SGVsbG8gRGVubw==Deployment with Deno Deploy
Deno Deploy is a globally distributed edge hosting platform built specifically for Deno. It runs your code on servers close to your users across 35+ regions, with automatic HTTPS, instant deployments from GitHub, and a generous free tier. It uses the same V8 isolate technology as Cloudflare Workers.
Setting up a Deno Deploy project:
# Deploy to Deno Deploy
# 1. Install deployctl CLI
deno install -A jsr:@deno/deployctl
# 2. Create main.ts for edge deployment
# Deno.serve((req: Request) => {
# return new Response("Hello from the edge!");
# });
# 3. Deploy from CLI
deployctl deploy --project=my-app main.ts
# 4. Or connect GitHub for auto-deploy:
# - Go to dash.deno.com
# - Create project > Link to GitHub repo
# - Set entrypoint to main.ts
# - Every push to main triggers deployment
# Docker deployment (alternative)
# FROM denoland/deno:latest
# WORKDIR /app
# COPY . .
# RUN deno cache main.ts
# EXPOSE 3000
# CMD ["run", "--allow-net", "--allow-read", "--allow-env", "main.ts"]Deno Deploy provides KV (key-value) storage, cron jobs, and BroadcastChannel for distributed state:
// Deno Deploy features: KV, Cron, BroadcastChannel
// Deno KV β distributed key-value storage
const kv = await Deno.openKv();
// Store a value
await kv.set(["users", "alice"], { name: "Alice", visits: 0 });
// Read a value
const entry = await kv.get(["users", "alice"]);
console.log(entry.value); // { name: "Alice", visits: 0 }
// Atomic transactions
await kv.atomic()
.check(entry) // Ensure no concurrent modification
.set(["users", "alice"], { name: "Alice", visits: 1 })
.commit();
// List entries by prefix
const users = kv.list({ prefix: ["users"] });
for await (const user of users) {
console.log(user.key, user.value);
}
// Deno Cron β scheduled tasks
Deno.cron("cleanup", "0 0 * * *", async () => {
console.log("Running daily cleanup...");
// Delete expired entries
});
// BroadcastChannel β cross-isolate messaging
const channel = new BroadcastChannel("updates");
channel.onmessage = (e) => console.log("Update:", e.data);The Fresh Framework
Fresh is the official full-stack web framework for Deno. It uses islands architecture for selective client-side hydration, renders pages on the server by default, supports JSX/TSX, and requires zero build step. Fresh routes are file-system based, and islands are the only components that ship JavaScript to the browser.
Creating and understanding a Fresh project:
# Create a Fresh project
deno run -A -r https://fresh.deno.dev my-fresh-app
cd my-fresh-app
deno task start
# Fresh project structure:
# my-fresh-app/
# routes/ File-based routes
# index.tsx GET / (server-rendered)
# about.tsx GET /about
# api/joke.ts GET /api/joke (API route)
# [name].tsx GET /:name (dynamic route)
# _middleware.ts Middleware for all routes
# islands/ Interactive components (client JS)
# Counter.tsx Ships JavaScript to the browser
# components/ Server-only components (no JS shipped)
# static/ Static files
# fresh.gen.ts Auto-generated manifest
# deno.json Project config
// routes/index.tsx β Server-rendered page
// export default function Home() {
// return (
// <div>
// <h1>Welcome to Fresh</h1>
// <Counter start={0} />
// </div>
// );
// }Fresh islands architecture for interactive components:
// islands/Counter.tsx β Interactive island component
// Only this component ships JavaScript to the browser
import { useSignal } from "@preact/signals";
interface CounterProps {
start: number;
}
export default function Counter(props: CounterProps) {
const count = useSignal(props.start);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => count.value--}>-1</button>
<button onClick={() => count.value++}>+1</button>
</div>
);
}
// routes/api/joke.ts β API route
// import { FreshContext } from "$fresh/server.ts";
// const JOKES = ["Why do programmers prefer dark mode?...",];
// export const handler = (_req: Request, _ctx: FreshContext) => {
// const joke = JOKES[Math.floor(Math.random() * JOKES.length)];
// return new Response(JSON.stringify({ joke }), {
// headers: { "Content-Type": "application/json" },
// });
// };Deno vs Node.js: Detailed Comparison
Deno and Node.js are both JavaScript runtimes built on V8, but they differ significantly in design philosophy, security model, and developer experience. This comparison helps you decide which runtime fits your project.
Feature comparison:
| Feature | Deno | Node.js |
|---|---|---|
| Language | Rust + V8 | C++ + V8 |
| TypeScript | Native, zero config | Via tsx/ts-node (native in v22+) |
| Security | Sandboxed by default | Unrestricted by default |
| Package Manager | Built-in (deno add) | npm / yarn / pnpm |
| Module System | ESM only (CJS via compat) | CJS + ESM |
| Formatter | Built-in (deno fmt) | Prettier (external) |
| Linter | Built-in (deno lint) | ESLint (external) |
| Test Runner | Built-in (deno test) | Built-in (node --test) / Jest |
| npm Compatibility | ~95%+ via npm: specifier | 100% native |
| Web APIs | Extensive (fetch, Streams, etc.) | Partial (fetch added in v18) |
| Edge Hosting | Deno Deploy (built-in) | Vercel / Cloudflare (external) |
| Ecosystem Maturity | 5+ years, growing | 15+ years, massive |
| Config Files | Single deno.json | package.json + tsconfig + .eslintrc + ... |
When to choose Deno: new projects that benefit from TypeScript-first development, security-sensitive applications, edge deployments, and teams that want built-in tooling without configuring Prettier, ESLint, Jest, and bundlers separately. When to choose Node.js: legacy projects with deep npm dependency trees, frameworks that require specific Node.js features, and teams with established Node.js workflows.
Best Practices
- Always use the minimum required permissions. Never use --allow-all in production. Scope permissions to specific paths and hosts.
- Pin dependency versions in your import map (deno.json imports). Use exact versions for reproducible builds.
- Use deno.lock to lock dependency versions. Run deno cache --lock=deno.lock to verify integrity.
- Prefer Deno-native APIs (Deno.readTextFile, Deno.serve) over Node.js compatibility modules for better performance and smaller footprint.
- Run deno fmt and deno lint in CI pipelines. They are fast, opinionated, and consistent across teams.
- Use Deno.test with steps for integration tests. Steps provide clear output and independent pass/fail status.
- Deploy to Deno Deploy for edge performance. Use Deno KV for distributed state instead of external databases when possible.
- Compile standalone binaries with deno compile for CLI tools and internal utilities that need to run without Deno installed.
# Recommended CI pipeline for Deno projects
# .github/workflows/ci.yml
#
# name: CI
# on: [push, pull_request]
# jobs:
# test:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: denoland/setup-deno@v2
# with:
# deno-version: v2.x
# - run: deno fmt --check
# - run: deno lint
# - run: deno check src/main.ts
# - run: deno test --allow-all --coverage=cov
# - run: deno coverage cov --lcov > coverage.lcovFrequently Asked Questions
Is Deno ready for production use?
Yes. Deno is production-ready and used by companies including Slack, Netlify, Supabase, and GitHub. The runtime has been stable since 1.0 and the Deno 2 release solidified backward compatibility and npm support. Deno Deploy handles billions of requests monthly.
Can I use npm packages with Deno?
Yes. Deno supports npm packages natively using the npm: specifier (e.g., import express from "npm:express@4"). Most npm packages work without modification. You can also use a package.json and node_modules if you prefer the traditional Node.js workflow.
How does the Deno permission system work?
Deno runs scripts in a sandbox by default with no file, network, or environment access. You grant permissions explicitly via flags: --allow-read for file system, --allow-net for network, --allow-env for environment variables, --allow-run for subprocesses. Permissions can be scoped to specific paths and hosts.
Do I need to configure TypeScript to use it with Deno?
No. Deno runs TypeScript natively without any tsconfig.json, build step, or transpiler setup. The TypeScript compiler is built into the Deno binary. You can optionally customize compiler options in deno.json, but it works out of the box.
What is the difference between Deno and Bun?
Both are modern JavaScript runtimes. Deno uses V8 and is written in Rust, focusing on security (permission system), web standards compliance, and TypeScript-first development. Bun uses JavaScriptCore and is written in Zig, focusing on raw execution speed and Node.js drop-in compatibility. Deno has more mature security defaults; Bun has faster startup and install times.
What is Deno Deploy and how much does it cost?
Deno Deploy is a globally distributed edge hosting platform. The free tier includes 100,000 requests per day, 1 GB outbound transfer, and Deno KV storage. The Pro tier costs 20 dollars per month with 5 million requests, 100 GB transfer, and higher KV limits. It deploys automatically from GitHub with zero configuration.
Can I migrate an existing Node.js project to Deno?
Yes. Deno 2 provides strong Node.js compatibility. Migrate incrementally: add a deno.json, update imports to use node: prefix for Node.js built-ins and npm: for packages, replace npm scripts with Deno tasks, and convert CommonJS to ESM. Most projects can migrate without rewriting application logic.
What is the Fresh framework?
Fresh is the official Deno web framework using islands architecture. It renders pages on the server by default, only shipping JavaScript for interactive components (islands). It supports file-system routing, JSX/TSX, middleware, and integrates with Deno Deploy. Fresh requires no build step and starts instantly.