Frends is a platform for rapid integrations development, which supports fast web API development among many other features. There are, however, many ways to achieve the same thing, as well as changes to the exact implementation coming from version updates. The purpose of this blog is to advice you about best approaches and guide you around different versions with the experience of a seasoned Frends API developer. Naturally there are many things to consider while developing APIs, but this first part will guide you through following topics:
Should I use Swagger or OpenAPI?
Future expansions in mind, how should I name my API and its operations?
How do I protect my API?
Should I define query or path parameters?
Swagger or OpenAPI?
Initially Frends API development started with support for Swagger/OpenAPI 2.0, with full support for OpenAPI 3.0 specification released in version 5.3. Choosing between Swagger/OpenAPI 2.0 and OpenAPI 3.0 specification depends on one thing only:
If you're working with a Frends version prior to 5.3, then the only choice is OpenAPI 2.0;
From 5.3 onwards it is recommended to use the newest version of the specification.
NB! If your current Frends environment is of the 5.2.x series and you're planning to update 5.3 or later version, it is recommended to convert the OpenAPI 2.0 specification to version 3.0. With the version change OpenAPI specification validation turned more stricter, and as a result features or API key security scheme might not function as before (Please, refer to release notes).
For OpenAPI 2.0 reference manual, please refer to here.
For OpenAPI 3.0 reference manual, please refer to here.
Naming of the API and the operations
When writing an API specification for, eg. a petstore, developer might be tempted to write the name of the API after the version denoter, so that the url appears like this: contoso.com/api/v1/petstore. It might save some time later, as you don't have to add version numbers to different operations.
But what would happen, if an operation, eg. `/inventory`, needed an update, but at the same time the old version should remain operational for backwards compatibility? You would have to write a completely new API specification, and then maintain two different `/inventory` methods in two different specifications.
Recommended approach to API and operation naming is as follows:
1. Put the name of the API after the `api` keyword (`api` keyword has to be the first naming element as per Frends requirements):
- Swagger/OpenAPI 2.0: write the `basePath` element like this:
```
"basePath": "/api/petstore", ...
```
- OpenAPI 3.0: write the `servers[].url` element like this:
```
"servers": [
{ "url": "https://localhost/api/petstore" }
], ...
```
2. Add the operations to the `paths` object, with the version number (`v1`) in front of the operation name:
```
"paths": {
"/v1/inventory": {
...
},
...
}
```
- Generally long and complicated path names are not endorsed; `/inventory` or `/inventory/{storeid}` are still simple enough to understand.
- A resource naming guide can be found here
3. Now, if you ever need to add an operation of the same name to the API, but with different content or functionality, you can specify that with the next available version number, eg. `v2`.
Describing authentication
Frends provides several authentication methods for API operations, and the API trigger documentation covers all the cases.
When writing an API specification, one must consider how to apply these different security schemes:
should the entire API with all of its operations be protected with authentication, or
should only some operations (mostly write operations) of the API require authentication.
With the first case, applying security on the root level of the API specification should do the job, while with the second case security scheme must be declared along with every operation requiring it. OpenAPI's online documentation describes how each case is done:
- for OpenAPI 2.0 here, and
- for OpenAPI 3.0 here.
Describing request parameters
It might be tempting to describe a request only as a GET request, which gets all the required information for processing as query and/or path parameters. For some use cases, this is outright not recommended. Here is a short outline, what these different parameters are, and when they can or should be used.
Path parameters
You might have noticed a path parameter earlier already, such as `/inventory/{storeid}`. If the parameter is a codified identifier without special characters, then it's logical to use that. Otherwise, consider using a query parameter, or, with the case of sensitive data such as SSNs and credit card numbers, a body parameter. Any information passed in the URI will go forward unprotected, and could be logged by several entities. More details from the OWASP Foundation.
A short example to defining a path parameter, case path: `/inventory/{storeid}`:
With OpenAPI 2.0:
```
"parameters": [
{
"name": "storeid",
"in": "path",
"description": "Store identification code",
"required": true,
"type": "string"
},
...
]
```
With OpenAPI 3.0:
```
"parameters": [
{
"name": "reportdate",
"in": "query",
"description": "Which day's inventory should be reported",
"required": false,
"schema": {
"type": "string",
"format": "date"
}
},
...
]
```
This parameter can be used then as such: `/inventory/{storeid}?reportdate=2021-05-15`
Body parameters
For complex data structures, long documents, and sensitive (personal) information, the right choice is a body parameter. Body parameters can be passed with `POST`, `PUT` and `PATCH` requests.
A short example to defining a body parameter, case path: `/inventory/{storeid}`:
With OpenAPI 2.0:
```
"parameters": [
{
"name": "inventory",
"in": "body",
"description": "Inventory of the specified store",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Inventory"
}
}
},
...
]
```
With OpenAPI 3.0:
```
"requestBody": {
"description": "Inventory of the specified store",
"required": true,
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Inventory"
}
}
}
}
},
...
```
Validating parameters
Though a user may define in the API specification intricate details for the format of the parameters, Frends doesn't actually perform any validation on them. Sure, if the user tests the implementation through Frends' Swagger UI, the interface will complain if any required parameter is missing from the request. But that's all.
Good thing is that if you have described the data object in JSON format in the specification, or if you have access to an XSD schema file, you can implement the validation in the Frends implementation of the operation with either of these two custom tasks:
Recommended way to validate any query parameters is using code shapes or statements.
Conclusion
If you've read so far, you might be asking the following questions:
How should I *really* define the API operation, and which HTTP request method should that operation use? (Like, this was discussed a bit in part 4, but I'm still confused.)
How should I define response messages?
I need SOAP! What about XML?
Don't panic! Second part in this blog series will discuss these questions in more detail.
Stay tuned!