~/blog/building-restful-apis-nodejs
Published on

Building RESTful APIs with Node.js and Express

849 words5 min read–––
Views
Authors
  • avatar
    Name
    Mohamed Adan
    Twitter

Building RESTful APIs is a fundamental skill for any full-stack developer. In this guide, I'll walk you through creating a robust API using Node.js and Express.js, covering authentication, error handling, and best practices.

Why RESTful APIs?

REST (Representational State Transfer) is an architectural style that provides a standard way to create, read, update, and delete resources over HTTP. RESTful APIs are stateless, cacheable, and follow a uniform interface, making them ideal for modern web applications.

Setting Up Your Project

First, initialize your Node.js project:

mkdir my-api
cd my-api
npm init -y
npm install express mongoose dotenv cors helmet
npm install --save-dev nodemon

Project Structure

Organize your project with a clear structure:

my-api/
├── routes/
│   ├── users.js
│   └── posts.js
├── controllers/
│   ├── userController.js
│   └── postController.js
├── models/
│   ├── User.js
│   └── Post.js
├── middleware/
│   ├── auth.js
│   └── errorHandler.js
├── config/
│   └── database.js
└── server.js

Creating the Express Server

Set up your main server file:

const express = require('express')
const cors = require('cors')
const helmet = require('helmet')
require('dotenv').config()

const app = express()

// Middleware
app.use(helmet())
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// Routes
app.use('/api/users', require('./routes/users'))
app.use('/api/posts', require('./routes/posts'))

// Error handling middleware
app.use(require('./middleware/errorHandler'))

const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

Authentication with JWT

Implement JWT-based authentication:

// middleware/auth.js
const jwt = require('jsonwebtoken')

const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization']
  const token = authHeader && authHeader.split(' ')[1]

  if (!token) {
    return res.status(401).json({ error: 'Access token required' })
  }

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid or expired token' })
    }
    req.user = user
    next()
  })
}

module.exports = { authenticateToken }

Error Handling

Create a centralized error handler:

// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  const statusCode = err.statusCode || 500
  const message = err.message || 'Internal Server Error'

  res.status(statusCode).json({
    success: false,
    error: message,
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
  })
}

module.exports = errorHandler

Best Practices

  1. Use environment variables for sensitive data
  2. Implement proper error handling at all levels
  3. Validate input data before processing
  4. Use HTTPS in production
  5. Implement rate limiting to prevent abuse
  6. Add logging for debugging and monitoring
  7. Document your API with tools like Swagger

Building RESTful APIs requires careful planning and attention to security, performance, and maintainability. By following these patterns, you'll create APIs that are both robust and scalable.