Why Use TypeScript with Node.js and Express?
Combining TypeScript with Node.js and Express brings the benefits of static typing to your backend development. This means better autocompletion, less runtime errors, and more maintainable code, especially in large applications. TypeScript helps you catch errors early in the development process and makes your API's data structures explicit and easy to understand.
Project Setup
Let's start by setting up a new Node.js project.
-
Initialize your project:
mkdir express-ts-api cd express-ts-api npm init -y
-
Install dependencies: We'll need Express for the server and TypeScript as a development dependency.
npm install express npm install typescript ts-node @types/node @types/express --save-dev
-
Initialize TypeScript: Create a
tsconfig.json
file to configure the TypeScript compiler.npx tsc --init
For a good starting configuration, you can use the following in your
tsconfig.json
:{ "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true } }
Creating a Basic Server
Now, let's create a src
directory and an index.ts
file inside it for our server's entry point.
// src/index.ts
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
app.get('/', (req: Request, res: Response) => {
res.send('Hello, TypeScript with Express!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
To run your server, you can add a script to your package.json
:
"scripts": {
"start": "ts-node src/index.ts"
}
Now, you can run npm start
in your terminal and visit http://localhost:3000
in your browser.
Structuring Your API
For a scalable API, it's a good practice to separate your concerns. A common structure is to have separate directories for routes, controllers, and services.
routes
: Define the API endpoints.controllers
: Handle the request and response logic.services
: Contain the business logic.
Example: A Simple User API
Let's create a simple API to manage a list of users.
1. User Controller (src/controllers/userController.ts
)
import { Request, Response } from 'express';
interface User {
id: number;
name: string;
}
let users: User[] = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Doe' },
];
export const getUsers = (req: Request, res: Response) => {
res.json(users);
};
export const createUser = (req: Request, res: Response) => {
const newUser: User = {
id: users.length + 1,
name: req.body.name,
};
users.push(newUser);
res.status(201).json(newUser);
};
2. User Routes (src/routes/userRoutes.ts
)
import { Router } from 'express';
import { getUsers, createUser } from '../controllers/userController';
const router = Router();
router.get('/users', getUsers);
router.post('/users', createUser);
export default router;
3. Update index.ts
to use the router and middleware
We need express.json()
middleware to parse the request body.
// src/index.ts
import express from 'express';
import userRoutes from './routes/userRoutes';
const app = express();
const port = 3000;
app.use(express.json()); // Middleware to parse JSON bodies
app.use('/api', userRoutes);
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
Now you have a structured and type-safe API. You can test your endpoints using a tool like Postman or curl.
This foundation provides a solid starting point for building more complex and robust REST APIs with Node.js, Express, and TypeScript, ensuring your backend code is scalable and maintainable.