Getting Started with Deno

· 5 min read · Updated April 22, 2026 · intermediate
deno javascript runtime typescript nodejs http-server

Overview

Deno is a modern JavaScript runtime built by the original creator of Node.js. It runs JavaScript and TypeScript directly, has no node_modules directory by default, and locks in security through explicit permissions. Where Node.js requires separate tools for formatting, linting, and testing, Deno ships them all built in.

Deno 2 added Node.js and npm compatibility, so existing npm packages work inside Deno. The toolchain stays focused: you get deno run, deno test, deno lint, deno fmt, and deno compile out of the box.

Installation

On macOS and Linux:

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

On Windows (PowerShell):

irm https://deno.land/install.ps1 | iex

Or via package managers:

# Homebrew
brew install deno

# Chocolatey
choco install deno

Verify the installation:

deno --version
# deno 2.x.x

Running Your First Script

Create a file called hello.ts:

// hello.ts
const message: string = "Hello from Deno!";
console.log(message);

Run it:

deno run hello.ts
# Hello from Deno!

No build step. No compiler flags. Deno handles TypeScript natively.

Permissions

Deno blocks access to the network, file system, and environment by default. A script that tries to read a file without permission fails:

// read_file.ts — this will fail
const text = await Deno.readTextFile("/etc/passwd");
console.log(text);
deno run read_file.ts
# Error: Permission denied (os error 63)
#   Hint: Use --allow-read flag to allow file system access

Grant specific permissions with flags:

deno run --allow-read=/tmp hello.ts
# Only /tmp is readable, other paths are blocked

Common permission flags:

FlagWhat it allows
--allow-readFile system read
--allow-writeFile system write
--allow-netNetwork access
--allow-envEnvironment variables
--allow-runRunning sub-processes
--allow-allEverything (avoid in production)

You can also scope permissions to specific hosts:

deno run --allow-net=api.example.com fetch_data.ts

The deno.json Configuration File

The deno.json (or deno.jsonc) file configures your project. Create one in your project root:

{
  "compilerOptions": {
    "allowJs": true,
    "lib": ["deno.window"],
    "strict": true
  },
  "tasks": {
    "dev": "deno run --watch --allow-net main.ts",
    "start": "deno run --allow-net --allow-env main.ts"
  },
  "imports": {
    "@oak/oak": "jsr:@oak/oak@^14"
  },
  "exclude": ["**/*.test.ts"]
}

The tasks section defines shortcuts. Instead of typing the full command:

deno task dev
# runs: deno run --watch --allow-net main.ts

The imports section registers JSR or npm specifiers for your project.

Dependencies and JSR

Deno uses JSR, a JavaScript registry with native TypeScript support. Import packages directly from their JSR or npm specifiers:

// Using JSR package
import { oak } from "jsr:@oak/oak@^14";

// Using npm package
import express from "npm:express@4";

No package.json, no node_modules. Deno caches downloads and locks them in deno.lock.

Update the lock on dependency changes:

deno cache --lock=deno.lock --lock-write main.ts

Using npm Packages

Deno 2 supports npm packages directly:

import express from "npm:express@4";
import { z } from "npm:zod@3";

const app = express();

app.get("/", (req, res) => {
  res.send("Hello from Deno with npm packages!");
});

app.listen(3000);

Deno installs them into a local node_modules directory when package.json is present. You can also import npm packages directly without a package.json using the npm: specifier.

Running a Web Server

Deno ships with web platform APIs — fetch, Request, Response, Headers — available globally, no imports needed.

// server.ts
const handler = async (req: Request): Promise<Response> => {
  const url = new URL(req.url);

  if (url.pathname === "/api/hello") {
    const name = url.searchParams.get("name") ?? "World";
    return Response.json({ message: `Hello, ${name}!` });
  }

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

Deno.serve({ port: 8000 }, handler);
deno run --allow-net server.ts
# Server running at http://localhost:8000

Deno.serve() is the native HTTP server. There’s no need to import an external package.

File System Access

Read and write files with the Deno global:

// Write to a file
const encoder = new TextEncoder();
const data = encoder.encode("Hello, Deno!\n");
await Deno.writeFile("hello.txt", data);

// Read back
const text = await Deno.readTextFile("hello.txt");
console.log(text);  // Hello, Deno!
deno run --allow-write --allow-read write_read.ts

Environment Variables

Read environment variables with Deno.env:

const port = Deno.env.get("PORT") ?? "8000";
const dbUrl = Deno.env.get("DATABASE_URL");

console.log(`Server starting on port ${port}`);

// Only allow specific env vars
// deno run --allow-env=PORT,DATABASE_URL server.ts

Built-in Testing

Write and run tests with Deno.test:

// math.ts
export function add(a: number, b: number): number {
  return a + b;
}

// math.test.ts
import { assertEquals } from "jsr:@std/assert";
import { add } from "./math.ts";

Deno.test("adds two numbers", () => {
  assertEquals(add(2, 3), 5);
});

Deno.test("handles negatives", () => {
  assertEquals(add(-1, 1), 0);
});

Run tests:

deno test
# Check file:///path/to/math.test.ts
# ok ... adds two numbers
# ok ... handles negatives

The @std/assert package provides test assertions. Deno has a built-in test runner with built-in assertion functions — no external test framework required for simple tests.

Lint and Format

Deno includes a linter and formatter:

# Format all TypeScript files
deno fmt

# Check for lint errors
deno lint

Configure linting and formatting rules in deno.json:

{
  "lint": {
    "include": ["src/"],
    "rules": {
      "tags": ["recommended"]
    }
  },
  "fmt": {
    "useTabs": true,
    "lineWidth": 80,
    "singleQuote": true
  }
}

Hot Reload During Development

Use --watch to restart on file changes:

deno run --watch --allow-net server.ts

The server restarts automatically whenever you save a change.

Compiling to an Executable

Deno can compile your script into a standalone binary:

deno compile --allow-net --allow-env -o my_server server.ts

This produces a native executable with Deno embedded. No runtime needed on the target machine.

Deno vs Node.js

FeatureDenoNode.js
TypeScriptNative, no configRequires ts-node or compilation
package.jsonOptionalRequired
node_modulesAuto-generated, optionalRequired
Built-in toolsLint, format, test, compileNone — external packages needed
PermissionsSecure by defaultFull access by default
ESM-firstYesCommonJS default
npm supportYes (Deno 2+)Yes

See Also