JavaScript was originally built for browsers (frontend). But with Node.js and Express.js, you can run JavaScript on the server (backend) too.
TypeScript is essentially JavaScript with an added type system. This means that you can write your server-side code using TypeScript, ensuring safer code compared to plain JavaScript.
Today, for Day 41, the goal was to understand the advantages of using TypeScript with Node.js and Express.js in the backend and how to use it.
🤨 Why Bother?
Let's look at a common issue with plain JavaScript.
const user = {
name: "David",
age: "25"
};
function getUser(id) {
return id.toUpperCase();
}
getUser(123);
Here, age should be a number and the function getUser must receive a string, but JavaScript won't warn us.
This can cause bugs later in the application, or worse, can even crash the application.
TypeScript Solution
interface User {
name: string
age: number
}
function getUser(id: string) {
return id.toUpperCase();
}
getUser(123); // ❌ Error: Argument of type 'number' is not assignable to 'string'
Now TypeScript will immediately detect incorrect data types.
Benefits of TypeScript
- Prevent runtime errors
- Strong type safety
- Better code readability
- Improved IDE autocomplete
- Safer refactoring
- Easier collaboration in large teams
Because of these advantages, many modern Node.js backends are built with TypeScript.
Setting Up TypeScript in a Node.js Project
To start using TypeScript, install the required dependencies.
npm install typescript ts-node @types/node --save-dev
Initialize the TypeScript configuration:
npx tsc --init
This creates the configuration file:
tsconfig.json
Important tsconfig.json Options
| Option | Purpose |
|---|---|
target |
JavaScript version to compile to |
module |
Module system (CommonJS / ESModules) |
rootDir |
Source code directory |
outDir |
Compiled JavaScript output |
strict |
Enables strict type checking |
Example project structure:
project
├── src
│ └── server.ts
├── dist
├── tsconfig.json
└── package.json
Installing Type Definitions for Express
TypeScript needs type definitions for libraries written in JavaScript.
Install them using:
npm install @types/express
Now you can safely import Express with full type support:
import express from "express";
Converting an Express Server to TypeScript
Rename your server file:
server.js → server.ts
Example Express server written in TypeScript:
import express, { Request, Response } from "express";
const app = express();
app.get("/", (req: Request, res: Response) => {
res.send("Hello from TypeScript backend");
});
app.listen(5000, () => {
console.log("Server running on port 5000");
});
Key Concepts
-
Request→ represents the incoming HTTP request -
Response→ used to send a response - Type imports ensure safer route handling
Creating Interfaces for Data
Interfaces define the structure of objects.
Example:
interface User {
name: string
email: string
age: number
}
Usage:
const user: User = {
name: "Saad",
email: "saad@test.com",
age: 23
};
Why This Matters
- Prevents invalid object structures
- Improves readability
- Helps maintain consistent data models
Typing Request Body in Express
Instead of using type assertions (as), Express allows typing request bodies using generics.
Example login request structure:
interface LoginRequest {
email: string
password: string
}
Now type the request body:
app.post(
"/login",
(req: Request<{}, {}, LoginRequest>, res: Response) => {
console.log(req.body.email);
res.send("Login request received");
});
Request Generic Structure
Request<Params, ResBody, ReqBody, Query>
Example:
Request<{id: string}, {}, LoginRequest, {page: number}>
This allows full type safety for params, body, and query values.
Typing API Responses
It's also helpful to define a consistent response structure.
Example:
interface ApiResponse {
success: boolean
message: string
}
Usage:
const response: ApiResponse = {
success: true,
message: "Login successful"
};
Benefits:
- Consistent API responses
- Easier frontend integration
- Better documentation
TypeScript with Controllers
Controllers can also be typed.
Example controller:
import { Request, Response } from "express";
export const getUsers = (req: Request, res: Response) => {
res.json({
message: "Users list"
});
};
Advantages
- Strongly typed API handlers
- Better maintainability
- Easier debugging
TypeScript with Middleware
Middleware functions can also be typed.
Example authentication middleware:
import { Request, Response, NextFunction } from "express";
export const authMiddleware = (
req: Request,
res: Response,
next: NextFunction
) => {
console.log("Auth middleware executed");
next();
};
Key Concept
NextFunction allows middleware to pass control to the next function in the stack.
Running a TypeScript Backend
Run TypeScript directly using ts-node:
npx ts-node src/server.ts
Or compile TypeScript into JavaScript:
npx tsc
Output example:
dist/server.js
Then run:
node dist/server.js
Key Benefits of Using TypeScript in Backend Development
TypeScript offers several advanced advantages with Node.js and Express.js for backend development.
1. TypeScript Project Setup
- Advanced
tsconfig.json - Using
nodemonwith TypeScript - Scalable folder structure
2. Advanced Express Typing
- Typing route params
- Typing query parameters
- Full generic usage with
Request<>
3. Typing Middleware
- Extending
Request - Adding custom properties like
req.user
4. Mongoose + TypeScript
- Typing schemas
- Typing models
- Handling database documents safely
5. TypeScript Utility Types
Some utility types are extremely useful:
Partial<T>Pick<T>Omit<T>
These help manipulate existing types without rewriting them.
Final Thoughts
Learning TypeScript on the backend significantly improves code reliability and maintainability, especially for large Node.js applications. Even though JavaScript works well, TypeScript helps catch bugs before your code even runs, which is extremely valuable in production systems.
Thanks for reading. Feel free to share your thoughts!
Top comments (0)