API plays a huge part in modern integration architecture design, a good design will allow your application to thrive, a bad design will end up on the cold stone bench and eventually vanishes. :(
Well, to avoid this tragedy from happening to our APIs, there are certain guidelines that we might want to consider to follow. I know there are many debates out there on the best practice of API design, and I don't think it will ever end. It really depends on many different factors; mostly dominated by the size and complexity of the integration solution, and the company culture. And many of them relate to how to manage it instead of designing it. (Of course, there are many others like API security, how to do versioning and all these sorts of things. These relate more closely to API management, that I will not cover in this post.)
This is what I think makes a good API:
- Intuitive - It must be easy to understand and can be used without documentation.
- Stable - Not only should it run but it should also have good performance too.
- Demands - Creating useful functionality, no matter how nicely you document your API or how easy it is to use, if people don't need it, they won't call it.
The Stability is mostly dependent on how robust your code is, the DEVOPS process, how the IT infrastructure is built and so on. The Demands are a much harder question to answer, so I am going to focus on the Intuitive. Here are just couple of best practices of that I think that can make APIs more intuitive, and how it's done in Camel. (RESTFul API, strictly speaking, the technology the most people use is the standard.)
- Simply but concrete naming for the URI.
- Common rules, use nouns instead of verbs, that describe the content the API is providing, such as customer, account, and product etc. In Camel the place to define it is in the REST DSL, it allows the developer to define REST services in its application. Define the name of the API in the URI and have several layers of API description.
<get uri="customer/{customerid}"> <to uri="direct:getCustomerinfo"/> </get> <get uri="product/{id}"> <to uri="direct:productInventory"/> </get> <get uri="account/profile/{acctid}"> <to uri="direct:getprofile"/> </get>
-
- From the architectural standpoint, one of the good things about Camel and Fuse/Fuse Integration Service, is you naturally create these independent chunks of service, that either connects to a datasource (or multiple if you want, but that does not quite fit in the spirit of doing microservices) and then becomes a microservice. Or you can use it to create a service that composes/orchestrates APIs for the consumer.
- Use HTTP Method for CRUD if possible:
- READ -> GET
- CREATE -> PUT
- UPDATE -> POST
- DELETE -> DELETE
- Instead of creating four different names with the verbs that we normally do in webservice back in the day, take advantage using the HTTP method, which is already there and the HTTP header actually maps to different operations that you need to do. In Camel DSL, it is very simple to define it.
- Not only does it eliminate the need for the whole team to agree upon the naming of the actions, since it's a globally recognized standard, consumers will find it easier to understand.
- Make the most out of HTTP Errors,
- Wrapping your own error is good, but think about this, when a consumer gets a response from the API, what is the first content it gets? Instead of processing the entire payload, (which is not restricted to error, there are several successful status codes too) the consumer receives the HTTP response header.
- Here is the list of HTTP status codes; some are commonly used in RESTApi along with a few common ones for RESTFul API.
HTTP Code | HTTP Methods | Description (From Wiki page) |
200 | GET, DELETE | Standard response for successful HTTP requests. The actual response will depend on the request method used. |
201 Created | PUT, POST | The request has been fulfilled, resulting in the creation of a new resource. |
202 Accepted | POST, PUT, DELETE | The request has been accepted for processing, but the processing has not been completed. |
400 Bad Request | GET, POST, PUT, DELETE | The server cannot or will not process the request due to an apparent client error |
401 Unauthorized | GET, POST, PUT, DELETE | Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. |
403 Forbidden | GET, POST, PUT, DELETE | The request was a valid request, but the server is refusing to respond to it. |
404 Not Found | GET, POST, PUT, DELETE | The requested resource could not be found. |
408 Request Timeout | GET, POST, PUT, DELETE | The server timed out waiting for the request. |
409 Conflict | PUT, PUT, DELETE | Indicates that the request could not be processed because of conflict in the request. |
500 Server Error | GET, POST, PUT, DELETE | A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. |
503 Service Unavailable | GET, POST, PUT, DELETE | The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state. |
-
- In Camel, you can set the value in the header to return the HTTP status code.
<setHeader headerName="camelhttpresponsecode"> <constant>400</constant > </setHeader>
- Setting the right granularity of data and using the common data format:
- I don't believe in the ONE SIZE FITS ALL data model when it comes to providing information to various consumers, the amount of information can be shown on a mobile device screen, a smart watch or a desktop computer is definitely different.
- Camel has the capability for you to automatically transform the data from POJO into the format you want by specifying the output, or you can simply marshal with various Camel supported data formats or do a little extra processing with the Data Mapper transform component.
The binding mode:
<restConfiguration bindingMode="json" />
Binding Mode | Description |
---|---|
off | Binding is turned off. This is the default option. |
auto | Binding is enabled and Camel is relaxed and supports json, XML, or both if the needed data formats are included in the classpath. Notice that if for example, camel-jaxb is not on the classpath, then XML binding is not enabled. |
json | Binding to/from json is enabled and requires a json capable data format on the classpath. By default, Camel will use json-jackson as the data format. See the INFO box below for more details. |
xml | Binding to/from XML is enabled and requires camel-jaxb on the classpath. See the INFO box below for more details. |
json_xml | Biding to/from json and xml is enabled and requires both data formats to be on the classpath. See the INFO box below for more details. |
Or DataFormat Marshaling (Don't forget to add the dependency of the converting libraries).
<marshal ref="transform-json"> <json library="Jackson"/> </marshal>
Or Data Mapper transform (Dozer).
- Automatically generated documentation:
- One of the nice handy little tricks when doing API in Camel with REST DSL, you can simply integrate it with Camel swagger java components. It will expose the API in swagger format.
- In Camel, don't forget to first to add the dependency in your maven pom.
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-swagger-java</artifactId> <version>x.x.x</version> </dependency>
And inside the Camel context, when setting up the rest configuration, simply add the API related configuration into it.
<restConfiguration apiContextPath="api-docs" bindingMode="json" component="servlet" contextPath="/demos"> <apiProperty key="cors" value="true"/> <apiProperty key="api.title" value="Healthcare demo clinical API"/> <apiProperty key="api.version" value="1.0.0"/> </restConfiguration>
In summary, this diagram maps to Camel and its best practices.
Thanks!
Last updated: August 29, 2023