Template8 min read
Node.js Template: Production-Ready Express API
A production-ready Node.js Express API template with OpenClaw deployment, health checks, error handling, and TypeScript.
A production-ready Node.js Express API template with everything you need to ship fast.
What's Included
- Express.js — battle-tested web framework
- TypeScript — catch errors before they happen
- Zod — runtime validation for request/response
- Winston — structured logging
- Health check endpoint — for OpenClaw monitoring
- Graceful shutdown — handle SIGTERM properly
- Error handling — consistent error responses
Quick Start
mkdir my-app && cd my-app
npm init -y
npm install express typescript zod winston
npm install --save-dev @types/express ts-node-dev
Project Structure
my-app/
├── src/
│ ├── index.ts # App entry point
│ ├── routes/
│ │ └── health.ts # Health check route
│ ├── middleware/
│ │ ├── error.ts # Error handler
│ │ └── validate.ts # Zod validation
│ └── types/
│ └── index.ts # Shared types
├── openclaw.json
├── tsconfig.json
└── package.json
src/index.ts
import express from 'express'
import { healthRouter } from './routes/health'
import { errorHandler } from './middleware/error'
import { validate } from './middleware/validate'
import { z } from 'zod'
const app = express()
app.use(express.json())
app.use(healthRouter)
// Example route with validation
app.post('/api/tasks',
validate(z.object({ title: z.string().min(1) })),
async (req, res) => {
const task = await db.tasks.create({ data: req.body })
res.json(task)
}
)
app.use(errorHandler)
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
openclaw.json
{
"name": "my-express-api",
"runtime": "node",
"port": 3000,
"healthCheck": "/health"
}
Deploy with OpenClaw
openclaw deploy
That's it. OpenClaw:
- Detects Node.js + TypeScript
- Runs
npm install - Compiles TypeScript
- Starts with health checks
- Issues SSL certificate
Health Check Endpoint
// src/routes/health.ts
import { Router } from 'express'
export const healthRouter = Router()
healthRouter.get('/health', async (req, res) => {
const dbOk = await checkDatabase()
const redisOk = await checkRedis()
if (!dbOk || !redisOk) {
return res.status(503).json({
status: 'degraded',
checks: { db: dbOk, redis: redisOk }
})
}
res.json({ status: 'ok', uptime: process.uptime() })
})
Error Handling
// src/middleware/error.ts
export function errorHandler(err: Error, req: express.Request, res: express.Response, next: express.NextFunction) {
console.error({
error: err.message,
stack: err.stack,
path: req.path,
method: req.method
})
if (err instanceof ZodError) {
return res.status(400).json({ error: 'Validation failed', details: err.errors })
}
res.status(500).json({ error: 'Internal server error' })
}
Deploy with EZClaw
If your backend supports an AI assistant, pair it with OpenClaw deployed via EZClaw. EZClaw handles the Fly.io provisioning — you keep your Node.js API wherever you like.