API
RESTful
Web Services
Software Development
Integration

Restful API service

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A RESTful API service is a web service that exposes application data as resources and uses standard HTTP semantics to work with them. In practice, that means clients use predictable URLs, verbs such as GET and POST, and self-contained requests instead of RPC-style custom commands.

Model the System as Resources

The first design step is to identify resources, not actions. A bookstore API usually has resources such as books, authors, and orders. Each resource gets a stable URL pattern:

  • 'GET /books returns a collection'
  • 'GET /books/42 returns one book'
  • 'POST /books creates a book'
  • 'PATCH /books/42 updates part of a book'
  • 'DELETE /books/42 removes a book'

That structure matters because it makes the API predictable. A client should be able to guess how to navigate the service without learning a new naming scheme for every feature.

Use HTTP Semantics Correctly

REST is not just "JSON over HTTP." The protocol details carry meaning:

  • 'GET should be safe and read-only'
  • 'POST is commonly used for creation'
  • 'PUT replaces a full resource representation'
  • 'PATCH applies a partial update'
  • 'DELETE removes a resource'

Status codes are part of the contract too. Returning 200 OK for everything throws away useful information. A better service distinguishes between success and failure states:

  • '200 OK for a normal successful read or update'
  • '201 Created after a new resource is created'
  • '204 No Content when deletion succeeds with no body'
  • '400 Bad Request for malformed input'
  • '404 Not Found when the resource does not exist'
  • '409 Conflict when the request breaks a business rule'

Keep Requests Stateless

One of the core REST constraints is statelessness. The server should not depend on hidden conversational state from an earlier request. Every request must include the authentication data, target resource, and body needed to process it.

That design makes scaling easier because any application instance can handle any request. It also simplifies retries: if a network failure happens, the client can repeat the request without needing a special session object stored on the server.

Example Service in Express

The following Node.js example shows a small RESTful service for books:

javascript
1const express = require("express");
2
3const app = express();
4app.use(express.json());
5
6const books = new Map([
7  [1, { id: 1, title: "Distributed Systems", inStock: true }],
8]);
9
10app.get("/books", (req, res) => {
11  res.json([...books.values()]);
12});
13
14app.get("/books/:id", (req, res) => {
15  const book = books.get(Number(req.params.id));
16  if (!book) return res.status(404).json({ error: "Book not found" });
17  res.json(book);
18});
19
20app.post("/books", (req, res) => {
21  const id = books.size + 1;
22  const book = { id, title: req.body.title, inStock: true };
23  books.set(id, book);
24  res.status(201).json(book);
25});
26
27app.patch("/books/:id", (req, res) => {
28  const id = Number(req.params.id);
29  const book = books.get(id);
30  if (!book) return res.status(404).json({ error: "Book not found" });
31
32  const updated = { ...book, ...req.body };
33  books.set(id, updated);
34  res.json(updated);
35});
36
37app.delete("/books/:id", (req, res) => {
38  const deleted = books.delete(Number(req.params.id));
39  if (!deleted) return res.status(404).json({ error: "Book not found" });
40  res.status(204).end();
41});
42
43app.listen(3000);

This is still a simple in-memory example, but it demonstrates the important shape of a RESTful service: resources, verbs, status codes, and JSON representations.

Representations, Validation, and Versioning

Clients do not manipulate database rows directly. They exchange representations, usually JSON documents. That separation gives the service room to validate input, hide internal fields, and evolve storage independently.

Validation is part of that contract. If a client sends a book without a title, the service should reject it explicitly rather than letting bad data leak into persistence.

javascript
1function validateBook(payload) {
2  return typeof payload.title === "string" && payload.title.trim() !== "";
3}
4
5app.post("/books", (req, res) => {
6  if (!validateBook(req.body)) {
7    return res.status(400).json({ error: "title is required" });
8  }
9
10  const id = books.size + 1;
11  const book = { id, title: req.body.title.trim(), inStock: true };
12  books.set(id, book);
13  res.status(201).json(book);
14});

Versioning also belongs at the contract layer. Some teams use /v1/books; others prefer header-based versioning. The key is consistency and a clear deprecation path when a representation changes.

Common Pitfalls

  • Designing endpoints around verbs such as /createBook and /getBook usually means the API is drifting toward RPC instead of resource-oriented REST.
  • Ignoring HTTP status codes forces clients to guess what happened, which makes error handling brittle.
  • Storing hidden session state on the server undermines RESTful scaling and complicates retries.
  • Returning raw database models often leaks fields that should stay internal and makes future schema changes harder.
  • Treating PUT and PATCH as identical creates ambiguity around partial versus full updates.

Summary

  • A RESTful API service exposes resources through predictable URLs and standard HTTP methods.
  • Good REST design uses status codes, stateless requests, and explicit representations as part of the contract.
  • The main design question is usually "what is the resource?" rather than "what action should I name?"
  • Validation, clear error responses, and consistent versioning matter as much as the route shape.

Course illustration
Course illustration

All Rights Reserved.