Back to Blog

How Good Engineers Design REST APIs

March 16, 2026
7 min read
By Kevin
How Good Engineers Design REST APIs
TL;DR

Designing a good REST API starts with identifying resources and building predictable endpoints around them. Clean URLs, correct HTTP methods, proper status codes, and consistent responses make APIs easier to understand, maintain, and scale.

What is an API?

In backend engineering, an API (Application Programming Interface) is a way for different software systems to communicate with each other.

For example, when someone clicks a button on a website or mobile app, the frontend sends a request to the backend through an API. The backend processes that request and sends a response back.

As applications grow, having a consistent and well-designed API becomes very important for any serious product or organization.

There are different styles of designing APIs, such as:

  • REST
  • GraphQL
  • gRPC

In this article, we focus on how to design REST APIs properly.


HTTP Methods & Status Codes

Before diving into design, let's review the standard HTTP tools at our disposal.

Common HTTP Methods

MethodDescriptionSuccess Code
GETRetrieve a resource or collection200 OK
POSTCreate a new resource201 Created
PUTUpdate an existing resource (full update)200 OK
PATCHPartially update a resource200 OK
DELETERemove a resource200 OK

Common Status Codes

CodeNameMeaning
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestClient-side error (invalid input)
401UnauthorizedAuthentication required
403ForbiddenAuthenticated but lacks permissions
404Not FoundResource does not exist
500Internal Server ErrorServer-side error

Designing a REST API

The first step when designing an API is to identify the resources in your system.

Resources represent the main entities in your application.

For example, if you are building a blog platform, the main resources might be:

  • users
  • blogs
  • comments

Once the resources are identified, the API should be designed around them.

A common convention is to use plural names for resources.

Good:

/users
/blogs
/comments

Avoid:

/user
/blog
/comment

Example: Designing a Blog API

Typical blog operations include:

  • Create a blog
  • Read a blog
  • Update a blog
  • Delete a blog

These operations map naturally to HTTP methods.


1. Create Blog

POST /blogs
 
{
  "title": "My First Blog",
  "content": "This is the content of my first blog post.",
  "authorId": 1
}

Response body (201 Created):

{
  "data": {
    "id": 1,
    "title": "My First Blog",
    "content": "This is the content of my first blog post.",
    "authorId": 1,
    "createdAt": "2026-03-16T12:00:00Z"
  },
  "message": "Blog created successfully",
  "error": null
}

JSON Naming Convention

JSON fields usually follow a consistent naming convention. Many APIs use camelCase, while others use snake_case.

Example:

authorId

or

author_id

The most important rule is to choose one convention and keep it consistent across the entire API.


Correct Status Codes

When a blog is successfully created, the API should return:

201 Created

Instead of:

200 OK

201 clearly indicates that a new resource has been created.

If a blog already exists with the same title, a suitable response could be:

409 Conflict

Status codes should accurately reflect the situation.


2. Get a Specific Blog

GET /blogs/1

Response body (200 OK):

{
  "data": {
    "id": 1,
    "title": "My First Blog",
    "content": "This is the content of my first blog post.",
    "authorId": 1
  },
  "message": null,
  "error": null
}

REST APIs follow a hierarchical structure.

This can be read as:

In the blogs collection, return the blog with ID 1.

General pattern:

/blogs/{id}

Avoid:

/1/blogs

Avoid Query Parameters for Resource IDs

Avoid designs like:

/blogs?id=1

In REST design, path parameters represent resources.

Better:

/blogs/1

Where:

  • blogs → resource collection
  • 1 → specific resource

3. Get All Blogs

GET /blogs

Response body (200 OK):

{
  "data": [
    { "id": 1, "title": "My First Blog" },
    { "id": 2, "title": "Understanding REST" }
  ],
  "message": null,
  "error": null
}

Filtering can be implemented with query parameters.

Example:

GET /blogs?status=active

The endpoint should still work without filters:

GET /blogs

Filters should always be optional.


Consistent Response Format

To make APIs predictable, responses should follow a consistent structure.

Example:

{
  "data": [],
  "message": "",
  "error": null
}

Consistency makes APIs easier for frontend developers and other services to integrate with.


4. Delete a Blog

DELETE /blogs/1

Response body (200 OK):

{
  "data": null,
  "message": "Blog deleted successfully",
  "error": null
}

Avoid sending the ID in the request body:

{
  "id": 1
}

The resource is already identified in the URL path.


Pagination

In real applications, APIs often return large collections of data. Returning everything at once can be inefficient and slow.

Instead, APIs should support pagination, allowing clients to request data in smaller chunks.

Example:

GET /blogs?page=1&limit=10

This means:

  • page=1 → first page
  • limit=10 → return 10 blogs per request

Example response:

{
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 120
  },
  "message": null,
  "error": null
}

Pagination helps prevent large responses, improves performance, and makes APIs more scalable.


Nested Resources

Resources in real systems often have relationships.

For example:

  • a blog has comments
  • a user has posts

REST APIs commonly represent these relationships through nested resources.

Example:

GET /blogs/1/comments

Similarly, creating a comment:

POST /blogs/1/comments
 
{
  "text": "This is a new comment",
  "authorId": 42
}

This creates a comment under blog 1.

Nested resources help maintain clear relationships between entities.


API Versioning

As APIs evolve, changes may break existing clients such as mobile apps, web applications, or third-party integrations.

To avoid breaking existing systems, APIs should be versioned.

A common approach is including the version in the URL.

Example:

/api/v1/blogs

If a breaking change is introduced later:

/api/v2/blogs

This allows old clients to continue using the previous version while new clients adopt the updated API.

Versioning is essential for long-term API stability.


URL Best Practices

API URLs should be clean and predictable.

Avoid:

  • spaces
  • underscores
  • uppercase letters

Prefer lowercase paths with hyphens:

/blog-posts
/user-comments

Clean URLs improve readability and maintain consistency across the API.


Custom Actions

Sometimes APIs require actions beyond standard CRUD operations.

For example, archiving a blog.

A clean design could be:

POST /blogs/1/archive

Response body:

{
  "data": {
    "id": 1,
    "status": "archived"
  },
  "message": "Blog has been successfully archived",
  "error": null
}

This reads naturally as:

From blogs → find blog 1 → archive it.


Final Thoughts

Designing a good REST API is mostly about clarity and consistency.

A well-designed API acts as a contract between systems, allowing frontend applications, mobile apps, and other services to reliably interact with your backend.

If you follow a few simple principles:

  • identify resources first
  • use proper HTTP methods
  • design clean URLs
  • return meaningful status codes
  • keep responses consistent
  • support pagination and versioning

your APIs will be easier to understand, maintain, and scale.

Ready to level up?

Join hundreds of engineers mastering high-stakes system design through real-world simulations.

Become a Senior Backend Engineer