// ── Simple HTTP Server (Standard Library) ──
import { serve } from "jsr:@std/http@1.0.0";
const handler = (req: Request): Response => {
const url = new URL(req.url);
if (url.pathname === "/" && req.method === "GET") {
return new Response("Hello from Deno!", {
headers: { "content-type": "text/plain" },
});
}
if (url.pathname === "/api/users" && req.method === "GET") {
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
return Response.json(users);
}
if (url.pathname === "/api/users" && req.method === "POST") {
const body = await req.json();
return Response.json({ id: 3, ...body }, { status: 201 });
}
return new Response("Not Found", { status: 404 });
};
serve(handler, { port: 8000 });
console.log("Server on http://localhost:8000");
// ── Oak Framework (Express-like) ──
import { Application, Router } from "jsr:@oak/oak@^13.0.0";
import { oakCors } from "jsr:@oak/cors@^0.1.0";
const app = new Application();
const router = new Router();
// ── Middleware ──
app.use(oakCors({ origin: "http://localhost:3000" }));
// Logger
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
console.log(`${ctx.request.method} ${ctx.request.url} - ${ms}ms`);
});
// Error handler
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = 500;
ctx.response.body = { error: err.message };
}
});
// ── Routes ──
router
.get("/api/users", (ctx) => {
ctx.response.body = { users: [{ id: 1, name: "Alice" }] };
})
.get("/api/users/:id", (ctx) => {
const id = ctx.params.id;
ctx.response.body = { id, name: "Alice" };
})
.post("/api/users", async (ctx) => {
const body = await ctx.request.body.json();
ctx.response.status = 201;
ctx.response.body = { id: Date.now(), ...body };
})
.delete("/api/users/:id", (ctx) => {
ctx.response.status = 204;
});
app.use(router.routes());
app.use(router.allowedMethods());
console.log("Listening on http://localhost:8000");
await app.listen({ port: 8000 });
// ── Hono (modern, fast alternative) ──
import { Hono } from "jsr:@hono/hono";
const hono = new Hono();
hono.get("/api/hello", (c) => c.json({ message: "Hello from Hono!" }));
hono.post("/api/users", async (c) => {
const body = await c.req.json();
return c.json({ id: crypto.randomUUID(), ...body }, 201);
});
Deno.serve(hono.fetch);
⚠️Hono is the recommended web framework for Deno 2. It is faster than Oak, has middleware support, built-in types, and works across runtimes (Deno, Node.js, Bun, Cloudflare Workers). Oak is still excellent for Express/Koa-style development.
import { assertEquals, assertExists, assertThrows, assertFalse } from "jsr:@std/assert@1.0.0";
// ── Basic Test ──
Deno.test("add function", () => {
assertEquals(add(2, 3), 5);
assertEquals(add(-1, 1), 0);
assertEquals(add(0, 0), 0);
});
// ── Async Test ──
Deno.test("fetch user data", async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await res.json();
assertEquals(user.name, "Leanne Graham");
assertExists(user.email);
});
// ── Step-based Test (describe/it pattern) ──
Deno.test("math operations", async (t) => {
await t.step("addition", () => {
assertEquals(2 + 2, 4);
});
await t.step("subtraction", () => {
assertEquals(5 - 3, 2);
});
await t.step("multiplication", () => {
assertEquals(3 * 4, 12);
});
});
// ── Test with Options ──
Deno.test({
name: "only runs with --focus",
focus: true, // deno test --focus
fn: () => {
assertEquals(1 + 1, 2);
},
});
Deno.test({
name: "skipped test",
ignore: true, // deno test --ignore
fn: () => {
assertEquals(1, 2);
},
});
// ── Permissions in Tests ──
Deno.test({
name: "test with permissions",
permissions: { read: true, net: true },
fn: async () => {
const text = await Deno.readTextFile("./test.txt");
assertExists(text);
},
});
// ── Run Tests ──
// deno test # run all tests
// deno test --allow-all # with permissions
// deno test --watch # watch mode
// deno test --filter "add" # filter by name
// deno test --cover # code coverage
// deno test --cover --coverage=coverage_dir
// ── Integration Testing with Oak ──
import { Application, Router } from "jsr:@oak/oak@^13.0.0";
import { assertEquals, assert } from "jsr:@std/assert@1.0.0";
import { superoak } from "jsr:@oak/superoak";
function createApp() {
const router = new Router();
router.get("/api/status", (ctx) => {
ctx.response.body = { status: "ok", version: "1.0.0" };
});
router.post("/api/users", async (ctx) => {
const body = await ctx.request.body.json();
ctx.response.status = 201;
ctx.response.body = { id: 1, ...body };
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
return app;
}
Deno.test("GET /api/status returns ok", async () => {
const app = createApp();
const request = await superoak(app);
await request.get("/api/status")
.expect(200)
.expect("Content-Type", /json/)
.expect({ status: "ok", version: "1.0.0" });
});
Deno.test("POST /api/users creates user", async () => {
const app = createApp();
const request = await superoak(app);
await request.post("/api/users")
.send({ name: "Alice" })
.expect(201)
.expect({ id: 1, name: "Alice" });
});
// ── FFI (Foreign Function Interface) ──
const lib = Deno.dlopen(
"/usr/lib/libssl.so",
{ SSLeay_version: { parameters: ["number"], result: "pointer" } },
);
const version = lib.symbols.SSLeay_version(0);
console.log(new Deno.UnsafePointerView(version).getCString());
Q: What makes Deno different from Node.js?Deno has built-in TypeScript (no tsc), permission-based security (explicit grants), URL-based imports (no node_modules), built-in tooling (test, fmt, lint), native ESM (no CJS), Web Platform APIs (fetch, WebSocket, crypto), and was created by Ryan Dahl (original Node.js creator) to fix design regrets.
Q: How do permissions work?Deno requires explicit permission flags (--allow-net, --allow-read, etc.) for security-sensitive ops. Deno 2 auto-permits read (cwd), localhost net, env read, sys info. Use --deny-* to block. This prevents supply chain attacks — malicious packages cannot access files/network without permission.
Q: How does Deno handle npm packages?Via the npm: spec: import express from 'npm:express@4.18.2'. Downloads and caches to ~/.cache/deno. JSR (JavaScript Registry) is Deno's native ESM-first registry: import { Hono } from 'jsr:@hono/hono'. Import maps in deno.json provide clean aliases.
Q: What is the Deno standard library?A curated set of reviewed, versioned modules for common tasks: HTTP server, file system, testing, path, encoding, crypto, datetime, JSON, YAML, TOML, and more. All imported via jsr:@std/module@version URLs. Audited for quality and compatibility.
Q: What is JSR?JavaScript Registry — Deno's native package registry. All packages are ESM-first, scoped (@scope/package), versioned with SemVer, and support provenance (verified publishers). Packages work across Deno, Node.js, Bun, and Cloudflare Workers.
Q: How do you deploy Deno apps?Deno Deploy (serverless edge, global), Docker (compile to binary or use deno image), VPS (systemd + deno run), or self-host. deno compile produces a standalone binary with no runtime dependency. Deno Deploy is optimized for the edge.
Q: What is Deno.fresh?Fresh is Deno's full-stack web framework with zero JS by default (islands architecture). It uses Preact for interactivity, file-based routing, server-side rendering, and edge-optimized deployment on Deno Deploy. Think Next.js but for Deno with better performance.
Q: How does testing work in Deno?Built-in: Deno.test() for defining tests, step-based nesting, async support, filtering (--filter), focus/ignore options, code coverage (--cover), and per-test permissions. No external test framework needed. superoak for HTTP integration testing.
💡Top Deno interview topics: permission system, URL imports vs npm, built-in TypeScript/ESM, JSR registry, standard library, Oak/Hono frameworks, deno test, import maps, Fresh framework, FFI, deno compile, and Deno Deploy.