Building a RESTful API from Scratch with Node.js (Part 1)
APIs tutorialcreate an APIAPI development guideTutorial

3 min read

In this tutorial, we'll walk through creating a RESTful API using Node.js and Express. We'll build a simple books API that demonstrates core concepts like routing, middleware, error handling, and basic CRUD operations.

Prerequisites

  • Node.js installed on your machine
  • Basic understanding of JavaScript
  • A code editor
  • Postman or similar tool for testing APIs

Project Setup

First, create a new directory and initialize your project:

mkdir books-api
cd books-api
npm init -y

Install the necessary dependencies:

npm install express body-parser dotenv

Project Structure

Create the following file structure:

books-api/
├── src/
│   ├── routes/
│   │   └── books.js
│   ├── middleware/
│   │   └── errorHandler.js
│   └── server.js
├── package.json
└── .env

Basic Server Setup

Create src/server.js:

const express = require('express');
const bodyParser = require('body-parser');
const booksRouter = require('./routes/books');
const errorHandler = require('./middleware/errorHandler');

// Initialize express app
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(bodyParser.json());

// Routes
app.use('/api/books', booksRouter);

// Error handling middleware
app.use(errorHandler);

// Start server
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

Creating Routes

Create src/routes/books.js:

const express = require('express');
const router = express.Router();

// In-memory database
let books = [
    { id: 1, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' },
    { id: 2, title: '1984', author: 'George Orwell' }
];

// GET all books
router.get('/', (req, res) => {
    res.json(books);
});

// GET book by ID
router.get('/:id', (req, res) => {
    const book = books.find(b => b.id === parseInt(req.params.id));
    if (!book) return res.status(404).json({ message: 'Book not found' });
    res.json(book);
});

// POST new book
router.post('/', (req, res) => {
    const book = {
        id: books.length + 1,
        title: req.body.title,
        author: req.body.author
    };
    books.push(book);
    res.status(201).json(book);
});

// PUT update book
router.put('/:id', (req, res) => {
    const book = books.find(b => b.id === parseInt(req.params.id));
    if (!book) return res.status(404).json({ message: 'Book not found' });

    book.title = req.body.title || book.title;
    book.author = req.body.author || book.author;
    
    res.json(book);
});

// DELETE book
router.delete('/:id', (req, res) => {
    const bookIndex = books.findIndex(b => b.id === parseInt(req.params.id));
    if (bookIndex === -1) return res.status(404).json({ message: 'Book not found' });

    books.splice(bookIndex, 1);
    res.status(204).send();
});

module.exports = router;

Error Handling

Create src/middleware/errorHandler.js:

function errorHandler(err, req, res, next) {
    console.error(err.stack);
    
    res.status(500).json({
        message: 'Something went wrong!',
        error: process.env.NODE_ENV === 'development' ? err.message : undefined
    });
}

module.exports = errorHandler;

Input Validation

Let's add basic validation middleware for our POST requests:

const validateBook = (req, res, next) => {
    if (!req.body.title || !req.body.author) {
        return res.status(400).json({
            message: 'Title and author are required'
        });
    }
    next();
};

// Apply to POST route
router.post('/', validateBook, (req, res) => {
    // ... existing post logic
});

Testing the API

Start your server:

node src/server.js

Use these curl commands to test your API:

# Get all books
curl http://localhost:3000/api/books

# Get single book
curl http://localhost:3000/api/books/1

# Create new book
curl -X POST -H "Content-Type: application/json" \
    -d '{"title":"New Book","author":"John Doe"}' \
    http://localhost:3000/api/books

# Update book
curl -X PUT -H "Content-Type: application/json" \
    -d '{"title":"Updated Title"}' \
    http://localhost:3000/api/books/1

# Delete book
curl -X DELETE http://localhost:3000/api/books/1

Next Steps

To enhance this basic API, consider:

  1. Adding a proper database (like MongoDB or PostgreSQL)
  2. Implementing authentication and authorization
  3. Implementing rate limiting
  4. Adding API documentation using Swagger
  5. Setting up automated testing
  6. Adding pagination for GET requests
  7. Implementing proper logging

Conclusion

You now have a working RESTful API built with Node.js and Express! This basic implementation demonstrates core concepts like routing, middleware, and error handling. While this example uses an in-memory array for storage, you can extend it by adding a database and implementing additional features as needed.

Remember to always:

  • Validate input data
  • Handle errors appropriately
  • Follow REST conventions
  • Implement proper security measures
  • Document your API endpoints

Happy coding! 🚀

Related Posts

API Management Tools: A Comprehensive Overview
APIs tutorialcreate an APIAPI development guideTutorial

5 min read

APIs (Application Programming Interfaces) are the backbone of modern digital applications. They allow different software systems to communicate, exchange data, and collaborate seamlessly. As businesse...

API Security: Best Practices for Protecting Your Application
APIs tutorialcreate an APIAPI development guideTutorial

4 min read

In today’s interconnected digital world, APIs (Application Programming Interfaces) are the backbone of communication between different software applications. From mobile apps to cloud services, APIs e...

API Design Best Practices: Crafting Robust and Scalable APIs
APIs tutorialcreate an APIAPI development guideTutorial

5 min read

In the modern digital ecosystem, APIs (Application Programming Interfaces) serve as the backbone of connectivity. Whether you're building microservices, enabling integrations, or crafting data pipelin...