» Node.js: Build a REST API with Express » 2. Development » 2.4 Routes

Routes

Modify src/app.ts to add the CRUD routes for the Book:

Add CRUD operations

import express, { Request, Response } from "express";
import sqlite3 from "sqlite3";

import { Book } from "./model/book";

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

// Middleware to parse JSON bodies
app.use(express.json());

// Database connection
let db: sqlite3.Database;

function initDB() {
  db = new sqlite3.Database("./test.db", (err) => {
    if (err) {
      console.error("Error opening database:", err.message);
    } else {
      console.log("Connected to the database.");
      db.exec(
        `CREATE TABLE IF NOT EXISTS books (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            author TEXT NOT NULL,
            published_at TEXT NOT NULL,
            description TEXT NOT NULL,
            isbn TEXT NOT NULL,
            total_pages INTEGER NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
          )`,
        (err) => {
          if (err) {
            console.error("Error opening database:", err.message);
          } else {
            console.log("Successfully initialized tables.");
          }
        }
      );
    }
  });
}

initDB();

app.get("/ping", (req: Request, res: Response) => {
  res.json({ message: "pong" });
});

// GET all books
app.get("/books", (req: Request, res: Response) => {
  db.all("SELECT * FROM books", (err, rows) => {
    if (err) {
      console.error("Error getting books:", err.message);
      res.status(500).json({ error: "Internal Server Error" });
    } else {
      res.json(rows);
    }
  });
});

// GET a single book by ID
app.get("/books/:id", (req: Request, res: Response) => {
  const id = parseInt(req.params.id);
  db.get("SELECT * FROM books WHERE id = ?", [id], (err, row) => {
    if (err) {
      console.error("Error getting book:", err.message);
      res.status(500).json({ error: "Internal Server Error" });
    } else if (row) {
      res.json(row);
    } else {
      res.status(404).json({ message: "Book not found" });
    }
  });
});

// POST a new book
app.post("/books", (req: Request, res: Response) => {
  const newBook: Book = req.body;
  const { title, author, published_at, description, isbn, total_pages } =
    newBook;
  db.run(
    `INSERT INTO books (title, author, published_at, description, isbn, total_pages) VALUES (?, ?, ?, ?, ?, ?)`,
    [title, author, published_at, description, isbn, total_pages],
    function (err) {
      if (err) {
        console.error("Error creating book:", err.message);
        res.status(500).json({ error: "Internal Server Error" });
      } else {
        newBook.id = this.lastID;
        res.status(201).json(newBook);
      }
    }
  );
});

// PUT (update) an existing book by ID
app.put("/books/:id", (req: Request, res: Response) => {
  const id = parseInt(req.params.id);
  const updatedBook: Book = req.body;
  const { title, author, published_at, description, isbn, total_pages } =
    updatedBook;
  db.run(
    `UPDATE books SET title = ?, author = ?, published_at = ?, description = ?, isbn = ?, total_pages = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
    [title, author, published_at, description, isbn, total_pages, id],
    (err) => {
      if (err) {
        console.error("Error updating book:", err.message);
        res.status(500).json({ error: "Internal Server Error" });
      } else {
        res.json(updatedBook);
      }
    }
  );
});

// DELETE a book by ID
app.delete("/books/:id", (req: Request, res: Response) => {
  const id = parseInt(req.params.id);
  db.run("DELETE FROM books WHERE id = ?", [id], (err) => {
    if (err) {
      console.error("Error deleting book:", err.message);
      res.status(500).json({ error: "Internal Server Error" });
    } else {
      res.sendStatus(204); // Send 204 status (No Content)
    }
  });
});

app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

At this point, we use a local SQLite1 database for demo purpose.

sqlite3 is an asynchronous, non-blocking SQLite3 bindings for Node.js.

Install it with this command:

npm i sqlite3

Try with curl

Create a new book:

curl -X POST \
  http://localhost:3000/books \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200
}'

It should respond with this:

{"title":"Sample Book","author":"John Doe","published_at":"2023-01-01","description":"A sample book description","isbn":"1234567890","total_pages":200,"id":1}

Fetch a single book by ID:

curl -X GET http://localhost:3000/books/1

Result:

{
  "id": 1,
  "title": "Sample Book",
  "author": "John Doe",
  "published_at": "2023-01-01",
  "description": "A sample book description",
  "isbn": "1234567890",
  "total_pages": 200,
  "created_at": "2024-02-29 11:39:17",
  "updated_at": "2024-02-29 11:39:17"
}

List all books:

curl -X GET http://localhost:3000/books

Result list:

[
  {
    "id": 1,
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200,
    "created_at": "2024-02-29 11:39:17",
    "updated_at": "2024-02-29 11:39:17"
  },
  {
    "id": 2,
    "title": "Great Book II",
    "author": "Rob Smith",
    "published_at": "2003-01-01",
    "description": "A sample book description",
    "isbn": "1234567880",
    "total_pages": 1200,
    "created_at": "2024-02-29 11:41:23",
    "updated_at": "2024-02-29 11:41:23"
  }
]

Update an existing book

curl -X PUT \
  http://localhost:3000/books/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Updated Book Title",
    "author": "Jane Smith",
    "published_at": "2023-01-01",
    "description": "A new description",
    "isbn": "1234567890",
    "total_pages": 200
}'

Result:

{
  "title": "Updated Book Title",
  "author": "Jane Smith",
  "published_at": "2023-01-01",
  "description": "A new description",
  "isbn": "1234567890",
  "total_pages": 200
}

Delete an existing book:

curl -X DELETE http://localhost:3000/books/1

It returns code 204 for a sucessful deletion.

The REST api server has formed its basic shape now. Not bad!

Footnotes

  1. SQLite: https://www.sqlite.org/index.html