第04章:プロジェクトの最小セットを作る📦
1) この章のゴール🎯
- TypeScriptで動くAPIサーバを1本だけ作る(エンドポイントは2つでOK😊)
- ビルドして起動できる(
npm run build→npm start)✨ - 次章以降(ホットリロード/テスト/Lint/Compose)で育てやすい形にしておく🧩
ここでは Node v24(Active LTS) が中心の世代感で進めます。(nodejs.org) TypeScript は現時点の最新版として 5.9.3 を基準にします。(npmjs.com)
2) まず作る“最小API”の完成イメージ🧠✨
GET /health→{ ok: true }GET /hello?name=komi→{ message: "Hello, komi!" }
フレームワークは Express v5 を使います(すでに正式リリース済み)。(expressjs.com) Express 5 は Node 18+ が要件なので、Node 24なら余裕でOKです👍(expressjs.com)
3) 作業手順(コピペで進めてOK)🛠️✨
Step 0: フォルダ作成 → VS Codeで開く📂
PowerShellでOKです😊
mkdir dx-min-api
cd dx-min-api
code .
Step 1: npmプロジェクトを初期化📦
npm init -y
Step 2: 依存関係を入れる📥
実行時:Express 開発時:TypeScript・Node型・Express型
npm i express
npm i -D typescript @types/node @types/express
ポイント:@types/express は Express v5 でも使います(v4と混ぜないのが大事!)🧯
「express@5 なら @types/express@5 を使う」が基本です。(GitHub)
Step 3: tsconfig.json を作る🧩
まず雛形を作ってから、内容を上書きします✍️
npx tsc --init
次に tsconfig.json をこれに置き換え👇(“後で育てやすい最小”に寄せてます😊)
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
TypeScript 5.9 系の話題(新機能や方向性)は公式リリースノートで追えます👀(typescriptlang.org)
Step 4: package.json の scripts を整える🧰
package.json の scripts をこんな感じにします👇(最低限!)
{
"scripts": {
"build": "tsc -p tsconfig.json",
"start": "node dist/server.js"
}
}
Step 5: ソースを作る(育てやすい2ファイル構成)🌱
src フォルダを作って、2ファイル置きます。
mkdir src
src/app.ts(アプリ本体:テストしやすい形)🧠
import express from "express";
export function createApp() {
const app = express();
app.get("/health", (_req, res) => {
res.json({ ok: true });
});
app.get("/hello", (req, res) => {
const name =
typeof req.query.name === "string" && req.query.name.trim()
? req.query.name.trim()
: "world";
res.json({ message: `Hello, ${name}!` });
});
return app;
}
src/server.ts(起動だけ担当:後でDocker/Composeに乗せやすい)🚀
import { createApp } from "./app.js";
const app = createApp();
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => {
console.log(`✅ Server running: http://localhost:${port}`);
});
./app.jsになってるのは **NodeNext(ESM)運用の“あるある”**対策です🙂 ここが./appのままだと、将来ハマりやすいので先に安全側へ寄せてます🧯
Step 6: ビルドして起動する▶️
npm run build
npm start
ブラウザで確認👀✨
http://localhost:3000/healthhttp://localhost:3000/hello?name=komiyanma
PowerShellでcurlでもOK😄
curl http://localhost:3000/health
curl "http://localhost:3000/hello?name=komi"
4) ここで“あえて”入れないもの🚫(次章で入れるやつ)
この章は土台づくりなので、いったん入れません🙂
- ホットリロード(次のホットリロード編で✨)
- テスト(テスト編で🧪)
- ESLint/Prettier/Biome(Lint/整形編で🧹)
- Docker/Compose(ワンコマンド化編で🧱)
入れない理由はシンプルで、最初から全部盛るとトラブル時に原因が分からなくなるからです😇 まず「動く」を作って、1個ずつ足していくのが最短ルート🏃♂️💨
5) よくある詰まりポイント集💣(先に潰す)
✅ 1) Cannot use import statement outside a module 系
tsconfig.jsonのmodule/moduleResolutionがNodeNextになってるか確認👀src/server.tsの import が./app.jsになってるか確認🔍
✅ 2) EADDRINUSE: address already in use :::3000
- 3000番を誰かが使ってます😅
→
PORT=3001で起動(PowerShell)👇
$env:PORT=3001; npm start
✅ 3) 型が合わない/補完が弱い
@types/nodeと@types/expressが入ってるか確認🧩(npmjs.com)- VS Code を一回リロード(
Ctrl+Shift+P→ “Reload Window”)で直ることも多いです🔄
6) ミニ課題(10〜15分)📝✨
課題A:GET /time を追加⏰
- 返すJSON:
{ now: "2026-02-10T..." }みたいなISO文字列 ヒント:new Date().toISOString()🧠
課題B:name が空文字のときは world にする🌍
- すでにそうしてるけど、意図をコメントで説明してみよう✍️🙂
課題C:/hello に lang=ja が来たら日本語で返す🇯🇵
Hello→こんにちはに切り替え✨- 条件分岐の練習です😄
7) AI拡張での時短ワザ🤖✨(“差分確認”がコツ)
AIに「全部書かせる」より、小さく頼んで、差分を眺めて採用が安全です🧯
使えるお願いテンプレ(そのまま貼ってOK)📌
- 「
src/app.tsに/timeを追加して。既存の書き方に合わせて、余計な依存は増やさないで」 - 「
tsconfig.jsonを NodeNext前提で、初心者がハマらない設定にして。理由も1行ずつコメントで」 - 「Express 5 + TypeScript で query param を安全に扱う最小例を出して(型ガチガチにしすぎない)」
最後にやることはこれ👇
- 変更点(diff)だけ見て「今の段階で理解できる」ものだけ採用✅
- わからない設定は“保留”でOK(後の章で拾える)🙂
8) できたかチェック✅(ここまでの完成条件)
npm run buildが通る✅npm startで起動できる✅/healthが{ ok: true }を返す✅/hello?name=xxxがメッセージを返す✅
次の章からは、この土台に「起動を迷わない形(ワンコマンド)」を被せていきます🧷✨ もし今の時点で「Expressじゃなくて別がいい(Fastifyとか)」みたいな好みがあれば、同じ“最小セット”をその流儀で作り直す版も出せます😄👍