2026-04-15

Node.js で DeepSeek V4 を使う:TypeScript / ストリーミング / tool use

DeepSeek V4 は OpenAI 互換 API なので、Node.js からは openai パッケージをそのまま流用できます。本記事では TypeScript で、基本呼び出しからストリーミング、tool use、Next.js の route handler まで、本番で使える構成を順に解説します。

1. インストールと環境構築

openai 公式 npm パッケージが baseURL 差し替えで DeepSeek に対応します。API キーは .env に置き、Git にも commit せず、絶対にブラウザ側へ渡さないこと。

npm install openai zod
echo "DEEPSEEK_API_KEY=sk-..." >> .env.local

2. TypeScript での最初の呼び出し

クライアントのインスタンス化は 1 回だけ。baseURL を DeepSeek のエンドポイントに向け、model は deepseek-chat を使います。

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function ask(question: string): Promise<string> {
  const res = await client.chat.completions.create({
    model: "deepseek-chat",
    messages: [
      { role: "system", content: "You are a concise technical assistant." },
      { role: "user", content: question },
    ],
    temperature: 0.2,
  });
  return res.choices[0].message.content ?? "";
}

3. Next.js route handler でのストリーミング

ブラウザに tokens を逐次表示したい場合、Next.js の route handler の中で OpenAI SDK が返す async iterable を ReadableStream に変換し、そのまま返すのが最短経路です。

// app/api/chat/route.ts
import OpenAI from "openai";

export const runtime = "nodejs";

const client = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY,
  baseURL: "https://api.deepseek.com/v1",
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = await client.chat.completions.create({
    model: "deepseek-chat",
    messages,
    stream: true,
  });

  const encoder = new TextEncoder();
  const body = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const delta = chunk.choices[0]?.delta?.content ?? "";
        if (delta) controller.enqueue(encoder.encode(delta));
      }
      controller.close();
    },
  });

  return new Response(body, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

4. Zod で tool use の引数を検証

V4 の tool use は本番エージェントに乗せられる水準で安定しています。ただしモデルは稀に引数を壊すので、Zod スキーマで検証してから実行しましょう。

import { z } from "zod";

const GetWeatherArgs = z.object({ city: z.string().min(1) });

const tools = [{
  type: "function" as const,
  function: {
    name: "get_weather",
    description: "Get the current weather for a city",
    parameters: {
      type: "object",
      properties: { city: { type: "string" } },
      required: ["city"],
    },
  },
}];

const res = await client.chat.completions.create({
  model: "deepseek-chat",
  messages: [{ role: "user", content: "Weather in Tokyo?" }],
  tools,
});

const call = res.choices[0].message.tool_calls?.[0];
if (call?.function.name === "get_weather") {
  const args = GetWeatherArgs.parse(JSON.parse(call.function.arguments));
  // … execute tool and feed result back as a { role: "tool" } message …
}

5. Node.js 側でのコスト管理

入力 / 出力 tokens が別課金で、出力は約 2 倍。system prompt と履歴はすべて入力 tokens に入るので、制御しないと長い会話のコストが一気に跳ね上がります。

maxTokens を必ず指定、古いターンは要約、RAG のチャンクはキャッシュ、が定番の三点セット。トラフィックが多いチームは /pricing の割引キーで単価を追加で 20〜40% 下げられます。

FAQ

ブラウザから直接呼べますか?

呼べません。API キー漏洩のリスクがあるため、必ず Node.js サーバーやサーバーレス、Next.js route handler 越しに呼んでください。

Bun / Deno / Cloudflare Workers でも動く?

動きます。openai SDK は ESM ビルドを配っており、fetch があれば大半のランタイムで動作します。ストリーミングを長く保つなら Node.js が無難です。

LangChain / Vercel AI SDK と組み合わせられる?

OpenAI 互換 baseURL を指定できるライブラリはそのまま DeepSeek に向けられます。

割引 API キーは?

/pricing に公式と同じ仕様の割引キーを掲載しています。

Node.js / Next.js のプロダクトなら、DeepSeek V4 への移行はほぼ baseURL と model の差し替えだけ。既存のプロンプト、ツール、リトライロジックを維持したまま、tokens コストを一桁落とせます。