Use Cases (更新: 2026/6/2)

Claude CodeでDeno開発を始める:権限管理とDeno.serveの実践ガイド

Claude CodeでDeno開発を始める実践ガイド。権限、deno.json、Deno.serve、fmt/lint/test、落とし穴を動くコードで解説。

Claude CodeでDeno開発を始める:権限管理とDeno.serveの実践ガイド

Deno開発は「動くコード」より「権限が狭いコード」から始める

DenoはJavaScript/TypeScriptを実行するランタイム、つまりコードを動かす土台です。Node.jsと違って、ファイル読み取り、ファイル書き込み、ネットワーク、環境変数、外部コマンド実行などの権限が初期状態では閉じています。Deno公式のSecurity and permissionsでも、必要なアクセスだけを明示的に許可する考え方が基本になっています。

Claude Codeと組み合わせると、DenoのAPIサーバー、CLI、テスト、設定ファイルを一気に作れます。ただし初心者が「とりあえず動かして」と頼むと、-Aで全権限を開いたり、Node.js前提のライブラリ構成になったりしがちです。この記事では、Claude Codeに渡すべき前提、deno.jsonのタスク、Deno.serveを使った小さなAPI、組み込みのformatter/linter/test runnerまでを、コピーして試せる形で整理します。

用語を先にそろえます。パーミッションは「このプログラムにどこまで触らせるか」という許可リストです。deno taskdeno.jsonに書いた名前付きコマンドです。formatterは書式を整える道具、linterはバグになりやすい書き方を検出する道具、test runnerはDeno.test()で書いたテストを実行する道具です。Denoはこれらを標準で持っているため、最初から大量の開発依存を足さなくても小さく始められます。

関連する基礎はClaude Code入門ガイド、API設計はClaude Code API開発、テスト設計はテスト戦略ガイドも合わせて読むと理解しやすいです。

Claude Codeに最初に渡す指示

曖昧な依頼を避け、Denoらしい制約を先に書きます。

Denoで小さなJSON APIを作ってください。Deno.serveを使い、Node.jsのExpressは使わないでください。deno.jsondevstartfmtlinttestcheckタスクを入れてください。実行権限は--allow-net=127.0.0.1:8000--allow-read=./data--allow-write=./dataだけにしてください。テストはDeno.test()で書き、-Aは禁止です。

この指示で、Claude Codeの出力はかなり安定します。ポイントは「使うAPI」「使わないライブラリ」「権限」「検証コマンド」を同時に指定することです。Denoの設定は公式のdeno.json and package.jsondeno taskが基準になります。

deno.jsonでタスクと品質チェックを固定する

まずdeno.jsonです。devstartは必要な権限だけを開き、checkでformat、lint、testをまとめて実行します。

{
  "tasks": {
    "dev": "deno run --watch --allow-net=127.0.0.1:8000 --allow-read=./data --allow-write=./data server.ts",
    "start": "deno run --allow-net=127.0.0.1:8000 --allow-read=./data --allow-write=./data server.ts",
    "fmt": "deno fmt",
    "lint": "deno lint",
    "test": "deno test",
    "check": "deno fmt --check && deno lint && deno test"
  },
  "fmt": {
    "lineWidth": 100,
    "semiColons": true
  },
  "lint": {
    "rules": {
      "tags": ["recommended"]
    }
  },
  "imports": {
    "@std/assert": "jsr:@std/assert"
  }
}

deno fmtdeno lintdeno testはDenoに組み込まれています。個人開発ならこの3つだけでも十分に品質の底上げになります。チームで使う場合は、checkをCIに入れて「フォーマット未適用」「lint違反」「テスト失敗」を同じ入口で止めるのが現実的です。

Deno.serveでコピーして動くAPIを作る

次にAPI本体です。Deno.serveは標準のRequestを受け取り、標準のResponseを返す形でサーバーを書けます。公式リファレンスはDeno.serveです。

// app.ts
export type Item = {
  id: string;
  title: string;
  done: boolean;
};

export interface ItemStore {
  list(): Promise<Item[]>;
  save(items: Item[]): Promise<void>;
}

export class FileItemStore implements ItemStore {
  constructor(private readonly path = "./data/items.json") {}

  async list(): Promise<Item[]> {
    try {
      const text = await Deno.readTextFile(this.path);
      return JSON.parse(text) as Item[];
    } catch (error) {
      if (error instanceof Deno.errors.NotFound) {
        return [];
      }
      throw error;
    }
  }

  async save(items: Item[]): Promise<void> {
    await Deno.writeTextFile(this.path, JSON.stringify(items, null, 2));
  }
}

export function createHandler(store: ItemStore): (request: Request) => Promise<Response> {
  return async (request: Request): Promise<Response> => {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return Response.json({ ok: true });
    }

    if (url.pathname === "/api/items" && request.method === "GET") {
      return Response.json(await store.list());
    }

    if (url.pathname === "/api/items" && request.method === "POST") {
      const body = await request.json().catch(() => null) as { title?: unknown } | null;

      if (!body || typeof body.title !== "string" || body.title.trim() === "") {
        return Response.json({ error: "title is required" }, { status: 400 });
      }

      const items = await store.list();
      const item: Item = {
        id: crypto.randomUUID(),
        title: body.title.trim(),
        done: false
      };

      await store.save([...items, item]);
      return Response.json(item, { status: 201 });
    }

    return new Response("Not Found", { status: 404 });
  };
}
// server.ts
import { createHandler, FileItemStore } from "./app.ts";

Deno.serve(
  { hostname: "127.0.0.1", port: 8000 },
  createHandler(new FileItemStore())
);

実行前に保存先ファイルを作ります。

mkdir -p data
printf "[]\n" > data/items.json
deno task dev

別ターミナルから確認します。

curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/api/items \
  -H "content-type: application/json" \
  -d '{"title":"Deno article draft"}'
curl http://127.0.0.1:8000/api/items

この例では、サーバー起動にネットワーク権限、JSONファイルの読み取りにread権限、追加保存にwrite権限が必要です。--allow-read=./data--allow-write=./dataに絞っているので、誤ってホームディレクトリ全体を読むコードが混ざってもDenoが止めてくれます。

Deno.testで権限なしの単体テストを書く

ファイルI/Oを直接テストすると権限が必要になります。そこで、APIハンドラはItemStoreに依存させ、テストではメモリ実装を差し込みます。これならdeno testだけで動きます。

// app_test.ts
import { assertEquals } from "@std/assert";
import { createHandler, type Item, type ItemStore } from "./app.ts";

class MemoryStore implements ItemStore {
  private items: Item[] = [];

  async list(): Promise<Item[]> {
    return [...this.items];
  }

  async save(items: Item[]): Promise<void> {
    this.items = [...items];
  }
}

Deno.test("GET /health returns ok", async () => {
  const handler = createHandler(new MemoryStore());
  const response = await handler(new Request("http://localhost/health"));

  assertEquals(response.status, 200);
  assertEquals(await response.json(), { ok: true });
});

Deno.test("POST /api/items creates an item", async () => {
  const handler = createHandler(new MemoryStore());
  const response = await handler(
    new Request("http://localhost/api/items", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ title: "Write article" })
    })
  );

  const created = await response.json() as Item;

  assertEquals(response.status, 201);
  assertEquals(created.title, "Write article");
  assertEquals(created.done, false);
});

Deno.test("POST /api/items rejects an empty title", async () => {
  const handler = createHandler(new MemoryStore());
  const response = await handler(
    new Request("http://localhost/api/items", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ title: "" })
    })
  );

  assertEquals(response.status, 400);
});

公式のdeno testにもある通り、Denoのテストは実行時権限と同じ考え方で動きます。テストに不要な権限を与えない設計にしておくと、Claude Codeが後から危ないI/Oを追加したときに気づきやすくなります。

実務で使える4つのユースケース

1つ目は、Expressで作るほどではない社内APIです。管理画面の試作、CSV取り込みの確認、外部SaaSのWebhook受信などは、Deno.serveとJSONファイルだけで十分なことがあります。Claude Codeには「本番DBではなくJSONでよい」「権限はdata配下だけ」と伝えると、検証用として扱いやすいコードになります。

2つ目は、リポジトリ内の小さな自動化スクリプトです。記事一覧の整形、設定ファイルの検査、APIレスポンスのスナップショット保存などは、DenoのTypeScript実行と標準ツールに向いています。Node.jsのpackage.jsonを増やさず、deno task checkだけで品質確認をそろえられるのが利点です。

3つ目は、学習用またはチーム導入用の教材です。Denoは権限エラーがはっきり出るので、「なぜこのスクリプトにネットワーク権限が必要か」「なぜテストには不要か」を説明しやすいです。Claude Codeの出力をレビューする練習にも向いています。

4つ目は、将来Deno Deployやエッジ環境へ寄せる小さなHTTP処理です。最初からWeb標準のRequest/Responseで書いておけば、フレームワーク依存が薄く、移植の判断がしやすくなります。ただしデプロイ先の制約は必ず公式ドキュメントで確認してください。

初心者がはまりやすい落とし穴

最初の落とし穴は、-Aを便利コマンドとして残すことです。-Aはすべての権限を開くため、Denoを選ぶ意味が薄れます。ローカル検証でも、--allow-read=./dataのように対象を絞る習慣をつけた方が安全です。

2つ目は、Node.jsの癖でExpress、Jest、Prettier、ESLintを一度に足してしまうことです。もちろん必要な場合は使えますが、Denoの入門段階ではDeno.servedeno fmtdeno lintDeno.test()を先に試す方が構成を理解できます。

3つ目は、ファイル書き込み時にディレクトリ作成を忘れることです。上の例ではdata/items.jsonを事前に作っています。Claude Codeに「存在しない場合はディレクトリも作る」まで実装させるなら、Deno.mkdirとwrite権限の扱いを明示してください。

4つ目は、テストに本物のファイルやネットワークを使いすぎることです。単体テストはメモリ実装で速く保ち、ファイルI/Oの確認は別の結合テストに分けると、権限も失敗原因も読みやすくなります。

5つ目は、古いサンプルをそのまま貼ることです。Denoの公式ドキュメントは更新されるため、deno.json、タスク、権限、標準ライブラリの参照方法は、実装前に公式ページを確認してください。Claude Codeにも「公式ドキュメントの現在の書き方に合わせて」と明示すると事故が減ります。

Claude Codeにレビューさせるチェックリスト

実装後は、次の観点でClaude Codeにレビューさせます。

  • deno task checkでformat、lint、testが同時に走るか
  • devstart-Aが残っていないか
  • Deno.serveのホストと--allow-netの対象が一致しているか
  • ファイル読み書きの対象が./dataに閉じているか
  • テストが不要な権限なしで動く設計になっているか
  • READMEや記事に、事前準備と確認用curlが書かれているか

この確認はClaude Code common workflowsのような「調査、編集、検証を小さく回す」使い方と相性が良いです。

収益導線と次の学習

Deno記事を読む読者は、単なるランタイム比較より「安全に小さくAI開発を回したい」段階にいることが多いです。まずは無料チートシートでClaude Codeの依頼文と確認コマンドを手元に置き、繰り返し使うなら教材・テンプレートCLAUDE.mdやレビュー用プロンプトを固めるのが近道です。チームで権限設計、CI、レビュー運用まで整える場合はClaude Code導入相談・研修で実リポジトリ前提の相談ができます。

この記事で紹介した内容を実際に試す場合は、最初にmkdir -p dataprintf "[]\n" > data/items.jsonを実行し、deno task devcurlでPOST、deno task checkの順に確認してください。私なら、最初のレビューでは機能追加よりも-Aの排除、権限の範囲、テストの権限不要化を優先します。DenoとClaude Codeの相性は、速く作ることより、危ない操作を小さく閉じ込められる点にあります。

#Claude Code #Deno #TypeScript #ランタイム #セキュリティ
無料

無料PDF: Claude Code はじめてのチートシート

まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。

スパムは送りません。登録情報は厳重に管理します。

Claude Codeを仕事で使える形にしませんか?

無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。

Masa

この記事を書いた人

Masa

Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。

PR

関連書籍・参考図書

この記事のテーマに関連する書籍を楽天ブックスで探せます。

※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。