Express is still the go-to framework for Node.js APIs. It’s minimal, flexible, and has a massive ecosystem. Here’s how to build a production-ready REST API from scratch.
Project Setup
mkdir my-api && cd my-api
npm init -y
npm install express cors helmet morgan dotenv
npm install -D nodemon
Basic Server
// server.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const app = express();
// Middleware
app.use(helmet()); // Security headers
app.use(cors());
app.use(morgan('dev')); // Logging
app.use(express.json());
// Routes
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date() });
});
// Error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Organizing Routes
// routes/users.js
const express = require('express');
const router = express.Router();
const users = [];
router.get('/', (req, res) => {
res.json(users);
});
router.get('/:id', (req, res) => {
const user = users.find(u => u.id === req.params.id);
if (!user) return res.status(404).json({ error: 'Not found' });
res.json(user);
});
router.post('/', (req, res) => {
const user = { id: Date.now().toString(), ...req.body };
users.push(user);
res.status(201).json(user);
});
module.exports = router;
// server.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);
Input Validation
Never trust user input. Use express-validator:
const { body, validationResult } = require('express-validator');
router.post('/',
body('email').isEmail(),
body('name').notEmpty().trim(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Create user...
}
);
Async Error Handling
// Wrapper for async routes
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
router.get('/:id', asyncHandler(async (req, res) => {
const user = await db.findUser(req.params.id);
if (!user) throw new NotFoundError('User not found');
res.json(user);
}));
References
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.