Deno 2 Complete Guide | Node.js Compatibility, npm, and JSR
이 글의 핵심
Deno 2 is a secure JavaScript runtime that now runs Node.js and npm packages natively. With built-in TypeScript, a permissions model, and the new JSR package registry, it's a compelling alternative to Node.js for new projects.
What Changed in Deno 2
Deno 2 (released October 2024) made Node.js compatibility a first-class feature:
| Feature | Deno 1 | Deno 2 |
|---|---|---|
| npm packages | Partial | Full native support |
node_modules/ | Not created | Created automatically |
package.json | Ignored | Fully supported |
require() | No | Yes |
| Node built-ins | Partial | Full (node:fs, node:path, etc.) |
| JSR | No | Yes |
Installation
# macOS / Linux
curl -fsSL https://deno.land/install.sh | sh
# Windows
irm https://deno.land/install.ps1 | iex
# Homebrew
brew install deno
# Check version
deno --version
1. Running Code
# Run a local file
deno run server.ts
# Run with permissions
deno run --allow-net --allow-read --allow-env server.ts
# Run a URL directly
deno run https://deno.land/std/examples/welcome.ts
# Run npm package
deno run npm:cowsay "Hello Deno"
# Watch mode (re-run on changes)
deno run --watch server.ts
Permission flags
--allow-net # all network access
--allow-net=api.com # only api.com
--allow-read # all filesystem reads
--allow-read=./data # only ./data directory
--allow-write # all filesystem writes
--allow-env # all env vars
--allow-env=API_KEY # only API_KEY
--allow-run # spawn subprocesses
--allow-all # all permissions (like Node.js — use carefully)
-A # shorthand for --allow-all
2. TypeScript — Zero Config
Deno runs TypeScript natively without compilation:
// server.ts — just run it, no tsconfig needed
interface User {
id: number
name: string
email: string
}
async function fetchUser(id: number): Promise<User> {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return res.json()
}
const user = await fetchUser(1)
console.log(`Hello, ${user.name}!`)
deno run --allow-net server.ts
3. npm Compatibility
// Use npm packages with npm: prefix
import express from 'npm:express@4'
import { z } from 'npm:zod'
import axios from 'npm:axios'
const app = express()
app.get('/', (req, res) => {
res.json({ message: 'Express on Deno!' })
})
app.listen(3000)
deno run --allow-net server.ts
# Creates node_modules/ automatically — no npm install needed
Using package.json (Node.js project)
// package.json — Deno 2 respects this
{
"dependencies": {
"express": "^4.18.0",
"zod": "^3.22.0"
}
}
# Install (like npm install but with Deno)
deno install
# Run
deno run --allow-net server.ts
4. JSR — JavaScript Registry
JSR is a TypeScript-first registry that works across runtimes:
// Import from JSR (TypeScript-native packages)
import { Hono } from 'jsr:@hono/hono'
import { join } from 'jsr:@std/path'
import { assertEquals } from 'jsr:@std/assert'
const app = new Hono()
app.get('/', (c) => c.text('Hello!'))
Deno.serve(app.fetch)
// deno.json — lock JSR versions
{
"imports": {
"@std/path": "jsr:@std/path@^1.0.0",
"@std/assert": "jsr:@std/assert@^1.0.0",
"hono": "jsr:@hono/hono@^4.0.0",
"zod": "npm:zod@^3.22.0"
}
}
Publishing to JSR:
# Publish your package to JSR
deno publish
5. Standard Library
Deno’s standard library (@std) covers common needs:
import { join, dirname, basename } from 'jsr:@std/path'
import { exists, readTextFile, writeTextFile } from 'jsr:@std/fs'
import { serve } from 'jsr:@std/http'
import { assertEquals, assertThrows } from 'jsr:@std/assert'
import { delay } from 'jsr:@std/async'
import { parse } from 'jsr:@std/csv'
import { encodeBase64, decodeBase64 } from 'jsr:@std/encoding'
// File operations
const content = await Deno.readTextFile('./config.json')
const config = JSON.parse(content)
await Deno.writeTextFile('./output.txt', 'Hello, Deno!')
// Directory
for await (const entry of Deno.readDir('./src')) {
console.log(entry.name, entry.isFile ? 'file' : 'dir')
}
// Path
const full = join(Deno.cwd(), 'src', 'index.ts')
6. HTTP Server
// Native Deno HTTP (recommended for new projects)
const handler = async (request: Request): Promise<Response> => {
const url = new URL(request.url)
if (url.pathname === '/') {
return new Response('Hello from Deno!', {
headers: { 'Content-Type': 'text/plain' },
})
}
if (url.pathname === '/api/users' && request.method === 'GET') {
const users = await fetchUsers()
return Response.json(users)
}
return new Response('Not Found', { status: 404 })
}
// Deno.serve — the recommended way
Deno.serve({ port: 3000 }, handler)
With Hono (recommended for APIs)
import { Hono } from 'jsr:@hono/hono'
import { cors } from 'jsr:@hono/hono/cors'
import { jwt } from 'jsr:@hono/hono/jwt'
const app = new Hono()
app.use('*', cors())
app.get('/api/health', (c) => c.json({ status: 'ok' }))
app.get('/api/users/:id', async (c) => {
const id = c.req.param('id')
const user = await getUser(id)
if (!user) return c.json({ error: 'Not found' }, 404)
return c.json(user)
})
Deno.serve({ port: 3000 }, app.fetch)
7. Built-in Tooling
Deno ships with everything built in — no npm install needed:
# Format
deno fmt # format all .ts/.js files
deno fmt src/ # format directory
deno fmt --check # check without modifying
# Lint
deno lint # lint all files
deno lint src/
# Test
deno test # run all *_test.ts files
deno test --watch # watch mode
deno test src/user_test.ts # specific file
# Benchmark
deno bench # run all *_bench.ts files
# Type check
deno check src/index.ts
# Bundle
deno compile --allow-net src/cli.ts # compile to single binary
# REPL
deno
Testing
// user_test.ts
import { assertEquals, assertRejects } from 'jsr:@std/assert'
Deno.test('adds two numbers', () => {
assertEquals(1 + 2, 3)
})
Deno.test('fetches user', async () => {
const user = await fetchUser(1)
assertEquals(user.id, 1)
assertEquals(typeof user.name, 'string')
})
Deno.test('throws on invalid id', async () => {
await assertRejects(
() => fetchUser(-1),
Error,
'Invalid user ID'
)
})
// Group tests
Deno.test('UserService', async (t) => {
await t.step('create', async () => {
const user = await createUser({ name: 'Alice' })
assertEquals(user.name, 'Alice')
})
await t.step('delete', async () => {
await deleteUser(user.id)
// ...
})
})
8. deno.json Configuration
{
"tasks": {
"start": "deno run --allow-net --allow-env src/main.ts",
"dev": "deno run --watch --allow-net --allow-env src/main.ts",
"test": "deno test --allow-net",
"lint": "deno lint",
"fmt": "deno fmt"
},
"imports": {
"hono": "jsr:@hono/hono@^4.0.0",
"zod": "npm:zod@^3.22.0",
"@std/assert": "jsr:@std/assert@^1.0.0"
},
"compilerOptions": {
"strict": true,
"lib": ["deno.window"]
},
"fmt": {
"lineWidth": 100,
"indentWidth": 2,
"singleQuote": true
},
"lint": {
"rules": {
"include": ["no-unused-vars", "eqeqeq"]
}
}
}
# Run tasks
deno task start
deno task dev
deno task test
Migrating from Node.js
// Node.js
import { readFileSync } from 'fs'
import { join } from 'path'
import { createServer } from 'http'
// Deno 2 (Node.js compat)
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { createServer } from 'node:http'
// Same code works! Just add "node:" prefix to built-ins
# Quick compatibility check
deno run --allow-all server.js # try running existing Node.js file
Deno vs Node.js vs Bun
| Deno 2 | Node.js 22 | Bun 1.x | |
|---|---|---|---|
| TypeScript | Native | Via tsx/ts-node | Native |
| npm compat | Full | Native | Full |
| Security | Permissions model | Full access | Full access |
| Built-in tools | fmt, lint, test, bench | None | Bundler, test |
| Performance | Fast | Fast | Fastest |
| Ecosystem | npm + JSR | npm | npm |
| Best for | Security-conscious, new projects | Existing ecosystem | Max speed |
Key Takeaways
- Deno 2 runs Node.js and npm packages natively —
npm:express,node:fs - No install step —
deno runfetches dependencies on first run - Permissions model — explicit flags required for network, filesystem, env access
- JSR — TypeScript-first registry, works across Deno/Node/Bun/browsers
- Built-in tooling —
deno fmt,deno lint,deno test,deno compile(no config needed) - deno.json — replaces package.json, tsconfig.json, .eslintrc, .prettierrc
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Deno 2 runs Node.js code and npm packages natively. Covers the Deno runtime, npm compatibility, JSR (JavaScript Registry…
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- TypeScript 5 Complete Guide | Decorators· satisfies
- Hono Framework Guide | Ultra-Fast Edge Web Framework
Keywords Covered in This Article (Related Search Terms)
This article covers Deno, JavaScript, TypeScript, Node.js, Runtime, Backend.