An API is a programming interface for communication between applications or components. APIs are divided into private and public. Private APIs are used within a company if, for example, it has several software products that communicate with each other. Public APIs can be used by third-party developers. True, in some cases you have to pay for it. But then the user requirements for the quality and usability of the API will also be higher.
When API services become public, no miracle happens. The number of users is not growing, and you can generally forget about making this functionality paid (we exclude cases when the product is promoted due to marketing lies with aggressive advertising).
And if the company's income directly depends on the public API, then the stakes are really high. This idea is discussed in more detail in the book โAPI Continuous Development. The right decisions in a changing technological landscape โ(Mehdi Medjui, Ronnie Mitra, etc.):
and we will go further ...
Developers can mainly focus on getting all the planned functionality implemented in the API. But there are โnon-functionalโ requirements that are often not fully met and require special attention.
In my opinion, for all APIs, it is imperative that the following non-functional requirements be met in a good quality:
- Safety
- Documentation
- Validation
- Testing
Safety
It probably isn't worth explaining why safety came first. I have identified four of the most important security challenges:
- Using HTTPS with SSL Certificates
- Sharing Resources from Different Sources (CORS)
- Authentication and JSON Web Tokens
- Authorization and access privileges
* Hereinafter, we will narrow the context to REST and JSON API.
1. Using HTTPS with SSL certificates
HTTPS using SSL certificates is now the de facto security standard. To generate certificates, I personally use Let's Encrypt , a free, automated CA from the non-profit Internet Security Research Group (ISRG).
These certificates ensure that the data going from your API to the user is encrypted.
2. Sharing resources from different sources (CORS)
To keep requests to other sources secure, browsers use a mechanism called CORS. CORS stands for Cross-Origin Resource Sharing, a technology for sharing resources from different sources. Although browsers (by virtue of the same source rule) do not allow access to resources from different sources, CORS allows you to bypass these restrictions and at the same time ensure that access to resources is secure.
User agents (for example, browsers), based on the values โโof the additional headers for CORS in the HTTP request, can make requests to other sources that would be blocked without CORS.
Here's an example : An
HTML page served by a server from http://domain-a.com requests src at http://domain-b.com/image.jpg .
Many pages load resources like CSS styles, images and scripts from different domains corresponding to different CDNs (Content Delivery Networks). CORS
implementation for Node.js is quite popular today .
3. Authentication and JSON Web Tokens (JWT)
There are several approaches to API user authentication, but one of the best is using JWT. These tokens are signed using various cryptographic algorithms.
JSON Web Token is an open standard for creating access tokens based on the JSON format. Typically used to pass authentication data in client-server applications.
To create a token, you need to define a header with general information on the token, payload, such as user id, role, and so on, as well as signatures. In simple terms, JWT is just a string in the following format header.payload.signature.
When the client logs on, the identity management service provides the JWT to the client. The client can then use this token to make API requests. The API has access to the public key or secret that it uses to validate the token.
For verification, you can use, for example, the jsonwebtoken library . Below is the JavaScript code:
import jwt from 'jsonwebtoken'
export default function (req, res, next) {
// req.headers.authorization Bearer token
const token = extractToken (req)
jwt.verify (token, SECRET, {algorithms: ['HS256']}, (err, decoded) => {
the if (err) {next (err)}
req.session = Decoded
next ()
})
}
More information about JWT, libraries and supported programming languages - online JWT.io .
4. Authorization and access privileges
Authentication is important, but so is authorization: does a client who authenticated and received the JWT have the privilege to execute a particular request?
To test this, we use a scope. By analyzing it, the API service determines if this client request can be fulfilled without costly ACL lookups.
Scope is a text block (usually separated by spaces) that describes the access privileges of an API endpoint. It describes the Resources and Actions that can be applied to them. This formalization works well for REST / JSON APIs as they are very similar in structure.
RESOURCE: ACTION (for example, ARTICLE: WRITE or ARTICLE: READ, where ARTICLE is a resource and READ and WRITE are actions).
This allows API developers to focus on features rather than roles or users. The identity management service can associate roles and users with specific scopes, and then, along with the JWT, send the scop to the client.
To sleep calmly
When developing and deploying APIs, security should always be one of the most important requirements. Of course, you can talk and write about it endlessly, but the competent implementation of the four described tasks in production will already allow you to sleep well.
Documentation
What could be worse than a lack of documentation? Outdated documentation!
Developers are ambiguous about documentation. Many clearly do not have much love for this business. Be that as it may, this task is very important - especially when it comes to the public API. Third party developers should be able to learn how to use it. Plus, good documentation will help you train new developers who join your team faster.
API documentation should include three basic sections:
- Introduction (README)
- Datasheet (Specifications)
- Usage examples (Getting started and other similar subsections)
1. README
The documentation should tell you about what the API is for, how to set up the environment, how to test the service, how to deploy the project. Of course, users also need to know how and where to report difficulties or problems.
This is written in the README file. This file is usually also placed in the repository, it gives developers a starting point to work with your project.
The README should contain:
- API Description
- Links to technical references and manuals
- Developer setup guide
- Tester's Guide
- Deployment Guide
- Dependency management
- Contributor Guide
- Code of conduct
- License
- Thanks
Be concise in your README; you don't need to explain all the nuances, but provide enough information for the developers to dive into your project.
2. Specifications
In the REST / JSON API, each endpoint is a function built for a specific purpose. It is important to have technical documentation that describes each endpoint, inputs and outputs, and how the service works with different clients.
You can create your own API documentation based on OpenAPI .
It is a specification and complete framework for describing, creating, consuming, and rendering REST web services. Its purpose is to allow documentation systems to synchronize their updates with changes on the server. Methods, parameters, models and other elements are integrated with the server software through OpenAPI and are synchronized with it all the time.
3. Examples of use
Your API users are unlikely to be happy if you just dump the specs on them. They want to know how to use your API in specific situations and how to solve common problems. Most potential users have problems and they turn to your API to solve them.
A great way to introduce users to your API is to create a Getting started subsection. This will help you understand typical use cases and use them to evaluate the benefits of your API.
Three whales
Documentation is a key component of any API. When writing documentation, keep in mind the three pillars of successful API documentation - READMEs, specifications, and use cases. And you (and your users) will be happy!
Data validation
An important aspect of API development, data validation, is often overlooked by many. Validation is the process of validating input from external sources. These sources can be a client sending JSON or a service responding to your request. This check will make sure that the data is exactly as it should be. Thanks to it, you can eliminate many potential problems. An important task of validation is to understand what format and what range of values โโthe input data should have and how to control it.
The best strategy is to run validation before your service performs any data manipulation. When a client sends their data to your API, such as email, date, and name, make sure it is actually an email address, the date is formatted correctly, and the string meets the length requirements. This simple check will make your service more secure and organized.
Also, when you get data from a service, from some kind of database or cache, re-check it to make sure the result returned is as expected.
You can implement validation by hand, but libraries such as Lodash or Ramda can also be used for this purpose .... They are great for small data objects. Libraries like Joi , Yup, or Zod work even better because they allow you to describe a general validation framework, saving you time and effort. If you need something independent of a particular programming language, take a look at JSON Schema .
Better keep it out
Validation is hardly exciting, but it can save you a ton of time that would otherwise be spent troubleshooting and writing data migration scripts. Don't make the mistake of trusting your client to send unverified data if you don't want bad data to end up in your business logic or storage.
Take some time and arrange for validation for your input. As the saying goes, it is better to overdo it than to miss it.
Testing
Testing is an integral part of the software life cycle. In our case, it should be perceived as one of the key non-functional requirements. Defining a testing strategy can be a daunting task for any project, including a service API. Always try to be aware of your limitations and define your strategy accordingly.
Integration testing is one of the most effective API testing methods. Its task is to verify that the application correctly transitions from one state to another. In our case, the set of integration tests should include testing the service API entry points, as well as mockups to simulate sending a request to it and receiving a response. This is how we check the main test case of our service.
This method allows you to focus only on the data processing logic, without considering the backend services internals or data presentation logic. The lack of dependencies makes test execution more reliable, easier to automate, and easier to incorporate into a continuous integration pipeline.
For integration testing, I use Tape , Test-server and Fetch-mock . These libraries allow you to run isolated tests against API endpoints, going from request to response.
Imitation game
Other types of testing and type checking are certainly useful too, with integration testing having the greatest advantage: it is highly efficient with fewer man-hours spent writing and maintaining tests. And using tools like Fetch-mock can provide a complete simulation of data exchange.
Don't stop there
Once you've completed all four non-functional requirements, don't stop there. There are a few more: application monitoring, logging and API management. But in any case, security, documentation, validation and testing should come first.
Cloud servers from Macleod are fast and secure.
Register using the link above or by clicking on the banner and get a 10% discount for the first month of renting a server of any configuration!