What you'll build
This is Part 1 of a series where you build a Product Catalog API — a backend service that any frontend (web, mobile, or another service) can call to list products, manage inventory, handle user accounts, and process Paystack payments.
By the end of the series you'll have:
- A fully typed Express API with Zod request validation
- A Supabase database with products, categories, and users tables
- JWT-based authentication (register, login, protected routes)
- A Paystack webhook handler that updates order status on payment
- A live deployment on Railway
By the end of Part 1 you'll have a running Express server with proper TypeScript setup, structured error handling, and a tested health check endpoint.
Prerequisites
- Node.js 20+ installed
- VS Code installed
- BlocWeave installed
- Basic familiarity with JavaScript (you don't need to know TypeScript — BlocWeave will write it)
Create the project
mkdir product-catalog-api && cd product-catalog-api
npm init -y
npm install express zod dotenv cors helmet
npm install -D typescript ts-node @types/node @types/express @types/cors nodemon
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["src"]
}
Open the project in VS Code:
code .
Tell BlocWeave about the project
Create BLOCWEAVE_PLAN.md:
# Product Catalog API
## Stack
- Node.js 20 + Express + TypeScript
- Supabase (PostgreSQL) for data
- Zod for request validation
- JWT for auth (jsonwebtoken)
- Paystack for payments
- Railway for hosting
## Conventions
- All source in src/
- Routes in src/routes/<name>.router.ts
- Controllers in src/controllers/<name>.controller.ts
- Middleware in src/middleware/
- Shared types in src/types.ts
- Error responses always use { error: string } shape
- Success responses always use { data: T } shape (or { data: T, meta: { total, page, limit } } for lists)
- Env vars: DATABASE_URL, SUPABASE_URL, SUPABASE_SERVICE_KEY, JWT_SECRET, PAYSTACK_SECRET_KEY
Build the server foundation
Set up the Express server foundation for the product catalog API. 1. src/index.ts — creates the Express app, registers middleware (helmet, cors, express.json()), mounts all routers (just /health for now), starts listening on process.env.PORT ?? 3000, and exports the app for testing. 2. src/middleware/errorHandler.ts — an Express error-handling middleware (4-argument signature). Logs the error. If the error has a statusCode property, use it; otherwise 500. Always responds with { error: error.message }. 3. src/middleware/validate.ts — a middleware factory: validate(schema: ZodSchema) returns an Express middleware that runs schema.safeParse(req.body); if success sets req.body to the parsed data; if fail returns 400 with { error: "Validation failed", details: zodError.errors }. 4. src/routes/health.router.ts — GET /health returns { status: "ok", timestamp: new Date().toISOString(), version: process.env.npm_package_version ?? "0.0.0" }.
Add dev scripts
Update package.json scripts:
{
"scripts": {
"dev": "nodemon --exec ts-node src/index.ts --ext ts",
"build": "tsc",
"start": "node dist/index.js",
"type-check": "tsc --noEmit"
}
}
Start the server
npm run dev
Test the health endpoint:
curl http://localhost:3000/health
You should see:
{ "status": "ok", "timestamp": "2026-06-14T10:00:00.000Z", "version": "1.0.0" }
Run the TypeScript type checker to confirm no errors:
npm run type-check
Create the .env file
PORT=3000
JWT_SECRET=change-this-to-a-long-random-string
SUPABASE_URL=
SUPABASE_SERVICE_KEY=
PAYSTACK_SECRET_KEY=
echo ".env" >> .gitignore
Project structure after Part 1
src/
middleware/
errorHandler.ts
validate.ts
routes/
health.router.ts
index.ts
BLOCWEAVE_PLAN.md
tsconfig.json
package.json
.env
Why Zod? Zod validates that incoming request data matches the shape you expect before it reaches your controller. Without it, a missing field or wrong type can cause a runtime error deep in your code that's hard to debug. Zod catches it at the boundary with a clear error message.
What's next
In Part 2 we connect Supabase, create the products and categories tables, and build the full set of CRUD endpoints — list, get by ID, create, update, and delete.
BlocWeave