OllamaのAPIをNode.jsから呼び出す方法|JavaScript・TypeScriptでローカルLLMをLinuxサーバーのWebアプリに組み込む手順

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > ローカルLLM > OllamaのAPIをNode.jsから呼び出す方法|JavaScript・TypeScriptでローカルLLMをLinuxサーバーのWebアプリに組み込む手順
「PythonでOllamaを呼び出す記事は多いが、JavaScriptやTypeScriptからの実装例が見つからない」
「Node.jsで社内向けWebアプリを作っている。バックエンドにOllamaを組み込む具体的な手順が知りたい」

そんな悩みを抱えるLinuxサーバー管理者・Webエンジニアは多いはずです。この記事では、Node.js 18以降で標準搭載されたfetchAPIを使ってOllamaのREST APIを呼び出し、TypeScriptで型安全なクライアントを実装する手順を解説します。
さらにExpress.jsのルートハンドラへの組み込み方と、ストリーミングレスポンスのリアルタイム受信まで、Ubuntu Server上で動作するコードとともに確認します。

この記事のポイント

・Node.js 18以降の標準fetchでOllamaのREST APIを追加パッケージなしで呼び出せる
・TypeScriptの型定義でOllamaのリクエスト・レスポンスを型安全に扱う実装手順
・stream: trueとReadableStreamを組み合わせてLLMのリアルタイム出力を受け取る方法
・Express.jsのPOSTルートにOllamaを組み込み、WebアプリのAPIエンドポイントとして公開する


OllamaのAPIをNode.jsから呼び出す方法|JavaScript・TypeScriptでローカルLLMをLinuxサーバーのWebアプリに組み込む手順

「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

JavaScriptからOllamaのAPIを呼び出す全体像

OllamaはHTTP REST APIを提供しています。
GETリクエストで動作中のモデル一覧を取得でき、POSTリクエストでテキスト生成を呼び出せます。

JavaScriptでOllamaを呼び出すアプローチは2種類あります。

・Node.js 18以降に標準搭載の `fetch` を直接使う(追加パッケージ不要)
・npmパッケージ `ollama` をインストールしてSDKとして使う

この記事では `fetch` を直接使う方法を中心に解説します。標準APIを使うと動作原理が理解でき、後でSDKに切り替える際も迷いません。
既存のTypeScriptプロジェクトへの組み込みにもそのまま応用できます。

OllamaのUbuntu Serverへの導入はすでに完了している前提で進めます。まだの場合は「Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド」を先に確認してください。

Node.js 18とTypeScriptの開発環境をUbuntu Serverに用意する

Ubuntu 24.04 LTSではNode.js 20以上が標準リポジトリから導入できます。
Node.js 18以上であれば標準fetchが使えるため、今回の手順はどちらでも動作します。

1. Node.jsとnpmをインストールする

# sudo apt update && sudo apt install -y nodejs npm # node --version v20.18.0 # npm --version 10.8.2

`v18.0.0` 以上が表示されれば標準fetchが使えます。

2. プロジェクトディレクトリとTypeScript環境をセットアップする

# mkdir ollama-ts && cd ollama-ts # npm init -y # npm install --save-dev typescript tsx @types/node # npx tsc --version Version 5.6.3

`tsx` はTypeScriptファイルをビルドなしに直接実行できるツールです。
開発中の動作確認に使います。

3. TypeScriptの設定ファイルを作成する

`tsconfig.json` を以下の内容で作成します。

{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "outDir": "dist" }, "include": ["src/**/*"] }

`"module": "NodeNext"` とすることで、import/export文をESモジュール形式で使えます。
`mkdir -p src` でソースディレクトリを作成してから次のステップに進みます。

fetchAPIを使ってOllamaに基本リクエストを送る

fetchAPIの基本的な使い方をGETとPOSTの2種類で確認します。
Ollamaが `localhost:11434` で動いていることをあらかじめ確認してから進めます。

1. GETリクエストでモデル一覧を取得する

`src/list-models.ts` を作成します。

const res = await fetch('http://localhost:11434/api/tags'); const data = await res.json() as { models: Array<{ name: string }> }; for (const model of data.models) { console.log(model.name); }

実行します。

# npx tsx src/list-models.ts llama3.3:70b-instruct-q4_0 phi4:14b-q4_0

プル済みのモデル名が1行ずつ表示されれば成功です。

2. POSTリクエストでテキスト生成を呼び出す

`src/basic-generate.ts` を作成します。

const res = await fetch('http://localhost:11434/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.3:70b-instruct-q4_0', prompt: 'Linuxのパーミッション設定を3行で教えてください', stream: false, }), }); const data = await res.json() as { response: string; done: boolean }; console.log(data.response);

実行します。

# npx tsx src/basic-generate.ts Linuxのパーミッションは読み取り(r)・書き込み(w)・実行(x)の3種類で...

`stream: false` を指定すると、生成が完了してからレスポンス全体が返ります。
短いプロンプトでは数秒で返りますが、長い文章の生成では10秒以上かかることがあります。

3. chat APIを使って会話形式のリクエストを送る

テキスト生成APIとは別に、チャット形式のAPIも使えます。

const res = await fetch('http://localhost:11434/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.3:70b-instruct-q4_0', messages: [ { role: 'user', content: 'crontabの書き方を教えてください' }, ], stream: false, }), }); const data = await res.json() as { message: { content: string }; done: boolean }; console.log(data.message.content);

`/api/generate` はプロンプト文字列を送るシンプルな形式で、`/api/chat` は会話履歴を配列で管理できる形式です。
複数ターンの会話を扱う場合は `/api/chat` が適しています。

TypeScriptで型安全なOllamaクライアントを実装する

フェッチの呼び出しをそのまま使い続けると、`as` による型アサーションが各所に散らばります。
型定義と関数を整理して、安全に再利用できる形にします。

社内でローカルLLMを導入する背景については「社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢」が参考になります。

1. APIリクエスト・レスポンスの型を定義する

`src/ollama-types.ts` を作成します。

export interface OllamaChatMessage { role: 'system' | 'user' | 'assistant'; content: string; } export interface OllamaChatRequest { model: string; messages: OllamaChatMessage[]; stream?: boolean; options?: { temperature?: number; num_ctx?: number; }; } export interface OllamaChatResponse { model: string; message: OllamaChatMessage; done: boolean; total_duration?: number; eval_count?: number; }

2. 再利用可能なclient関数を作る

`src/ollama-client.ts` を作成します。

import type { OllamaChatRequest, OllamaChatResponse } from './ollama-types.js'; const BASE_URL = process.env.OLLAMA_HOST ?? 'http://localhost:11434'; export async function chat(request: OllamaChatRequest): Promise { const res = await fetch(`${BASE_URL}/api/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...request, stream: false }), signal: AbortSignal.timeout(120_000), }); if (!res.ok) { throw new Error(`Ollama API error: ${res.status} ${res.statusText}`); } return res.json() as Promise; }

`AbortSignal.timeout(120_000)` で120秒のタイムアウトを設定しています。
LLMのモデルサイズによっては生成に時間がかかるため、適切な値に調整します。
`process.env.OLLAMA_HOST` を参照することで、後から環境変数で向き先を変更できます。

3. 型エラーなしで呼び出せることを確認する

# npx tsc --noEmit # echo "型エラーなし" 型エラーなし

`npx tsc --noEmit` でビルドせずに型チェックだけを実行します。
エラーが出なければ型定義は正しく機能しています。

ストリーミングレスポンスをリアルタイムで受け取る

`stream: false` ではLLMの生成が完了してから全文が返ります。
長文の生成では応答が来るまで画面が固まったように見えます。
`stream: true` にするとトークンが生成されるたびに少量のJSONが返り、リアルタイムに表示できます。

1. stream: trueでリクエストを送る

`src/streaming.ts` を作成します。

const res = await fetch('http://localhost:11434/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.3:70b-instruct-q4_0', messages: [{ role: 'user', content: 'Linuxのcronを3行で説明してください' }], stream: true, }), });

2. ReadableStreamとTextDecoderで分割受信する

Ollamaのストリーミングレスポンスは改行区切りのJSONを順番に送ってきます。

if (!res.body) throw new Error('Response body is null'); const reader = res.body.getReader(); const decoder = new TextDecoder('utf-8'); while (true) { const { done, value } = await reader.read(); if (done) break; const lines = decoder.decode(value, { stream: true }).split('\n'); for (const line of lines) { if (!line.trim()) continue; const chunk = JSON.parse(line) as { message?: { content: string }; done: boolean }; if (chunk.message?.content) { process.stdout.write(chunk.message.content); } } } process.stdout.write('\n');

`decoder.decode(value, { stream: true })` の `stream: true` オプションは、マルチバイト文字(日本語など)が分割されて送られてくる場合に文字化けを防ぎます。

3. ターミナルにリアルタイム出力する

# npx tsx src/streaming.ts Linuxのcronはタスクスケジューラです。 設定ファイルはcrontabコマンドで編集し...

文字がリアルタイムに流れてくれば成功です。
モデルによってトークンの速度は異なりますが、生成の開始は `stream: false` と比べて体感的に速く感じます。

量子化によって速度と精度のバランスが変わるため、モデル選定に迷う場合は「ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント」が参考になります。

Express.jsのAPIルートにOllamaを組み込む

ここまでで実装したTypeScriptクライアントを、Express.jsのWebアプリのバックエンドとして動かします。
社内チャットツールやドキュメント検索ツールのAPIとして利用できます。

1. expressとその型定義をインストールする

# npm install express # npm install --save-dev @types/express

2. /api/chatルートを実装する

`src/server.ts` を作成します。

import express from 'express'; import { chat } from './ollama-client.js'; const app = express(); app.use(express.json()); app.post('/api/chat', async (req, res) => { const { prompt, model } = req.body as { prompt: string; model?: string }; if (!prompt) { res.status(400).json({ error: 'prompt is required' }); return; } try { const result = await chat({ model: model ?? 'llama3.3:70b-instruct-q4_0', messages: [{ role: 'user', content: prompt }], }); res.json({ answer: result.message.content }); } catch (err) { const message = err instanceof Error ? err.message : 'Unknown error'; res.status(500).json({ error: message }); } }); const PORT = Number(process.env.PORT ?? 3000); app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

3. curlでエンドポイントを確認する

別のターミナルでサーバーを起動して確認します。

# npx tsx src/server.ts & Server running on port 3000 # curl -s -X POST http://localhost:3000/api/chat \ -H "Content-Type: application/json" \ -d '{"prompt":"Linuxのcronを1行で説明して"}' | python3 -m json.tool { "answer": "Linux の cron は、指定した時刻にコマンドを自動実行するスケジューラです。" }

`"answer"` フィールドにLLMのレスポンスが入っていれば成功です。
このエンドポイントをフロントエンドから呼び出すことで、チャットUIのバックエンドとして機能します。

環境変数とエラー処理を整備する

開発中は `localhost:11434` で動作しますが、本番では別サーバーのOllamaに向けることがあります。
接続先を環境変数で管理し、fetchの失敗を適切にハンドルします。

1. OllamaのURLを環境変数で管理する

`.env` ファイルを作成します(このファイルは `.gitignore` に追加します)。

# cat .env OLLAMA_HOST=http://10.0.0.100:11434 PORT=3000

`dotenv` パッケージを使うか、起動時に `OLLAMA_HOST=http://... npx tsx src/server.ts` のように環境変数を渡します。
`ollama-client.ts` の `process.env.OLLAMA_HOST ?? 'http://localhost:11434'` がそのまま参照します。

2. fetchの失敗とネットワークエラーを区別する

fetchは次の2種類の失敗を区別して処理する必要があります。

・`fetch()` 自体が例外をスローする(Ollamaに到達できない場合)
・`res.ok` が false になる(Ollamaが4xx/5xxを返す場合)

`ollama-client.ts` の `chat` 関数は両方を `throw new Error(...)` で統一していますが、エラーの種類を分けたい場合は以下のように判別できます。

try { const result = await chat({ model: 'llama3.3:70b-instruct-q4_0', messages }); return result; } catch (err) { if (err instanceof TypeError) { // ネットワーク到達不能(Ollamaがダウンしているなどのケースをここで処理する) console.error('Ollama に接続できません:', err.message); } else { // HTTPエラー(401, 404, 500 など) console.error('Ollama APIエラー:', err instanceof Error ? err.message : err); } throw err; }

`fetch()` がネットワーク接続に失敗した場合は `TypeError` がスローされます。
Ollamaが応答しているが4xxや5xxを返した場合は `Error` がスローされます。

よくあるエラーと対処法

「TypeError: fetch failed」になる

Ollamaが起動していないか、指定したURLに到達できない場合に発生します。

# systemctl status ollama # curl http://localhost:11434/api/tags

サーバー自身からcurlでOllamaに届くことを確認してからコードを疑います。
別サーバーのOllamaに向けている場合は、Ollamaのバインドアドレスがデフォルトのループバックのままになっていないか確認します。

「JSON.parse: Unexpected token」になる

ストリーミング処理で、1回の `reader.read()` に複数のJSON行が含まれることがあります。
`.split('\n')` で行ごとに分割してから `JSON.parse` するようにします。空行はスキップします。

TypeScriptのimport文で「.js拡張子が必要」というエラーが出る

`"module": "NodeNext"` を設定したプロジェクトでは、TypeScriptファイル間のimportでも `.js` 拡張子が必要です。

// 誤り import { chat } from './ollama-client'; // 正しい import { chat } from './ollama-client.js';

これはNode.jsのESモジュール仕様の要件です。`tsconfig.json` を `"module": "CommonJS"` に変えると `.js` 拡張子は不要になりますが、将来的なESモジュール移行を考えると `NodeNext` を推奨します。

生成が途中でタイムアウトする

70bクラスのモデルは1回の生成に数十秒かかることがあります。
`ollama-client.ts` の `AbortSignal.timeout(120_000)` を `300_000`(5分)程度に延ばして試します。
Expressの `res.timeout` も合わせて延ばします。

まとめ

この記事ではNode.js標準のfetchAPIを使ってOllamaのREST APIをTypeScriptから呼び出し、Express.jsのWebアプリに組み込む手順を解説しました。
Pythonが使えない環境や、既存のNode.jsプロジェクトにローカルLLMを追加したい場面でそのまま応用できます。
20年以上サーバー管理に携わった経験からも、言語やフレームワークに依存しないHTTP REST APIの理解が、どのスタックへの組み込みにも共通する基礎になります。

実装ステップ 主なコマンド・ファイル 目的
環境構築 npm install --save-dev typescript tsx @types/node TypeScriptをビルドなしで実行できる環境を整える
基本リクエスト fetch('http://localhost:11434/api/chat', { method: 'POST', ... }) 依存ゼロでOllamaのREST APIを呼び出す
型安全化 npx tsc --noEmit TypeScriptの型チェックでリクエスト・レスポンスを守る
ストリーミング res.body.getReader() + TextDecoder 生成されるトークンをリアルタイムに受け取る
Webアプリ組み込み npm install express && npx tsx src/server.ts OllamaをExpress.jsのPOSTルートとして公開する

OllamaのAPI活用からWebアプリ連携まで2日間のハンズオンで体験する

ローカルLLMの導入からREST API・チームWebアプリへの組み込みまで、実機GPU環境で手を動かして学びたい方向けに、「ローカルAIマスターセミナー」を開催しています。
少人数(最大8名)ZOOMハンズオン形式で実施しています。

>> ローカルAIマスターセミナーの詳細を確認する
ローカルLLMの構築・運用に関する関連記事もあわせて参考にしてください。

・Ubuntu ServerでローカルLLMを構築する方法|Ollamaで機密データを外に出さず業務AIを動かす完全ガイド
・社内でChatGPTが使えないときの代替手段|機密データを守るローカルLLMという選択肢
・ローカルLLMのモデルを比較する方法|Llama3.3・Mistral・Gemma・Phi-4をUbuntuで使い分けるポイント

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。