Table of Contents
- Introduction
- Prerequisites
- Step 1: Initialize a New Node.js Project
- Step 2: Install Dependencies
- Step 3: Setting Up TypeScript
- Step 4: Setting Up Express
- Step 5: Create Routes and Handlers
- Step 6: Create Models and Controllers
- Step 7: Connect to a Database (Optional)
- Step 8: Set Up TypeScript Compilation
- Step 9: Run the API
- Conclusion
Introduction
Express.js is one of the most popular web frameworks for Node.js, and TypeScript offers static typing, which can improve the development experience and reduce errors. Combining Express with TypeScript allows developers to write maintainable, scalable, and type-safe applications.
In this guide, we’ll walk through building a simple REST API using Express and TypeScript. By the end, you will have a fully working API with basic CRUD (Create, Read, Update, Delete) operations.
Prerequisites
Ensure you have the following tools installed on your system:
- Node.js: You’ll need Node.js to run the backend application.
- npm: The Node Package Manager will be used to install dependencies.
To check if Node.js and npm are installed, run:
node -v
npm -v
If these tools are not installed, download and install Node.js from here.
Step 1: Initialize a New Node.js Project
Start by creating a new directory for your project and initializing it as a Node.js project:
mkdir express-ts-api
cd express-ts-api
npm init -y
This will generate a package.json
file with default settings.
Step 2: Install Dependencies
Now, install the required dependencies for Express and TypeScript:
npm install express
npm install typescript @types/express @types/node --save-dev
express
: The Express web framework.typescript
: TypeScript compiler.@types/express
: Type definitions for Express.@types/node
: Type definitions for Node.js (useful for Node’s built-in modules likefs
,http
, etc.).
Additionally, you can install ts-node and nodemon to run and watch TypeScript files in development:
npm install ts-node nodemon --save-dev
ts-node
: Allows direct execution of TypeScript code.nodemon
: Automatically restarts the server when code changes.
Step 3: Setting Up TypeScript
Next, initialize TypeScript in your project by generating the tsconfig.json
file:
npx tsc --init
The tsconfig.json
file will be created in the root directory. Modify it for a Node.js and Express setup:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
This configuration ensures that TypeScript compiles to modern JavaScript, targets ES6, and puts the output in the dist
directory.
Step 4: Setting Up Express
Create the directory structure for your project:
mkdir src
touch src/index.ts
In src/index.ts
, set up the basic Express application:
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
app.use(express.json()); // Middleware to parse JSON bodies
// Basic route
app.get('/', (req: Request, res: Response) => {
res.send('Hello, TypeScript and Express!');
});
// Start the server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
This code creates a simple Express app that responds to requests at the root (/
) endpoint with a “Hello” message.
Step 5: Create Routes and Handlers
Let’s set up basic routes for the API. Create a routes
folder in the src
directory:
mkdir src/routes
touch src/routes/userRoutes.ts
In userRoutes.ts
, define routes for a simple CRUD API:
import express, { Request, Response } from 'express';
const router = express.Router();
// Sample in-memory store for users
let users: { id: number, name: string }[] = [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
];
// GET /users - Get all users
router.get('/users', (req: Request, res: Response) => {
res.json(users);
});
// GET /users/:id - Get a single user by ID
router.get('/users/:id', (req: Request, res: Response) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (user) {
res.json(user);
} else {
res.status(404).send('User not found');
}
});
// POST /users - Create a new user
router.post('/users', (req: Request, res: Response) => {
const newUser = {
id: users.length + 1,
name: req.body.name
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT /users/:id - Update a user by ID
router.put('/users/:id', (req: Request, res: Response) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (user) {
user.name = req.body.name;
res.json(user);
} else {
res.status(404).send('User not found');
}
});
// DELETE /users/:id - Delete a user by ID
router.delete('/users/:id', (req: Request, res: Response) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index !== -1) {
users.splice(index, 1);
res.status(204).send();
} else {
res.status(404).send('User not found');
}
});
export default router;
Now, import and use the routes in the main index.ts
file:
import express from 'express';
import userRoutes from './routes/userRoutes';
const app = express();
const port = 3000;
app.use(express.json());
app.use('/api', userRoutes); // Prefix all routes with /api
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
Now you have CRUD operations for users available at /api/users
.
Step 6: Create Models and Controllers
In more complex applications, it’s a good practice to separate models and controllers. For simplicity, we’ll continue to keep everything in a single file structure. However, you can create separate folders for models and controllers as your application grows.
Step 7: Connect to a Database (Optional)
To store data persistently, you can integrate a database like MongoDB or PostgreSQL. For MongoDB, you can use Mongoose as an ORM. For PostgreSQL, use Sequelize or TypeORM. Here’s an example of using MongoDB:
- Install MongoDB and Mongoose:
npm install mongoose
- Set up a connection in
index.ts
:
import mongoose from 'mongoose';
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/express-ts-api', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
Step 8: Set Up TypeScript Compilation
To compile your TypeScript code into JavaScript, add a build script in your package.json
:
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "nodemon -x ts-node src/index.ts"
}
This will allow you to run the project in development mode using npm run dev
.
Step 9: Run the API
To run the API, use the following command:
npm run dev
This will start the server using ts-node
and automatically restart it on file changes.
Conclusion
Congratulations! You’ve successfully built a REST API using Express and TypeScript. This setup gives you the benefits of static typing, which reduces runtime errors and improves code maintainability. You can now extend the API by adding more features, handling database interactions, implementing authentication, and much more.