How to build a REST API with Express in Node.js
Building RESTful APIs is fundamental for modern web applications, providing a standardized way for clients to communicate with servers. As the creator of CoreUI with over 12 years of Node.js experience since 2014, I’ve architected numerous REST APIs serving millions of requests. Express is the most popular Node.js framework for building APIs, offering routing, middleware, and request/response handling. This approach follows REST conventions for creating scalable, maintainable backend services.
Use Express to build a REST API with proper routing, middleware, and CRUD operations.
npm install express
Create a basic REST API:
const express = require('express')
const app = express()
// Middleware
app.use(express.json()) // Parse JSON bodies
app.use(express.urlencoded({ extended: true }))
// In-memory data store (use database in production)
let users = [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' }
]
let nextId = 3
// GET - List all users
app.get('/api/users', (req, res) => {
res.json({
success: true,
data: users,
total: users.length
})
})
// GET - Get user by ID
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id))
if (!user) {
return res.status(404).json({
success: false,
error: 'User not found'
})
}
res.json({
success: true,
data: user
})
})
// POST - Create new user
app.post('/api/users', (req, res) => {
const { name, email } = req.body
if (!name || !email) {
return res.status(400).json({
success: false,
error: 'Name and email are required'
})
}
const newUser = {
id: nextId++,
name,
email
}
users.push(newUser)
res.status(201).json({
success: true,
data: newUser
})
})
// PUT - Update user
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id))
if (!user) {
return res.status(404).json({
success: false,
error: 'User not found'
})
}
const { name, email } = req.body
if (name) user.name = name
if (email) user.email = email
res.json({
success: true,
data: user
})
})
// DELETE - Delete user
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id))
if (index === -1) {
return res.status(404).json({
success: false,
error: 'User not found'
})
}
users.splice(index, 1)
res.json({
success: true,
message: 'User deleted successfully'
})
})
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack)
res.status(500).json({
success: false,
error: 'Something went wrong'
})
})
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
error: 'Route not found'
})
})
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`API server running on port ${PORT}`)
})
With router separation:
// routes/users.js
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => { /* list users */ })
router.get('/:id', (req, res) => { /* get user */ })
router.post('/', (req, res) => { /* create user */ })
router.put('/:id', (req, res) => { /* update user */ })
router.delete('/:id', (req, res) => { /* delete user */ })
module.exports = router
// app.js
const userRoutes = require('./routes/users')
app.use('/api/users', userRoutes)
Best Practice Note
Follow REST conventions: GET for retrieval, POST for creation, PUT/PATCH for updates, DELETE for removal. Always validate input and return appropriate HTTP status codes (200, 201, 400, 404, 500). Use express.json() middleware to parse JSON request bodies. Implement error handling middleware to catch and format errors consistently. Separate routes into modules for better organization. Add CORS middleware for cross-origin requests. This is the foundation we use for CoreUI backend APIs—Express with proper routing, validation, error handling, and RESTful design patterns.



