How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch/case router — without introducing Express R
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-vanilla-ts-http-route-handler-5b1be2ff7166 ,按照其中的说明把「vanilla-ts-http-route-handler」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
Use this skill whenever you need to add a new route to a TypeScript project
that uses Node's built-in http module and a switch/case pathname router
without Express or Next.js.
Before writing any code, inspect server/index.ts (or whichever file starts
the HTTP server). Look for:
http.createServer((req, res) => {
const { pathname } = new URL(req.url!, `http://${req.headers.host}`);
switch (pathname) {
case '/api/foo': ...
}
});
If you see this pattern, follow the steps below.
Do NOT use express.Router, next/server, or any framework-specific
handler signature — even if other files in server/routes/ happen to
use those patterns.
Create server/routes/<feature>.ts.
Use only the native Node types: http.IncomingMessage and
http.ServerResponse.
// server/routes/health.ts
import http from 'http';
export async function handleHealth(
req: http.IncomingMessage,
res: http.ServerResponse
): Promise<void> {
// Parse body for POST/PUT if needed
const body = await readBody(req); // helper shown in Step 3
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
}
Rules for the route file:
handle<Feature>).(req: http.IncomingMessage, res: http.ServerResponse).Promise<void>.next() or return a Response object.server/index.tsAdd a named import at the top of the file alongside any existing imports:
import { handleHealth } from './routes/health';
case entry to the switch blockLocate the existing switch (pathname) block and add a new case:
switch (pathname) {
case '/api/health':
await handleHealth(req, res);
break;
// … existing cases …
default:
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}
break (or return) after calling the handler.default case last.If multiple routes need to parse a JSON request body, add a small utility rather than duplicating the logic:
// server/utils/readBody.ts
import http from 'http';
export function readBody(req: http.IncomingMessage): Promise<unknown> {
return new Promise((resolve, reject) => {
let data = '';
req.on('data', (chunk) => (data += chunk));
req.on('end', () => {
try {
resolve(data ? JSON.parse(data) : {});
} catch {
reject(new Error('Invalid JSON'));
}
});
req.on('error', reject);
});
}
Import and use it inside any route handler that needs it.
server/routes/<feature>.ts exists and exports a named async handler.(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>.res.writeHead(...) and res.end(...) on every code path.server/index.ts.case entry is present in the switch (pathname) block.| ❌ Wrong | ✅ Correct |
|---|---|
import { Router } from 'express' | import http from 'http' |
export default function handler(req: NextApiRequest, ...) | export async function handleFoo(req: http.IncomingMessage, ...) |
router.get('/foo', ...) | case '/api/foo': await handleFoo(req, res); break; |
| Returning a value from the handler | Calling res.end(...) and returning void |