Build the Express Server and API Routing
Create the Express application, configure middleware, implement API routing, and understand request processing.
Create the Express Application
Create src/app.js.
This file will initialize the Express application and register global middleware and routes.
We will keep app initialization separate from server startup. This makes the application easier to test using Supertest without opening a network port.
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
const app = express();
app.use(helmet());
app.use(cors({
origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173'
}));
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({
extended: true,
limit: '100kb'
}));
app.get('/health', (req, res) => {
res.status(200).json({
success: true,
message: 'API is running'
});
});
export default app;Creates an Express application, registers security headers, CORS, body parsing middleware, and a health endpoint.
Express applications are initialized using express().
Middleware processes requests before route handlers.
Separate application initialization from server startup.
Understand Express Middleware
Middleware functions are functions that execute during the request-response lifecycle.
A middleware function receives three important arguments:
(req, res, next)
- req: Contains incoming request information.
- res: Provides methods for sending the response.
- next: Passes control to the next middleware.
Middleware can:
- Read request information.
- Modify request or response objects.
- Perform authentication.
- Validate input.
- Log requests.
- Handle errors.
- Terminate requests by sending responses.
If middleware neither sends a response nor calls next(), the request may remain pending.
Middleware order matters because Express executes middleware in registration order.
function requestLogger(req, res, next) {
console.log({
method: req.method,
path: req.originalUrl,
timestamp: new Date().toISOString()
});
next();
}
app.use(requestLogger);Logs incoming request information and passes execution to the next middleware.
Middleware executes during the request-response lifecycle.
next() passes execution to the next middleware.
Middleware order determines execution order.
Create the Main API Router
Create src/routes/index.js.
Express Router allows you to organize endpoints into independent modules.
For example, user-related endpoints can live in user.routes.js, while authentication endpoints can live in auth.routes.js.
The main router combines these route modules under a common API prefix.
import { Router } from 'express';
import userRoutes from './user.routes.js';
import authRoutes from './auth.routes.js';
const router = Router();
router.use('/users', userRoutes);
router.use('/auth', authRoutes);
export default router;Combines user and authentication routes into a single router.
Express Router organizes API endpoints.
Separate routes by application resource.
A main router provides a central routing entry point.
Mount API Routes in Express
Update src/app.js to register the main API router.
We will expose all API endpoints under /api/v1.
Versioning helps introduce future API changes without immediately breaking existing clients.
import apiRoutes from './routes/index.js';
app.use('/api/v1', apiRoutes);Registers all API endpoints under the /api/v1 prefix.
Version API endpoints using a consistent prefix.
Centralize route registration.
Separate API versions when breaking changes are introduced.
Create the Server Entry Point
Create src/server.js.
This file will be responsible for:
- Loading environment variables.
- Connecting to MongoDB.
- Importing the Express application.
- Starting the HTTP server.
The database connection must complete before the server begins accepting requests that depend on MongoDB.
import 'dotenv/config';
import app from './app.js';
import { connectDatabase } from './config/database.js';
const PORT = process.env.PORT || 3000;
async function startServer() {
await connectDatabase();
app.listen(PORT, () => {
console.log(`REST API running at http://localhost:${PORT}`);
});
}
startServer().catch((error) => {
console.error('Application startup failed:', error);
process.exit(1);
});Loads configuration, connects to MongoDB, and starts the Express application only after the database connection succeeds.
The server entry point controls application startup.
Connect to the database before accepting requests.
Separate app.js from server.js for easier testing.
Test the Initial Express Server
Start the application:
npm run dev
Open your browser or API client and send a GET request:
GET http://localhost:3000/health
Expected response:
{
"success": true,
"message": "API is running"
}
If you receive the expected response, the Express application is running.
You should also see a successful MongoDB connection message in the terminal.
npm run devStarts the backend using the development script.
Verify the server using the health endpoint.
Confirm that MongoDB connects successfully.
Check terminal logs when startup fails.