How to Use Deno: The Beginner's Guide (First Script, the Permissions Model, a Real Server, and Deploy)
CodexFuse Team · 2026-10-31T03:57:27.257Z · 16 min read
Install Deno free, write your first TypeScript script with no build step, grant permissions one flag at a time, build a real Deno.serve server, and deploy it — the beginner's guide to the secure JavaScript runtime.
How do you use Deno?
You use Deno by installing one binary, writing a TypeScript file, and running it with deno run — there is no build step, no package.json to create, and no node_modules to install. Deno runs TypeScript directly, denies file, network, and environment access until you grant it, and ships with the formatter, linter, test runner, and HTTP server built in.
- Install. One command on macOS/Linux, one on Windows PowerShell — then
deno --versionconfirms you are on Deno 2.9, the current release as of late October 2026 (deno.com). - Write a first script. Create
hello.tswith aconsole.logline and rundeno run hello.ts. TypeScript compiles on the fly; nothing is configured. - Grant permissions one flag at a time. Your script can do nothing by default. When it needs the network, run
deno run --allow-net hello.tsinstead of reaching for-A. - Serve. Call
Deno.serve()in a file, run it with--allow-net, and openhttp://localhost:8000— Deno listens on port 8000 unless you say otherwise. - Grow up deliberately. Add packages with
npm:orjsr:specifiers, wire repeat commands intodeno.jsontasks, and check quality withdeno fmt,deno lint, anddeno test. - Ship. Compile to a single self-contained binary with
deno compile, or deploy the same code to Deno Deploy's edge platform with no config file.
The six steps above are the whole workflow. The rest of this guide explains what each step is actually doing, so the permissions model feels like a feature instead of a nuisance. You will build a real JSON API server along the way — when it is running, point our free REST API tester at http://localhost:8000 to poke at your endpoints without writing a client.
What is Deno, and why would you pick it over Node?
Deno is a secure-by-default JavaScript and TypeScript runtime built on V8 and Rust, created by Ryan Dahl — the original creator of Node.js — to fix the design regrets he had with Node. The headline difference is the security posture: a Node script gets complete system access the moment it runs, while a Deno script starts with nothing and must be granted each capability explicitly.
That single decision shapes everything else. Because Deno assumes distrust, it also ships the tooling Node leaves to the ecosystem: TypeScript and JSX run with zero configuration, deno fmt and deno lint replace Prettier and ESLint setup, deno test replaces a test framework for most projects, and a standard library of audited modules lives on JSR instead of scattered across npm. Deno 2, released in late 2024, added the missing piece that kept Deno an experiment for years: real Node.js compatibility — npm: specifiers, node: imports, package.json support, and lockfile reading — so existing Node projects can move over gradually instead of all at once.
The table below is CodexFuse's synthesis of deno.com's own published benchmarks and the two runtimes' documented designs. Deno's homepage numbers (Deno 2.9 canary vs Node v26.5 vs Bun 1.4 canary, median of 3 runs on AMD EPYC x86_64, fetched October 30, 2026) put Deno ahead on server throughput: 72,400 requests/second vs Bun's 68,200 and Node's 44,000 on the Realworld benchmark, with 1.87 ms p99 latency against 2.80 ms and 3.76 ms. Bun still wins TypeScript startup (14.7 ms vs Deno's 19.6 ms) and peak memory. Treat vendor benchmarks as directional — your workload will differ — but the ordering is consistent across their published runs.
| Deno 2.9 | Node.js 26 | Bun 1.4 | |
|---|---|---|---|
| Default security posture | Deny everything; grant per flag | Full system access | Full system access |
| TypeScript | Native, zero config | Needs setup (or type stripping) | Native, fastest startup |
| Package installs | URL / JSR / npm:, global cache | npm + node_modules | npm-compatible, very fast |
| Formatter / linter / test runner | Built in (fmt, lint, test) | Bring your own | Built-in test runner; Biome-style fmt |
| HTTP server | Deno.serve() built in, port 8000 | node:http or a framework | Bun.serve() built in |
| Single-binary output | deno compile | Experimental / third-party | bun build --compile |
| Best fit | Security-sensitive services, TypeScript APIs | The biggest ecosystem, maximum hiring pool | Speed-obsessed scripts and servers |
The honest beginner advice: Node's ecosystem is still the largest, and Bun is the speed pick — but if you want TypeScript without a toolchain and a runtime that treats every dependency as untrusted until proven otherwise, Deno is the permissions-first lane, and this guide stays in it.
How do you install Deno?
You install Deno with one command and verify it with deno --version. On macOS or Linux, the official installer from deno.com is:
curl -fsSL https://deno.land/install.sh | sh
On Windows, open PowerShell and run the official Windows installer (documented in the October 2026 DEV Community walkthrough of Deno on Cloudflare Workers):
irm https://deno.land/x/install/install.ps1 | iex
Then confirm the install and check you are current:
deno --version deno upgrade
deno --version should report a 2.9.x release — 2.9 is the current line as of late October 2026, with 2.9.8 verified in the wild on October 25, 2026. If the command is not found after installing, your shell's PATH is missing the install directory; the installer prints the exact line to add (usually export PATH="$HOME/.deno/bin:$PATH"). deno upgrade moves you to the newest stable release whenever one ships, which is how you will pick up future 2.x minors.
How do you run your first Deno script?
You run your first Deno script by saving TypeScript in a .ts file and executing deno run on it — Deno type-strips and runs the file directly, so there is no tsc step and no tsconfig to create. Create hello.ts:
const language: string = "TypeScript";
console.log(`Hello from Deno, running ${language} with no build step.`);
// Top-level await works anywhere — no async wrapper needed.
const res = await fetch("https://api.github.com/zen");
console.log(await res.text());
Run it with:
deno run hello.ts
The first line prints immediately. The fetch line will not — Deno kills it with a permission error, because network access is denied by default. That failure is the whole point of the next section, and it is the correct behavior: notice how the error names exactly which permission was requested and which flag grants it. Deno never silently lets a script phone home.
Two details worth knowing now. First, fetch, Request, and Response are the web-standard APIs — the same ones you know from browsers — so skills transfer in both directions. Second, rename the file to hello.js and it still runs; Deno treats TypeScript as the default authoring language but never forces it on you.
How does the Deno permissions model work?
The permissions model is simple: every sensitive capability starts denied, and you open exactly what the program needs, nothing more. Think of it as a bouncer with a guest list — your code gets in, but each of its capabilities (network, filesystem, environment variables, subprocesses, high-resolution timers) waits outside until you name it on the command line.
The cheat sheet below covers the flags you will actually use as a beginner. It is CodexFuse's synthesis of the permission reference on deno.com and the Deno docs, current for Deno 2.9:
| Flag | Unlocks | Example |
|---|---|---|
--allow-net | All network access (fetch, servers, DNS) | deno run --allow-net hello.ts |
--allow-net=api.example.com | Network, but only to named hosts | deno run --allow-net=api.github.com main.ts |
--allow-read | Reading files | deno run --allow-read config.json app.ts |
--allow-read=./data | Reading, scoped to one directory | deno run --allow-read=./data app.ts |
--allow-write | Writing files | deno run --allow-write=./logs app.ts |
--allow-env | Reading environment variables | deno run --allow-env=PORT,API_KEY app.ts |
--deny-net | Explicitly blocks network, even if allowed elsewhere | deno run --allow-net --deny-net=evil.com app.ts |
-A / --allow-all | Everything — the escape hatch, not the default | Local experiments only; never in committed scripts |
The discipline that makes this model work is granting one flag at a time and reading the error. Run hello.ts above and Deno tells you it needs --allow-net; add the flag, and the GitHub Zen API answers. If you had started with -A, you would never have learned which capability the script actually uses — and a compromised dependency could have used all the rest. Deno 2.5 added permission sets (--permission-set=dev) for teams that want named bundles of grants, and deno audit plus deno audit fix scan your dependency tree for known-vulnerable packages — supply-chain safety is the third layer of the model, alongside default-deny and scoped grants.
What is the one permissions habit that matters most?
Keep the flags next to the command that needs them, in a deno.json task or your README, and never commit a script whose run instructions are just deno run -A. Six months from now, the explicit flag list is documentation: it tells the next reader exactly what the program touches.
How do you build a real server with Deno.serve?
You build a server by calling Deno.serve() with a handler function and running the file with --allow-net. Deno listens on port 8000 by default — confirmed in the Deno docs' serve reference, fetched October 30, 2026. Create server.ts:
// server.ts — a tiny JSON API. Run: deno run --allow-net server.ts
Deno.serve({ port: 8000 }, (req) => {
const url = new URL(req.url);
if (url.pathname === "/") {
return new Response("Deno server is running. Try /api/time.");
}
if (url.pathname === "/api/time") {
return Response.json({ now: new Date().toISOString(), runtime: "deno" });
}
return new Response("Not found", { status: 404 });
});
Start it with deno run --allow-net server.ts and open http://localhost:8000/api/time — you get a JSON timestamp. Test the endpoints properly with our Postman beginner guide's approach: send GET requests, inspect status codes, and confirm the 404 branch behaves. The handler receives a standard web Request and returns a standard Response, so everything you know about the Fetch API applies — routing is just if checks on url.pathname until the app outgrows them.
A warning for readers of old tutorials: guides from the Deno 1.x era build servers with import { serve } from "https://deno.land/std/http/server.ts" and a for await request loop. That pattern still exists, but the built-in Deno.serve() replaced it as the recommended API years ago — if a tutorial's first server import comes from a URL instead of the Deno namespace, the tutorial is outdated. When the route list grows past a handful, reach for a framework like Hono (deno add jsr:@hono/hono, then Deno.serve(app.fetch)), which the Deno docs themselves use in their examples.
How do you restart the server automatically while you develop?
Add --watch: deno run --watch --allow-net server.ts restarts the process on every save. Better still, put it in a task (next section but one) so the whole team runs the same command.
How do you add npm packages and JSR modules?
You add packages with specifiers instead of an install step: npm: for the npm registry and jsr: for Deno's own registry, and Deno fetches, caches, and type-checks them on first run. No node_modules folder appears in your project — dependencies live in Deno's global cache, and a deno.lock file pins their exact versions.
// npm package, no package.json required
import lodash from "npm:lodash@4";
// JSR module — Deno's registry, home of the audited standard library
import { assertEquals } from "jsr:@std/assert@^1";
For a nicer developer experience, register dependencies once with deno add — deno add jsr:@std/assert or deno add npm:lodash — which records them in deno.json under an "imports" map so the rest of your code imports the bare name. When your server needs persistent data, that is also the moment to pick a database: our Supabase beginner guide walks through the hosted Postgres option, and its JavaScript client imports into Deno through the npm: specifier like any other package.
One security note that matters here: Deno disables npm postinstall scripts by default. A package that needs a postinstall step must be opted in with --allow-scripts, and deno audit flags known-vulnerable versions in your tree. That is the supply-chain layer of the permissions model doing its job — the install step is exactly where malicious packages historically strike.
How do deno.json tasks work?
deno.json tasks are Deno's answer to npm scripts: named commands defined in your project's config file and run with deno task. The syntax, from the Deno docs' configuration reference fetched October 30, 2026, is:
{
"tasks": {
"dev": "deno run --watch --allow-net server.ts",
"test": "deno test --allow-net",
"lint": "deno lint",
"fmt": "deno fmt --check"
}
}
Run any of them with deno task dev or deno task test. The task runner uses a cross-platform shell, so the same deno.json works on Windows, macOS, and Linux — and tasks can declare dependencies on each other and run in parallel with wildcard patterns like deno task "build:*". Two conveniences: deno init my_project scaffolds a project with a starter deno.json and a dev task already wired, and if you bring an existing Node project, Deno reads its package.json scripts directly — deno task start runs them with no conversion step.
This is also where the permissions habit from earlier pays off: the flags live in the task definition, version-controlled and visible, instead of in one developer's shell history.
How do formatting, linting, and testing work in Deno?
They work with three commands and zero configuration files: deno fmt formats your code, deno lint checks it against a recommended rule set borrowed from ESLint's wisdom, and deno test runs your test files. There is no Prettier config, no ESLint config, and no test framework to install for the common cases.
// math_test.ts — Deno discovers *_test.ts files automatically
import { assertEquals } from "jsr:@std/assert";
Deno.test("doubling works", () => {
assertEquals(2 * 21, 42);
});
Run deno test and the runner finds the file, executes it in the sandbox (note: tests need their permission flags too — deno test --allow-net if they touch the network), and reports results. The Deno docs recommend adding deno fmt --check to CI so formatting is enforced on every pull request — which pairs naturally with the GitHub beginner guide if you are setting up your first Actions workflow.
How do you compile and deploy a Deno app?
You ship a Deno app in two ways: compile it to a single self-contained binary with deno compile, or deploy the source to a platform that runs Deno for you. For the binary route:
deno compile --allow-net --output my-server server.ts
The result is one executable with the runtime embedded — no Deno install needed on the target machine, and cross-compilation targets like --target x86_64-pc-windows-msvc build Windows executables from other platforms (verified in an October 25, 2026 build log shipping a Fresh web app as a Windows EXE). That binary drops into any deployment you already know: a VPS, a container, or a serverless function. If you are weighing the self-hosted path, our VPS hosting explainer covers what a virtual private server actually gives you before you rent one.
The zero-config route is Deno Deploy, the edge platform built for Deno: push your code and it runs globally with no config file, first-class support for Deno KV and OpenTelemetry included. One deployment detail applies everywhere: platforms inject the port through the PORT environment variable rather than letting you hardcode 8000, so production servers read it — Deno.serve() honors the platform's address automatically when the environment provides it.
A note on the bigger picture, attributed properly: an October 11, 2026 DEV Community analysis by Naveen Malothu reports that Cloudflare announced the acquisition of Deno earlier that week, folding the runtime into Cloudflare's edge platform with promised Workers integration. Deno's own homepage, fetched for this guide on October 30, 2026, still presents Deno Deploy as its platform and makes no acquisition claim of its own — treat the acquisition as reported news, not as a change to how you install or run Deno today.
What are the most common beginner mistakes?
Five traps catch nearly every Deno beginner, and all five are avoidable once you see them listed. First, reaching for -A immediately — it works, but it teaches you nothing and ships a program whose real requirements are unknown; grant one flag at a time instead. Second, following a Deno 1.x tutorial — if the guide's version output says deno 1.0.0 or its server imports from deno.land/std/http/server.ts, close it and find one written for Deno 2. Third, assuming port 3000 — Node muscle memory says 3000, but Deno.serve() defaults to 8000; pass { port: 3000 } explicitly if you want it. Fourth, forgetting that tests are sandboxed too — a test that fetches a URL fails without --allow-net on the deno test command, which surprises people exactly once. Fifth, ignoring deno.lock — commit it like you would a package-lock file; it is what makes your builds reproducible, and deno install --frozen in CI refuses to silently resolve new versions.
Sources
Deno homepage — current release Deno 2.9, install command, permissions model, built-in toolchain, benchmark figures vs Node v26.5 and Bun 1.4 (deno.com, fetched October 30, 2026). Deno 2.9 release notes — deno desktop headline feature (deno.com/blog/v2.9). Deno docs: deno serve CLI reference — default port 8000, --port override (docs.deno.com/runtime/reference/cli/serve/, fetched October 30, 2026). Deno docs: writing an HTTP server — Deno.serve() API, port configuration (docs.deno.com/runtime/fundamentals/http_server/). Deno docs: deno task reference and deno.json configuration reference — tasks syntax, cross-platform shell, wildcard and dependency features (docs.deno.com, fetched October 30, 2026). Deno docs: initialize a project tutorial — deno init scaffolding (docs.deno.com/examples/initialize_project_tutorial/). Naveen Malothu, "Cloudflare Acquires Deno – What It Means for AI & DevOps Engineers" — acquisition report, install commands, Workers example (DEV Community, published October 11, 2026). Critical Developer blog — Fresh web app compiled to Windows EXE with Deno 2.9.8, tested deno.json tasks (blog.criticaldeveloper.com, October 25, 2026). Sevalla docs — deploying a Deno application, PORT environment variable, deno install --frozen (docs.sevalla.com, fetched October 30, 2026). x-cmd Deno install guide — permission sets (--permission-set=dev, Deno 2.5+), zero-config TypeScript (x-cmd.com). Reactgo "A Beginner's Guide to Deno" and CodeForGeek "Deno Tutorial" — representative Deno 1.x-era guides showing the outdated std serve-loop pattern (used as the outdated-pattern example).
Frequently Asked Questions
Should I learn Deno or Node.js first?
Learn Node.js first if your goal is employment — its ecosystem and job market are still far larger. Learn Deno first if your goal is understanding modern server JavaScript cleanly: one binary, TypeScript with no build step, and explicit permissions teach better habits, and Deno 2's Node compatibility means the skills transfer back.
Does Deno replace npm and package.json?
No — it absorbs them. Deno imports npm packages directly with npm: specifiers, reads existing package.json files and their scripts, and even understands npm, pnpm, and yarn lockfiles. You can adopt Deno without abandoning the npm ecosystem.
Can Deno run my existing Node.js project?
Often yes, since Deno 2: point Deno at the project and it resolves the same dependencies from package.json, runs the project's scripts via deno task, and supports node: imports and CommonJS. Deno's homepage positions it as "the drop-in JavaScript runtime for Node developers" — try deno run on your entry point before assuming a rewrite.
Is Deno free for commercial use?
Yes. Deno is a fast, open-source runtime — the CLI, the standard library on JSR, and the core tooling cost nothing, and deno compile produces binaries you can ship anywhere. Deno Deploy, the hosted edge platform, is the commercial product with its own pricing; the runtime itself has no license fee.
What is the Cloudflare–Deno story?
An October 11, 2026 DEV Community analysis reports that Cloudflare announced the acquisition of Deno that week, aiming at tighter Workers integration and edge AI workloads. Deno's own site, checked October 30, 2026, makes no acquisition claim and still presents Deno Deploy as its platform — so treat it as reported industry news that does not change how you install or run Deno today.