Marcell CD

Good API Design

Designing an API can be complicated, but it doesn’t have to be! An API is simply a way for different software to communicate. Whether you’re building a web app or a mobile app, a well-designed API will make it much easier to work with and understand.

Let’s discuss some important concepts for creating APIs that are user-friendly and easy to maintain.

What Makes a Good API?

It’s important to define and to know what makes a good API, there are different preference but I think most developers can at least come agree with some basic rules of what the definition of a good API design really is. A good API follows some principles:

1. Use Clear Naming

When developing your application, it’s essential to name your endpoints and data fields in a manner that is easily comprehensible to all users, regardless of their technical background. Opt for simple, everyday language that conveys the purpose of each endpoint clearly. This approach not only enhances readability but also promotes better understanding and collaboration among team members. It is crucial to steer clear of confusing abbreviations that may lead to misinterpretation or frustration.

Example: For instance, instead of using a less intuitive name like /usrInfo, which may not be immediately clear to some users, consider using a more straightforward and descriptive alternative such as /users. This makes it instantly recognizable and understandable, ensuring that everyone is on the same page.

2. Stick to RESTful Patterns

REST is a style of API that relies on simple rules. You use different HTTP methods depending on what you want to do:

Example Routes: There are, of course, different opinions on whether we should use singular or plural naming conventions for the endpoints. For simplicity, I choose plural because that is what I have seen most often when working with good APIs. I don’t mean to suggest that singular is bad, but it’s important to be consistent with either singular or plural.

Practical Examples

Here’s how you might create a user API in Node.js using TypeScript (with Express):

import express from "express"
const app = express()
app.use(express.json())

// Pretend database
let users = [
  {id: 1, name: "Alice"},
  {id: 2, name: "Bob"},
]

// Get all users
app.get("/users", (req, res) => {
  res.json(users)
})

// Create new user
app.post("/users", (req, res) => {
  const newUser = {id: users.length + 1, name: req.body.name}
  users.push(newUser)
  res.status(201).json(newUser)
})

// Get user by ID
app.get("/users/:id", (req, res) => {
  const user = users.find((u) => u.id === Number(req.params.id))
  if (!user) {
    return res.status(404).json({error: "User not found"})
  }
  res.json(user)
})

// Update user
app.put("/users/:id", (req, res) => {
  const user = users.find((u) => u.id === Number(req.params.id))
  if (!user) {
    return res.status(404).json({error: "User not found"})
  }
  user.name = req.body.name
  res.json(user)
})

// Delete user
app.delete("/users/:id", (req, res) => {
  const idx = users.findIndex((u) => u.id === Number(req.params.id))
  if (idx === -1) {
    return res.status(404).json({error: "User not found"})
  }
  const deletedUser = users.splice(idx, 1)
  res.json(deletedUser[0])
})

app.listen(3000, () => {
  console.log("API listening on port 3000")
})

3. Handle Errors Gracefully

Effective error handling plays a pivotal role in the creation of a well-designed API. It is essential to ensure that when an issue arises, the response provided is not only straightforward but also informative. Clear and helpful messages significantly enhance the experience for the API consumer, allowing them to understand what went wrong and how to address it.

Rather than simply responding with a vague “Error,” it is important to convey specific details about the situation. This includes providing a meaningful status code that accurately reflects the nature of the problem. Such clarity aids developers in troubleshooting and improves the overall usability of the API.

Example:

if (!user) {
  return res.status(404).json({error: "User not found"})
}

In this example, the API does not merely indicate that there is an error; it explicitly informs the user that the requested resource, in this case, a user, could not be located. This approach not only enhances communication but also fosters trust and reliability in the API’s functionality.

4. Document Everything

Documentation is challenging. Making the text easy to read and understand is not always simple. Maintaining documentation is even harder. However, having good documentation is one of the most valuable and appreciated resources for developers.
Write clear instructions on how to use your API. Explain each endpoint, what data to send, and what to expect in return. A tool like Swagger can help.

In Summary

Designing a good and clean API is a challenging endeavor that requires careful consideration and attention to detail. However, it is crucial to prioritize ease of understanding and usability for the consumers who will interact with it. To achieve this, it is essential to maintain clarity in naming conventions, employ straightforward patterns, handle errors gracefully, and thoroughly document every aspect of your work. By laying a solid foundation from the beginning, anyone, regardless of their experience level, can create an API that is not only functional but also a joy to use. Focusing on these key elements will ensure that your API stands out and provides a positive experience for its users.

Useful Articles

Microsoft Azure’s API Design Best Practices covers RESTful concepts, including resource URI naming conventions (use nouns, plural for collections), implementing pagination and filtering with query parameters, and organizing hierarchical relationships between resources.

Swagger’s Best Practices in API Design emphasizes that effective APIs should be easy to learn, difficult to misuse, and comprehensive. It discusses resource modeling, URL structure, error handling with appropriate response codes, and using parameters effectively to manage relationships and limit data exposure.

API Architecture - Design Best Practices for REST APIs by Wahab provides practical guidance on consistent HTTP status code usage (200 for GET/PUT/PATCH, 201 for POST, 204 for DELETE), avoiding nested resources in favor of query parameters, and treating API design as an art that improves with practice. [^3]

Stack Overflow’s Best Practices for REST API Design covers essential topics, including accepting and responding with JSON, using nouns instead of verbs in endpoints, handling errors gracefully, implementing filtering, sorting, pagination, maintaining security practices, caching for performance, and API versioning.

Ambassador’s 7 REST API Best Practices focuses on managing requests and responses effectively with appropriate status codes, using proper authentication headers (Authorization, API keys, custom headers), understanding when to use path versus query parameters, and providing informative error messages with try-catch blocks.