BackendTypeScriptExpressREST APINode.jsBackend

Building a REST API with TypeScript and Express: Production Setup

Set up a production-ready REST API with TypeScript and Express — type-safe routes, Zod validation, JWT auth, error handling, and deployment to Render.

Abdur Razzak

Abdur Razzak

Full-Stack Web Developer

June 29, 2024 10 min read

Why TypeScript for Express APIs?

TypeScript transforms the Express development experience. Without TypeScript, req.body is typed as any — you can access any property and only discover typos at runtime. With TypeScript, you define your request body shape and the compiler catches mismatches immediately. Type-safe route handlers, typed Mongoose models, and autocomplete on all objects make your API code dramatically easier to write and refactor safely.

Project Setup: tsconfig and package.json

Install: typescript, ts-node-dev, @types/node, @types/express, express, mongoose, zod, jsonwebtoken, bcryptjs, dotenv. Initialize with npx tsc --init. Key tsconfig.json settings: strict: true, target: 'ES2020', module: 'commonjs', outDir: './dist', rootDir: './src'. Add scripts: 'dev': 'ts-node-dev --respawn src/server.ts', 'build': 'tsc', 'start': 'node dist/server.js'. This gives you hot-reload in development and compiled JavaScript for production.

Type-Safe Request Validation with Zod

Create a validation middleware that takes a Zod schema and validates req.body, req.params, or req.query. If validation fails, return a 400 response with the Zod error messages formatted for the client. In your route handlers, use the validated and typed data — TypeScript now knows the exact shape of the request. Define reusable schemas: createBlogSchema, updateBlogSchema, paginationSchema. Zod's parse() throws on invalid data; safeParse() returns a result object without throwing.

Typed Mongoose Models

Define a TypeScript interface for your document: interface IBlog { title: string; slug: string; content: string; publishedAt?: Date }. Use mongoose.model<IBlog>('Blog', blogSchema) to create a typed model. Now BlogModel.create(), BlogModel.find(), and BlogModel.findById() return properly typed documents. The Mongoose types package (@types/mongoose is built in for Mongoose 6+) provides full TypeScript integration including query types.

Error Handling Middleware

Create a custom AppError class that extends Error, adding statusCode and isOperational properties. In controllers, throw new AppError('Blog not found', 404). Your global error middleware (4-argument Express middleware) catches all thrown errors, formats them into a consistent JSON response, and logs unexpected errors. This pattern separates expected errors (client mistakes) from unexpected errors (bugs) and gives you centralized error logging.

Deploying TypeScript Express to Render

In your Render service configuration, set the Build Command to npm install && npm run build and the Start Command to npm start (which runs node dist/server.js). Render runs the TypeScript compilation during build and serves the compiled JavaScript. Set all environment variables (MONGODB_URI, JWT_SECRET, PORT) in Render's dashboard. Enable Auto-Deploy from your main branch so every push triggers a new deployment automatically.

Share this article

All posts
#TypeScript#Express#REST API#Node.js#Backend
Abdur Razzak — Full Stack Web Developer

Free Consultation

Got a Project Idea? Let's Talk.