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
| Method | Description | Success Code |
|---|---|---|
| GET | Retrieve a resource or collection | 200 OK |
| POST | Create a new resource | 201 Created |
| PUT | Update an existing resource (full update) | 200 OK |
| PATCH | Partially update a resource | 200 OK |
| DELETE | Remove a resource | 200 OK |
Common Status Codes
| Code | Name | Meaning |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Client-side error (invalid input) |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Authenticated but lacks permissions |
| 404 | Not Found | Resource does not exist |
| 500 | Internal Server Error | Server-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/1Response 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 collection1→ specific resource
3. Get All Blogs
GET /blogsResponse 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/1Response 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=10This means:
page=1→ first pagelimit=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/commentsSimilarly, 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