
The Web API is used for building modern websites. It provides loosely coupled, easy-to-maintain, and cross-platform features, which have become a standard for backend frameworks nowadays.
Our project is required to provide at least 10 APIs. These are listed below based on our features and user scenarios.
- GetStatus
- SearchCityByName
- GetCurrentWeatherByCity
- GetForecastWeatherByCity
- GetUserProfile (cookie needed)
- AddFavoriteCity (cookie needed)
- DelFavoriteCity (cookie needed)
- Register
- Login
- Logout (cookie needed)
API design
My general approach to API design is to keep it simple and easy to use. Here are some of the rules I follow:
- Create the contract before writing any code.
- Each API should perform only one task; avoid designing APIs with nested queries.
- The API name should clearly indicate the method, function, and query parameters.
- Use GET only for data fetching and POST only for data submission; do not use PUT or DELETE methods.
- Input parameters should support fuzzy queries.
- A successful query should return a 200 status code with a JSON body.
- If the query result is empty, return a 200 status code with an empty JSON body.
- For all other exceptions, return a 4xx or 5xx status code with an error message.
Here is an example,
/GetForecastWeatherByCity
Method: Get
QueryString: id={city_id}
Body (success, 200):
{"basic": {
"id": 1,
"cityname": "Beijing",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"sun_raise":"05:00",
"sun_set":"17:00",
"pressure":"1034",
"humidy":"71",
"feels_like":"21",
"detail_desc":"scattered clouds",
"simp_desc":"Clouds"
},
"forecast": [
{
"date":"2025-05-06",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-07",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-08",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-09",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-10",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-11",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
},
{
"date":"2025-05-06",
"temp": "20",
"temp_min":"15",
"temp_max":"23",
"simp_desc":"Clouds"
}]
}
Body (no result, 200):
{"cities": []}
Body (others, 404)
{"error": "{exception msg}"}
Questions
During my design process, I was considering how to name the APIs, define the paths, and set the parameters to follow the “best practices”. However, after deeper investigation, I realized that my API design style is closer to the RPC style rather than the RESTful style. There are many differences between these two approaches.
| Feature | RPC Style | RESTful Style |
|---|---|---|
| Design Philosophy | Remote Procedure Call | Resource Model and Uniform Interface |
| Path Design | Includes operation name and parameters | Uses nouns to represent resources |
| HTTP Methods | Mainly uses GET and POST | Fully utilizes GET, POST, PUT, DELETE, etc. |
| Parameter Passing | Query parameters or request body | Path parameters and query parameters |
| Resource Model | Does not emphasize the concept of resources | Emphasizes the concept of resources |
| Scalability and Flexibility | Paths increase when adding new operations | Expanded by adding new resources or resource collections |
| Applicable Scenarios | Complex business logic | Scenarios with a clear resource model |
Best practices for RPC-Style APIs
- Use clear and consistent method naming
- Follow action-oriented naming conventions: getUser, createOrder, updateInventory.
- Use namespaces or service names if needed, e.g., user.getProfile.
- Keep method interfaces simple
- Prefer single input and single output message formats (e.g., request and response objects).
- Avoid excessive nesting in request/response objects.
- Version your services
- Use explicit versioning in the service name or endpoint: UserServiceV2, v1.UserService.
- Avoid breaking changes without incrementing versions.
- Define strong contracts
- Use schemas (e.g., Protobuf for gRPC) to enforce strict typing.
- Leverage tools for API linting and validation.
- Error handling
- Standardize error codes and messages.
- Use consistent and structured error formats with fields like code, message, and details.
- Optimize for performance
- Use efficient binary protocols (e.g., gRPC with Protobuf) for better performance and lower latency.
- Enable streaming for large or continuous data transfers when appropriate.
- Authentication & authorization
- Apply authentication at the transport layer (e.g., TLS, mTLS).
- Use tokens (JWT, OAuth2) and scopes for access control.
- Documentation and tooling
- Generate client/server stubs from interface definitions.
- Auto-generate and publish service documentation.
Best Practices for RESTful-Style APIs
- Use resource-based URIs
- Use nouns, not verbs: /users, /orders/123, not /getUser.
- Maintain consistent URI structure.
- Proper HTTP methods
- GET – Retrieve resources
- POST – Create a new resource
- PUT – Update/replace an existing resource
- PATCH – Partially update a resource
- DELETE – Remove a resource
- Use standard HTTP status codes
- 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error, etc.
- Pagination, filtering, and sorting
- Support pagination: ?page=1&limit=20
- Filtering: ?status=active
- Sorting: ?sort=created_at&order=desc
- Use HATEOAS (Hypermedia as the engine of application state)
- Include relevant links in responses to guide clients:
{ "id": 1, "name": "John", "links": [ { "rel": "self", "href": "/users/1" }, { "rel": "orders", "href": "/users/1/orders" } ] }
- Statelessness
- Each request should contain all necessary context (e.g., tokens).
- Avoid server-side session state.
- Content negotiation
- Support Accept and Content-Type headers (e.g., application/json).
- Provide meaningful error messages in the same format.
- API versioning
- Use URI versioning (/v1/users) or header-based versioning (Accept: application/vnd.api+json;version=1).
- Security
- Use HTTPS.
- Implement authentication (OAuth2, API keys).
- Validate and sanitize all inputs to avoid injection attacks.
- Documentation
- Provide OpenAPI (Swagger) specs.
- Ensure examples, status codes, and data models are clear.
Some findings and thoughts
- RESTful-style APIs focus more on resource operations, while RPC-style APIs are more focused on functions and methods.
- “Best Practices” do not mean a single standard. Different API styles can all achieve the same goals. However, following a shared standard can improve team collaboration, especially in large engineering teams.