- Easy to use for the end users/customers/clients
- Maintainable, modifiable and testable
- Simple, intuitive and adoptable
- Efficient
- GET /users - Retrieves a list of users
- GET /users/lorem or ? and then param list or a combination of both - Retrieves a specific user
- POST /users - Creates a new user
- PUT /users/lorem - Updates user lorem
- PATCH /users/lorem - Partially updates user lorem
- DELETE /users/lorem - Deletes user lorem
Don't mix up on verbs, nouns, singulars and plurals. The standard in the industry is to use plural nouns and to keep it simple. I wouldn't want to name an api what i would name my camel. Nor would someone want to call it at the end of the day.
Always version the API's. having the version in the api itself is more beneficial. /users/v1 or v1/users (choose one and stick to it) The other approaches are:
- through a parameter --> overhead
- through a custom header --> overhead
Writing a good api is of no use if someone can take a look at the code/documentation(not wikis) and be able to tell how to invoke it or what it does
Coverage is a must. Imagine a scenario where you have written a service and 6 months later someone pushes new code into it without building it. Figuring out what these changes broke will be painful and repetitive. Its like the below scenario:
while(true) { // no breaks here }
Sometimes its easier to just retrieve the nodes that are needed rather than a complete dump. A good api should contain the ability to pass in a comma separated list of nodes that are needed, else all are returned. eg. fields=abc,def,xyz
There are two options here:
Over head when using with traditional serializers and deserializers. Developers have to have annotations or custom parsing for snake casing. Snake casing doesn't comply with the coding standards for java. One advantage snake case has over camel case is that its easier to read. Would be a better approach if majority of the clients are in JS. If needed can have a wrapper.
Since the api is being designed to return a JSON(JavaScript Object Notation) response it is better to stick with JS naming convention and that is camel casing. Serializers and deserializers available also work seamlessly with this.
Add all common pojo's to the artifactory. The clients can too then use the pojo's for deserializing the response.
Return the appropriate status codes so that the consumers can accordingly handle/route the requests. There are a lot of status codes. We won't need to handle all. Just the most common and basic ones 200 – OK – Eyerything is working 201 – OK – New resource has been created 204 – OK – The resource was successfully deleted 304 – Not Modified – The client can use cached data 400 – Bad Request – The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“ 401 – Unauthorized – The request requires an user authentication 403 – Forbidden – The server understood the request, but is refusing it or the access is not allowed. 404 – Not found – There is no resource behind the URI. 422 – Unprocessable Entity – Should be used if the server cannot process the enitity 500 – Internal Server Error – API developers should avoid this error. If an error occurs in the global catch blog, the stracktrace should be logged and not returned as response. This should also be send back as part of the response header
This is one part that people usually mis out on when developing a service. An error is when a validation fails. Before processing the request have it go through a Validator(spring provides it). If the validations fail the response should contain a usefull error message. eg.
"error" :[ {
errorCode
relevant message
},{}...
]```
Don't use numbers pertaining to certain types of errors. Use standard errors, eg: NotNull, MissingStoreId, SystemError, etc.
#### Authentication
To be discussed if needed, i didnt see a need yet so haven't added it here
#### Easy to Debug
The service by itself should provide the ability to be debugged easily by providing a parameter/node in the payload.
A debug response isn't part of the response that a consumer/client consumes. This should come as a standalone node as this is used by the developers and not to be exposed to the client.
Important information a debug node should contain:
* Time taken for sub services/ tasks (individually)
* Total time take
* Any exceptions/errors should be returned here as well(yes we will still send them to the log, but do you want to keep scraping logs to see why something went wrong)
* All services/DB queries etc that were formed and executed
* All properties that were used(yes at times the issue lies in pushing incorrect properties and it is hard to find it)
#### Response Structures
Remember to keep it simple and easy to use. Any one can pickup the service and have the ability to utilize the data it sends back. See an example below :
```{
"status":"succes",
"statusCode": 200/201/500
"data":[encapsulate the response that the user wants as part of this node],
"error":[encapsulate the error code(s) and message(s) for the user/client],
"debug":[encapsulate the debug response for the developer],
}```
#### Field value
Don't over complicate things by creating arrays where not needed or converting floating points and integers to strings. Keep it simple.
## Don't use **GET** to alter state, we have a lot of scenarios where this is done.