はじめに
「Node.jsを作った人が、Node.jsの反省点を活かして新しいランタイムを作った」
この話を聞いたとき、正直かなり興味を惹かれました。
Denoは、Node.jsの生みの親であるRyan Dahl氏が2018年に発表した次世代JavaScriptランタイムです。GitHubでは105,000スター以上を獲得しており、2024年にはDeno 2.0がリリースされるなど、着実に進化を続けています。
Node.jsを10年以上使ってきた身としては、「じゃあ何が違うの?」という疑問がありました。結論から言うと、Denoはセキュリティと開発体験を根本から見直したランタイムです。
Denoとは
Denoは、JavaScript、TypeScript、WebAssemblyのランタイムで、V8エンジンとRust言語で構築されています。
Node.jsの設計上の問題点(Ryan Dahl氏が「10 Things I Regret About Node.js」として講演で語った内容)を解決するために生まれました。
主な設計思想:
- セキュリティ・バイ・デフォルト: ファイルやネットワークへのアクセスは明示的な許可が必要
- TypeScriptネイティブ: 設定なしでTypeScriptが動く
- Web標準準拠: ブラウザと同じAPIが使える
- オールインワン: フォーマッター、リンター、テストランナーが内蔵
公式サイト: https://deno.com GitHub: https://github.com/denoland/deno
特徴・メリット
1. セキュリティ・バイ・デフォルト
これ、意外と重要なんですよ。
Node.jsって、npmパッケージをインストールしたら、そのパッケージがファイルシステムにもネットワークにも自由にアクセスできてしまいます。悪意のあるパッケージが紛れ込んだら大変です。
Denoは違います。
# ファイルアクセスなしで実行
deno run app.ts
# ファイル読み取り許可
deno run --allow-read app.ts
# ネットワークアクセス許可
deno run --allow-net app.ts
# 特定ドメインのみ許可
deno run --allow-net=api.example.com app.ts
「このスクリプトは何にアクセスするのか」が明示的になります。30代になって思うのは、こういうセキュリティ意識って本当に大事だということ。
2. TypeScriptがそのまま動く
個人的にはこれが一番ありがたい。
// hello.ts
interface User {
id: number;
name: string;
email: string;
}
const user: User = {
id: 1,
name: "easegis",
email: "hello@example.com"
};
console.log(`Hello, ${user.name}!`);
deno run hello.ts
# Hello, easegis!
tsconfig.json?不要です。トランスパイル設定?不要です。いきなり動きます。
3. Web標準APIが使える
ブラウザで使い慣れたAPIがそのまま使えます。
// fetch APIがそのまま使える
const response = await fetch("https://api.github.com/users/denoland");
const data = await response.json();
console.log(data);
// Web Crypto API
const encoder = new TextEncoder();
const data = encoder.encode("Hello");
const hash = await crypto.subtle.digest("SHA-256", data);
ブラウザとサーバーで同じコードが書けるのは、コスパ的にかなりいい。
4. オールインワンツールチェーン
Denoには以下が全部入っています:
| 機能 | コマンド | 説明 |
|---|---|---|
| フォーマッター | deno fmt |
Prettier相当 |
| リンター | deno lint |
ESLint相当 |
| テストランナー | deno test |
Jest相当 |
| ドキュメント生成 | deno doc |
JSDoc生成 |
| バンドラー | deno compile |
単一実行ファイル生成 |
| タスクランナー | deno task |
npm scripts相当 |
devDependenciesに大量のツールを入れる必要がなくなります。これだけでQOL上がりますね。
5. npm互換性(Deno 2.0以降)
「でもnpmパッケージ使えないんでしょ?」
それ、昔の話です。
Deno 2.0からはnpmパッケージがそのまま使えるようになりました。
// npm:プレフィックスで直接インポート
import express from "npm:express@4";
import chalk from "npm:chalk@5";
// package.jsonも読める
既存のNode.jsプロジェクトからの移行ハードルがかなり下がりました。
インストール方法
シングルバイナリなので、インストールは簡単です。
macOS
# Homebrew
brew install deno
# シェルスクリプト
curl -fsSL https://deno.land/install.sh | sh
Linux
curl -fsSL https://deno.land/install.sh | sh
Windows
# PowerShell
irm https://deno.land/install.ps1 | iex
# Scoop
scoop install deno
# Chocolatey
choco install deno
# Winget
winget install DenoLand.Deno
バージョン確認
deno --version
# deno 2.5.6 (release, aarch64-apple-darwin)
# v8 13.x.x
# typescript 5.x.x
アップデート
deno upgrade
これだけ。時短になりますね。
基本的な使い方
Hello World
// main.ts
console.log("Hello, Deno!");
deno run main.ts
HTTPサーバー
// server.ts
Deno.serve({ port: 3000 }, (req) => {
return new Response("Hello World!");
});
deno run --allow-net server.ts
Deno.serveはWeb標準のRequest/Responseを使います。Expressを覚え直す必要なし。
ファイル操作
// read.ts
const text = await Deno.readTextFile("./data.txt");
console.log(text);
// write.ts
await Deno.writeTextFile("./output.txt", "Hello!");
deno run --allow-read read.ts
deno run --allow-write write.ts
テストを書く
// math_test.ts
import { assertEquals } from "jsr:@std/assert";
Deno.test("addition", () => {
assertEquals(1 + 1, 2);
});
Deno.test("async test", async () => {
const result = await Promise.resolve(42);
assertEquals(result, 42);
});
deno test
設定ファイル不要で即テスト実行。
deno.jsonで設定
{
"tasks": {
"dev": "deno run --watch --allow-net main.ts",
"test": "deno test --allow-read",
"fmt": "deno fmt",
"lint": "deno lint"
},
"imports": {
"@std/": "jsr:@std@1/"
}
}
deno task dev
実践的なユースケース
1. REST APIサーバー
// api.ts
Deno.serve({ port: 8000 }, async (req) => {
const url = new URL(req.url);
if (url.pathname === "/api/users" && req.method === "GET") {
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
return Response.json(users);
}
if (url.pathname === "/api/users" && req.method === "POST") {
const body = await req.json();
return Response.json({ id: 3, ...body }, { status: 201 });
}
return new Response("Not Found", { status: 404 });
});
フレームワークなしでもこれくらいなら書けます。
2. CLIツールの作成
// greet.ts
import { parseArgs } from "jsr:@std/cli/parse-args";
const args = parseArgs(Deno.args, {
string: ["name"],
default: { name: "World" },
});
console.log(`Hello, ${args.name}!`);
deno run greet.ts --name="Deno"
# Hello, Deno!
3. 単一実行ファイルにコンパイル
deno compile --allow-net server.ts
# serverという実行ファイルが生成される
./server
# Denoなしで実行可能
配布用のCLIツールを作るときに便利です。
4. Honoフレームワークとの組み合わせ
// app.ts
import { Hono } from "npm:hono";
const app = new Hono();
app.get("/", (c) => c.text("Hello Hono!"));
app.get("/api/users", (c) => c.json([{ id: 1, name: "User" }]));
Deno.serve(app.fetch);
HonoはDeno、Bun、Node.js、Cloudflare Workersで動くので、相性がいいですね。
パフォーマンス
公式ベンチマークによると、DenoはNode.jsよりも高いパフォーマンスを示しています。
| 項目 | Deno | Node.js |
|---|---|---|
| HTTP RPS | 105,200 | 48,700 |
| 起動時間 | 高速 | 普通 |
| メモリ使用量 | 効率的 | 普通 |
もちろんベンチマークは参考程度ですが、体感としても軽快です。
Bunとの違い
「DenoとBunってどう違うの?」という話。
| 項目 | Deno | Bun |
|---|---|---|
| 設計思想 | セキュリティ・Web標準重視 | 速度・互換性重視 |
| エンジン | V8 | JavaScriptCore |
| Node.js互換 | 中程度(npm対応) | 高い(ドロップイン) |
| セキュリティ | 許可制で堅牢 | Node.js同様 |
| 開発元 | Deno Land社 | Oven社 |
個人的には、新規プロジェクトでセキュリティ重視ならDeno、既存Node.jsの高速化ならBunという使い分けがいいと思います。
注意点
いいことばかり書きましたが、注意点もあります。
1. 学習コストがある
Node.jsとはAPIが違う部分があります。特にファイルI/OやHTTPサーバーの書き方が異なるので、最初は戸惑うかもしれません。
2. エコシステムはNode.jsより小さい
npm互換になったとはいえ、Deno専用のライブラリはまだ少ないです。ただ、npmが使えるようになったので実用上は問題ないことが多い。
3. 本番採用事例はまだ少なめ
Node.jsに比べると本番での採用事例は少ないです。まずは社内ツールや個人プロジェクトから試すのがおすすめ。
まとめ
Denoは「Node.jsの反省を活かした次世代ランタイム」として、確かに魅力的な選択肢です。特に以下の条件に当てはまるなら試す価値があります:
- セキュリティを重視したい
- TypeScriptを設定なしで使いたい
- ツールチェーンをシンプルにしたい
- Web標準APIで開発したい
- CLIツールを配布したい
30代になって思うのは、新しい技術をキャッチアップし続けることの大切さです。Node.jsが使えなくなるわけではないですが、Denoという選択肢を知っておくと、プロジェクトに応じて使い分けられます。
まずはインストールして、簡単なスクリプトから試してみてください。
公式サイト: https://deno.com GitHub: https://github.com/denoland/deno ドキュメント: https://docs.deno.com