DEV Community

Carlos Oliva Pascual
Carlos Oliva Pascual

Posted on • Originally published at stacknotice.com

Deno 2 Complete Guide: Runtime, Packages & Deploy (2026)

Deno 2 is a different runtime from what Deno was in 2020. The original version launched with a hard stance against node_modules and npm. The vision was clean but impractical — most real-world JavaScript code assumes npm packages exist.

Deno 2 walked that back. You can now run npm packages, import from npm directly, use node_modules, and run most Node.js code without modification.

What Changed in Deno 2

  • npm packages workimport { express } from 'npm:express' or install into node_modules
  • Node.js APIs implementedfs, path, http, crypto, stream, and more
  • node_modules supportdeno install creates a proper node_modules directory
  • Workspaces — monorepo support with multiple deno.json configs
  • JSR — the new TypeScript-first registry, replacing deno.land/x
  • LTS releases — stable tracks for production

Installation

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

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

deno --version
Enter fullscreen mode Exit fullscreen mode

Package Management

npm Packages

// No install needed — resolved on first run, cached globally
import express from 'npm:express@4'
import { z } from 'npm:zod'

// Or install explicitly for editor tooling
deno install npm:express npm:zod npm:hono
Enter fullscreen mode Exit fullscreen mode

JSR — TypeScript-First Registry

deno add jsr:@std/path
deno add jsr:@hono/hono
Enter fullscreen mode Exit fullscreen mode
import { join } from 'jsr:@std/path'
Enter fullscreen mode Exit fullscreen mode

deno.json

{
  "imports": {
    "@/": "./src/",
    "hono": "jsr:@hono/hono@^4",
    "zod": "npm:zod@^3"
  },
  "tasks": {
    "dev": "deno run --watch --allow-net src/main.ts",
    "build": "deno compile --allow-net src/main.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

HTTP Server

Deno.serve({ port: 3000 }, async (req: Request) => {
  const url = new URL(req.url)

  if (url.pathname === '/api/health') {
    return Response.json({ status: 'ok' })
  }

  return new Response('Not Found', { status: 404 })
})
Enter fullscreen mode Exit fullscreen mode

With Hono

import { Hono } from 'jsr:@hono/hono'
import { zValidator } from 'npm:@hono/zod-validator'
import { z } from 'npm:zod'

const app = new Hono()

app.get('/api/posts', async (c) => {
  const posts = await db.query('SELECT * FROM posts WHERE published = 1')
  return c.json(posts)
})

app.post('/api/posts',
  zValidator('json', z.object({ title: z.string(), content: z.string() })),
  async (c) => {
    const data = c.req.valid('json')
    const post = await db.insert(data)
    return c.json(post, 201)
  }
)

Deno.serve({ port: 3000 }, app.fetch)
Enter fullscreen mode Exit fullscreen mode

Permissions Model

Deno's security model is explicit — code can't access the network, file system, or environment without you granting permission:

deno run --allow-net main.ts
deno run --allow-read=./src,./config main.ts
deno run --allow-write=/tmp main.ts
deno run --allow-env=DATABASE_URL,PORT main.ts

# Allow everything (dev only)
deno run -A main.ts
Enter fullscreen mode Exit fullscreen mode

Practical tip: Start with -A during development, then lock down permissions before shipping.

File APIs

const text = await Deno.readTextFile('./config.json')
const data = JSON.parse(text)

await Deno.writeTextFile('./output.txt', 'Hello, Deno!')

// Append
await Deno.writeTextFile('./log.txt', 'entry\n', { append: true })

// Directory
for await (const entry of Deno.readDir('./src')) {
  console.log(entry.name, entry.isFile)
}
Enter fullscreen mode Exit fullscreen mode

Built-in Toolchain

deno fmt              # format (like Prettier)
deno fmt --check      # CI check

deno lint             # lint
deno check main.ts    # type-check
deno test             # run tests
deno doc src/utils.ts # generate docs
Enter fullscreen mode Exit fullscreen mode

Compile to Binary

# Standalone binary — no Deno runtime needed on target machine
deno compile --allow-net --output my-app src/main.ts

# Cross-compile
deno compile --target x86_64-unknown-linux-gnu --output app-linux src/main.ts
deno compile --target x86_64-pc-windows-msvc --output app.exe src/main.ts
Enter fullscreen mode Exit fullscreen mode

Deno KV

Built-in key-value store, available locally and on Deno Deploy:

const kv = await Deno.openKv()

await kv.set(['users', userId], { name: 'Alice', email: 'alice@example.com' })

const result = await kv.get(['users', userId])
console.log(result.value?.name)

const entries = kv.list({ prefix: ['users'] })
for await (const entry of entries) {
  console.log(entry.key, entry.value)
}
Enter fullscreen mode Exit fullscreen mode

Deno vs Node.js vs Bun

Deno 2 Node.js Bun
TypeScript Native Requires tsx Native
Formatting Built-in Prettier None
Linting Built-in ESLint None
Test runner Built-in Jest/Vitest Built-in
SQLite jsr:@db/sqlite better-sqlite3 Built-in
Permissions Explicit (sandboxed) None None
Binary compile Yes (deno compile) pkg Yes
Edge deploy Deno Deploy Vercel/Railway Cloudflare Workers
npm compat High Native High
Maturity Growing Mature Growing

Choose Deno 2 if you want a secure-by-default runtime with built-in tooling. The permissions model and built-in formatter/linter/tester are genuinely useful for teams.


Full article at stacknotice.com/blog/deno-2-complete-guide-2026

Top comments (0)