API and API Design

Backend Engineering

API and API Design

Designing REST API → API Endpoint URL

Designing REST API → API Endpoint URL

Designing a RESTful API involves establishing a well-structured and easy-to-understand interface for clients to interact with your system. One of the foundational elements in this design is the construction of API endpoint URLs.

What is an API Endpoint?

An API Endpoint is a specific path through which a resource can be accessed or interacted in a RESTful web service. Each endpoint represents a unique API part mapped to a specific operation or data resource.

1. API Endpoint URL

API endpoint URLs are the addresses through which clients interact with the server's resources. Designing these URLs clearly and consistently is vital for creating a user-friendly and maintainable API.

Principles for Designing Effective API Endpoint URLs

  1. Resource-Based:

    • URLs should represent resources and use nouns, not verbs.

    • Example: /users is better than /getUsers.

  2. Hierarchy and Filtering:

    • Implement hierarchical relationships with nested URLs.

    • Example: /users/:id/orders to list orders for a specific user.

  3. Plural Naming Convention:

    • Use plural nouns for resource names.

    • Example: /books instead of /book.

  4. HTTP Methods:

    • Use standard HTTP methods to indicate the action: GET, POST, PUT, DELETE, PATCH.

    • Example: GET /books to retrieve a list of books, POST /books to create a new book.

  5. Consistent Naming:

    • Maintain consistency in naming conventions throughout the API.

    • Example: Stick to camelCase or snake_case but not both.

  6. Versioning:

    • Include versioning in your URL to manage different iterations of your API.

    • Example: /v1/books/v2/books.

  7. Use of Query Parameters:

    • Use query parameters for filtering, sorting, and pagination.

    • Example: /books?author=JohnDoe&sort=asc&page=2&pageSize=10.

Practical Example

Let's design a RESTful API for a library system with endpoints for books, authors, and genres.

Step 1: Define Resources and Relationships

  1. Books:

    • List all books: GET /books

    • Create a new book: POST /books

    • Retrieve a specific book by ID: GET /books/:id

    • Update a book by ID: PUT /books/:id

    • Delete a book by ID: DELETE /books/:id

  2. Authors:

    • List all authors: GET /authors

    • Create a new author: POST /authors

    • Retrieve a specific author by ID: GET /authors/:id

  3. Genres:

    • List all genres: GET /genres

    • Create a new genre: POST /genres

    • Retrieve a specific genre by name: GET /genres/:name

  4. Book-Author Relationship:

    • List books by a specific author: GET /authors/:id/books

Step 2: Implementing the Design

Using Node.js with Express to implement some endpoints.

  1. Initialize Project

    npm init -y
    npm install express body-parser
    
  2. Basic Server Setup

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

app.use(bodyParser.json());

const books = [
  { id: 1, title: 'Learn RESTful APIs', author: 1, genre: 'technology' }
];
const authors = [
  { id: 1, name: 'John Doe' }
];
const genres = {
  technology: { name: 'Technology' }
};

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

app.post('/books', (req, res) => {
  const newBook = req.body;
  books.push({ id: books.length + 1, ...newBook });
  res.status(201).json(newBook);
});

app.get('/books/:id', (req, res) => {
  const bookId = parseInt(req.params.id);
  const book = books.find(b => b.id === bookId);
  if (book) {
    res.json(book);
  } else {
    res.status(404).send('Book not found');
  }
});

app.put('/books/:id', (req, res) => {
  const bookId = parseInt(req.params.id);
  const updatedBook = req.body;
  let book = books.find(b => b.id === bookId);
  
  if (book) {
    book = { ...book, ...updatedBook };
    books[bookId - 1] = book;
    res.json(book);
  } else {
    res.status(404).send('Book not found');
  }
});

app.delete('/books/:id', (req, res) => {
  const bookId = parseInt(req.params.id);
  const bookIndex = books.findIndex(b => b.id === bookId);

  if (bookIndex > -1) {
    books.splice(bookIndex, 1);
    res.status(204).send(); // No content to confirm deletion
  } else {
    res.status(404).send('Book not found');
  }
});

app.get('/authors', (req, res) => {
  res.json(authors);
});

app.post('/authors', (req, res) => {
  const newAuthor = req.body;
  authors.push({ id: authors.length + 1, ...newAuthor });
  res.status(201).json(newAuthor);
});

app.get('/authors/:id', (req, res) => {
  const authorId = parseInt(req.params.id);
  const author = authors.find(a => a.id === authorId);
  
  if (author) {
    res.json(author);
  } else {
    res.status(404).send('Author not found');
  }
});

app.get('/authors/:id/books', (req, res) => {
  const authorId = parseInt(req.params.id);
  const authorBooks = books.filter(b => b.author === authorId);
  res.json(authorBooks);
});

app.get('/genres', (req, res) => {
  res.json(Object.values(genres));
});

app.post('/genres', (req, res) => {
  const newGenre = req.body.name.toLowerCase();
  if (!genres[newGenre]) {
    genres[newGenre] = { name: req.body.name };
    res.status(201).json(genres[newGenre]);
  } else {
    res.status(409).send('Genre already exists');
  }
});

app.get('/genres/:name', (req, res) => {
  const genreName = req.params.name.toLowerCase();
  const genre = genres[genreName];

  if (genre) {
    res.json(genre);
  } else {
    res.status(404).send('Genre not found');
  }
});

app.listen(port, () => {
  console.log(`Server running at <http://localhost>:${port}`);
});

Designing effective API endpoint URLs is crucial for creating an intuitive and easy-to-use RESTful API. Here are some key takeaways:

  1. Resource-Based URLs: Use nouns to represent resources, not actions.

  2. Hierarchical Structure: Represent relationships with nested URLs.

  3. Plural Naming Convention: Use plural form for resource collections.

  4. HTTP Method Utilization: Assign actions based on the HTTP method. Use HTTP methods to indicate actions on resources rather than embedding actions in URLs.

  5. Consistent Naming: Maintain uniformity in naming conventions.

  6. Versioning: Include version information in the URL to handle updates.

  7. Query Parameters: Use for filtering, sorting, and pagination.

Well-designed API endpoint URLs are essential for creating intuitive and maintainable RESTful APIs. By adhering to best practices such as using nouns for resource identification, maintaining consistency, and using HTTP methods for actions, you can create easy-to-use and understand APIs.

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board