NanoAPI uses annotations to simplify your codebase. Annotations are comments that are added to your code to provide additional information about your endpoints. They are used to define the structure of your API and to provide additional information about your endpoints.
Note: Annotations are currently required but will be optional in the future.
An annotation takes the form:
// @nanoapi path:/random method:GET [group:Math]
app.get('/random', (req, res) => {
res.send(Math.random());
});
Let's break down the annotation:
@nanoapi
This is the annotation keyword. It tells NanoAPI that this is an annotation.
path:<path>
This is the path of the endpoint. It tells NanoAPI what path this endpoint should be available at.
Paths may include path variables, such as /random/:length
if using ExpressJS. When working with path variables, you have one of two options:
/random/:length
/random/\\d+
method:<method>
This is the HTTP method of the endpoint. It tells NanoAPI what method this endpoint should respond to.
[group:<group>]
This is an optional field that allows you to group endpoints together. This is useful for organizing your endpoints and providing additional context. When naming groups, the group name will become the name of the output folder. e.g. /dist/Math/index.js
Ultimately, this powerful annotation allows you to create microservices or spin-off endpoints anywhere you like, and view them with our UI.
When it comes to annotating your code, you have three options:
If you are using a supported web framework, such as ExpressJS, the CLI will automatically detect your endpoints and optionally generate the annotations for you. This is the easiest way to get started with annotations.
The best part: annotations are completely optional outside of grouping. If you don't want to use them, you don't have to.
You can view the list of supported languages and web frameworks in our Language Support Roadmap.
If you are not using a supported web framework, you can manually add annotations to your codebase. This is a more manual process, but it allows you to have more control over your annotations.
If you have a large codebase and want to add annotations quickly, you can use the CLI to generate annotations for you. This is a more advanced option, but it allows you to add annotations to your codebase quickly and efficiently. To do this, check out the CLI documentation.
Annotations enable our code-splitting and UI display functionality. Here's an example of an annotated ExpressJS codebase:
// @nanoapi path:/random method:GET group:Math
app.get('/random', (req, res) => {
res.send(Math.random());
});
// @nanoapi path:/addition method:POST group:Math
app.post('/addition', (req, res) => {
const { a, b } = req.body;
res.send(a + b);
});
// @nanoapi path:/wordCount method:POST group:Writing
app.post('/wordCount', (req, res) => {
const { text } = req.body;
res.send(text.split(' ').length);
});
In this example, we have annotated three endpoints: /random
, /addition
, and /wordCount
. Each endpoint has a path, method, and group associated with it. When running a build, this will result in an output folder with three separate folders, each containing one of the endpoints.
When you run a split build, NanoAPI will keep the annotated endpoints, middleware, and initialization code and remove the rest of the code. This allows you to create microservices or spin-off endpoints anywhere you like, and view them with our UI.
On the flipside, NanoAPI will remove any API-based code that is not annotated and all of it's de-referenced dependencies. This allows you to keep your codebase clean and organized.
To better understand what code gets removed, let's examine the following two files:
// index.js
import express from 'express';
import * as service from './service';
const app = express();
const port = 3000;
// @nanoapi path:/random method:GET group:Math
app.get('/random', (req, res) => {
res.send(service.getRandomNumber());
});
// @nanoapi path:/wordCount method:POST group:Writing
app.post('/wordCount', (req, res) => {
const { text } = req.body;
res.send(service.countWords(text));
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
// service.js
export const getRandomNumber = () => Math.random();
export const countWords = (text) => text.split(' ').length;
In this example, the service.js
file contains two functions: getRandomNumber
and countWords
. However, if you remove the /wordCount
endpoint then the countWords
function becomes dead code.
NanoAPI handles this by removing the countWords
function from the output folder, keeping only the getRandomNumber
function. This allows you to keep your codebase clean and organized, and only keep the code that is necessary for your endpoints.
The resulting output folder would look like this:
// /dist/Math/index.js
import express from 'express';
import * as service from './service';
const app = express();
const port = 3000;
app.get('/random', (req, res) => {
res.send(service.getRandomNumber());
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
And the service file:
// /dist/Math/service.js
export const getRandomNumber = () => Math.random();
Often, the hardest part of working with large codebases is understanding what is happening and where. Up until now, the normal approach is to either read the code line-by-line to understand, or else to rely on documentation that may or may not be up-to-date (or exist).
With NanoAPI and our annotation system, you can now view your codebase in a more visual way. This allows you to understand your codebase better, and to choose exactly how and where to split it to create more efficient and robust microservices.