Specify a webhook-driven task for OpenClaw
Describe event inputs, validation, deduplication, and intended actions before integration work.
5,269,871 AI resources indexedWorking toward the world’s largest AI resource index.
How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch/case router — without introducing Express Router or Next.js patterns.
---
name: vanilla-ts-http-route-handler-344c5cf3dfc0
description: How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch/case router — without introducing Express Router or Next.js patterns.
---
# Adding Route Handlers to a Vanilla TypeScript HTTP Server
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**.
---
## Step 0 — Confirm the routing style
Before writing any code, inspect `server/index.ts` (or whichever file starts
the HTTP server). Look for:
```ts
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.
---
## Step 1 — Create the route file
Create `server/routes/<feature>.ts`.
Use **only** the native Node types: `http.IncomingMessage` and
`http.ServerResponse`.
```ts
// 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:
- Export the handler as a **named async function** (`handle<Feature>`).
- Accept exactly `(req: http.IncomingMessage, res: http.ServerResponse)`.
- Return `Promise<void>`.
- Do **not** call `next()` or return a `Response` object.
- Handle errors internally and write an appropriate HTTP status + JSON body.
---
## Step 2 — Import the handler in `server/index.ts`
Add a named import at the top of the file alongside any existing imports:
```ts
import { handleHealth } from './routes/health';
```
---
## Step 3 — Add a `case` entry to the switch block
Locate the existing `switch (pathname)` block and add a new `case`:
```ts
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' }));
}
```
- Match the exact pathname string used by the client.
- Always `break` (or `return`) after calling the handler.
- Keep the `default` case last.
---
## Step 4 — Optional: shared body-reading helper
If multiple routes need to parse a JSON request body, add a small utility
rather than duplicating the logic:
```ts
// 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.
---
## Checklist
- [ ] `server/routes/<feature>.ts` exists and exports a named async handler.
- [ ] Handler signature is `(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>`.
- [ ] Handler always calls `res.writeHead(...)` and `res.end(...)` on every code path.
- [ ] Handler is imported in `server/index.ts`.
- [ ] A matching `case` entry is present in the `switch (pathname)` block.
- [ ] No Express / Next.js / Koa types or patterns were introduced.
---
## Anti-patterns to avoid
| ❌ 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` |---
name: vanilla-ts-http-route-handler-344c5cf3dfc0
description: How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch/case router — without introducing Express Router or Next.js patterns.
---
# Adding Route Handlers to a Vanilla TypeScript HTTP Server
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**.
---
## Step 0 — Confirm the routing style
Before writing any code, inspect `server/index.ts` (or whichever file starts
the HTTP server). Look for:
```ts
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.
---
## Step 1 — Create the route file
Create `server/routes/<feature>.ts`.
Use **only** the native Node types: `http.IncomingMessage` and
`http.ServerResponse`.
```ts
// 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:
- Export the handler as a **named async function** (`handle<Feature>`).
- Accept exactly `(req: http.IncomingMessage, res: http.ServerResponse)`.
- Return `Promise<void>`.
- Do **not** call `next()` or return a `Response` object.
- Handle errors internally and write an appropriate HTTP status + JSON body.
---
## Step 2 — Import the handler in `server/index.ts`
Add a named import at the top of the file alongside any existing imports:
```ts
import { handleHealth } from './routes/health';
```
---
## Step 3 — Add a `case` entry to the switch block
Locate the existing `switch (pathname)` block and add a new `case`:
```ts
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' }));
}
```
- Match the exact pathname string used by the client.
- Always `break` (or `return`) after calling the handler.
- Keep the `default` case last.
---
## Step 4 — Optional: shared body-reading helper
If multiple routes need to parse a JSON request body, add a small utility
rather than duplicating the logic:
```ts
// 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.
---
## Checklist
- [ ] `server/routes/<feature>.ts` exists and exports a named async handler.
- [ ] Handler signature is `(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>`.
- [ ] Handler always calls `res.writeHead(...)` and `res.end(...)` on every code path.
- [ ] Handler is imported in `server/index.ts`.
- [ ] A matching `case` entry is present in the `switch (pathname)` block.
- [ ] No Express / Next.js / Koa types or patterns were introduced.
---
## Anti-patterns to avoid
| ❌ 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` |Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.