Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reference Specification for API first approach #93

Merged
merged 17 commits into from
Nov 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions docs/functional-components/apifirst.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
sidebar_position: 1
---

# REST APIs Development

Building [RESTFull](https://www.redhat.com/en/topics/api/what-is-a-rest-api) APIs is a typical use
case for Node.js. There are two typical approaches:

* API First - define the API, use tools to help scaffold and then fill in the implementation.
* Code First - implement the APIs and then generate documentation based on exposed API

The team's experience is that the API first approach based on [OpenAPI](https://swagger.io/specification/)
provides benefits in both initial implementation and maintenance and our recommended packages and
guidance is based on that approach, particularly if one or more of the following are true:

- New project without existing API
- Client side and backend are developed by different teams that need way to communicate API changes
- Development on clients and backends starts around the same time giving developers ability to mock API based on OpenAPI spec

> NOTE: This section of the reference architecture focuses on building RESTfull APIs.
For GraphQL please refer to the [GraphQL Guide][] section.

## "API-first" approach

With the API-first approach, designing the API is the first priority before writing any code. Design of the API involves thorough thinking and planning through collaboration with different teams (both client and backend side). This results in high-level documentation describing the intent of the API and ability to build client without waiting for server to be finished.

This API contract acts as a central draft keeping all your team members aligned on what your API’s objectives are and how your API’s resources are exposed. The finalization of the contract allows the team to build the interface of the application.

After this, the cross-functional teams rely on this interface to build the rest of the application independent of each other. In practice, teams can generate backends stubs and fully functional client libraries to avoid deviation from specification set in the OpenAPI spec file.

## Code First vs API first

Code first approach provides libraries that understand server side backend structure and generate respective OpenAPI files.
In this approach full control over API lies within server side team - generated OpenAPI file is read only and cannot be effectively
used to negotiate API between client and server.

API first approach uses OpenAPI file as source of truth. Both client and server side generate code based on the OpenAPI file.

## Recommended Packages

List bellow provides comprehensive set of libraries that can be used for an end to end full stack application written in Node.js,
Express.js as well as client side applications.

### Code Generation Tools

[openapitools/openapi-generator-cli](https://www.npmjs.com/package/@openapitools/openapi-generator-cli)
This CLI provides support for generating source code based on the OpenAPI spec. The project that
provides this cli for JavaScript also provides generators for a number of other
languages as well. This CLI has widespread usage across industry including many community projects at Red Hat.
The project is maintained by OpenAPI Generator Community and you can read the documentation
here:- <https://openapi-generator.tech>. It can be used as both a backend and client generator as follows:

**backend generator**

The nodejs-express-server option can be used to generate Express.js based stub
implementations based on your OpenAPI file.
```bash
npx @openapitools/openapi-generator-cli generate -g nodejs-express-server -i yourapi.json -o ./yourproject
```

**Client generator**

The typescript-node option can be used to generate a client for Node.js applications
that allows us to perform requests against another backend

```bash
npx @openapitools/openapi-generator-cli generate -g typescript-node -i yourapi.json -o ./yourproject
```

### API mocking

[openapi-backend](https://www.npmjs.com/package/openapi-backend) allows you to mock based
on an OpenAPI definition by returning predefined strings. The library provides way not only
return predefined stubs but also perform validation or handle different use cases depending on request

[@stoplightio/prism](https://www.npmjs.com/package/@stoplight/prism-http) allows you to
automatically mock API using OpenAPI spec definitions. This package is recommended if you
need is an out of the box way to mock API without any development involved.

### API validation middleware

[express-openapi-validator](https://www.npmjs.com/package/express-openapi-validator) is a validator
for express middleware that some of the build have used successfully.

### Creating/editing OpenAPI Specifications

[swagger-editor](https://www.npmjs.com/package/swagger-editor) is the most popular editor
which can be embedded into an existing server or run standalone. If you want to edit your
specifications in YAML, you can use the
[openapi-editor](https://www.npmjs.com/package/openapi-editor) wrapper.

[vscode-openapi](https://github.com/42Crunch/vscode-openapi) is a VScode plugin for
building and validation of OpenAPI files that members of the team using vscode
have used successfully.

## Guidance

Based on the teams experience we recommend the following:

1. Define the API using OpenAPI 3.0, you can write the OpenAPI definitions in YAML or JSON formats, the team does not have a preference for one over the other.
2. Prefer generating code from OpenAPI file for both client and server. Generating code based on the specification will ensure that the same types, formats are used. This will enable your team to iterate on the specification without worry of getting out of sync.
3. When making changes in the OpenAPI file change it's [version](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore-expanded.yaml#L3). Changed version will help others to detect what kind of changes were made.
4. When introducing breaking changes consider adding them as new endpoints by introducing another v2 prefix in the API path.
5. Every path should have `operationId` field specified. This field is used by generator to generate method names for clients etc.
6. When building response objects prefer referencing predefined Objects and Enums in [Schemas](https://swagger.io/docs/specification/data-models/)
7. If an API returns a specific [error object](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore-expanded.yaml#L148-L158) it should be defined in the Schemas.
8. Declare servers and security scheme to enable users to call API from OpenAPI Editor and other tools.
9. Use [tags](https://swagger.io/docs/specification/grouping-operations-with-tags/) to define namespaces for your API. Grouping operations with tags that will be used to organize your generated source code into folder structure.

### A good example

OpenAPI spec provides an complete and minimalistic [PetStore](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore-expanded.yaml) example. This example follows all the best practices and patterns for building API.


[GraphQL Guide]: https://nodeshift.dev/nodejs-reference-architecture/functional-components/graphql
2 changes: 1 addition & 1 deletion docs/functional-components/auth.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 2
---

# Authentication
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/data-caching.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
---

# Data Caching
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/databases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Databases
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/graphql.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
---

# GraphQL Development
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/internationalization.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 5
sidebar_position: 6
---

# Internationalization
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/message-queuing.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 6
sidebar_position: 7
---

# Message Queuing
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/nodejs-versions-images.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 7
sidebar_position: 8
---

# Node.js Versions and Container Images
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/scaling-multi-threading.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 8
sidebar_position: 9
---

# Scaling and Multi-threading
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/static-assets.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 9
sidebar_position: 10
---

# Static assets
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/template-engines.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 10
sidebar_position: 11
---

# Template Engines (Server Side)
Expand Down
2 changes: 1 addition & 1 deletion docs/functional-components/webframework.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 11
sidebar_position: 12
---

# Web Framework
Expand Down
47 changes: 47 additions & 0 deletions npcheck.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,30 @@
"name": "kafkajs",
"npmlink": "https://www.npmjs.com/package/kafkajs"
},
{
"name": "@openapitools/openapi-generator-cli",
"npmlink": "https://www.npmjs.com/package/@openapitools/openapi-generator-cli"
},
{
"name": "openapi-backend",
"npmlink": "https://www.npmjs.com/package/openapi-backend"
},
{
"name": "@stoplight/prism-http",
"npmlink": "https://www.npmjs.com/package/@stoplight/prism-http"
},
{
"name": "express-openapi-validator",
"npmlink": "https://www.npmjs.com/package/express-openapi-validator"
},
{
"name": "swagger-editor",
"npmlink": "https://www.npmjs.com/package/swagger-editor"
},
{
"name": "openapi-editor",
"npmlink": "https://www.npmjs.com/package/openapi-editor"
},
{
"name": "nyc",
"npmlink":"https://www.npmjs.com/package/nyc"
Expand Down Expand Up @@ -155,6 +179,29 @@
"eslint": {
"note": "dependency argparse uses licence Python-2.0",
"allow": ["Python-2.0"]
},
"@openapitools/openapi-generator-cli":{
"allow": ["0BSD"]
},
"openapi-backend": {
"note": "Multiple dependencies use licenses",
"allow": ["Python-2.0"]
},
"express-openapi-validator": {
"note": "Multiple dependencies use licenses",
"allow": ["Python-2.0"]
},
"swagger-editor": {
"note": "Multiple dependencies use licenses",
"allow": ["Python-2.0","0BSD"]
},
"@stoplight/prism-http":{
"note": "dependency tslib@2.3.1 reports 0BSD which is less restrictive than BSD",
"allow": ["0BSD"]
},
"openapi-editor": {
"note": "dependency caniuse-lite uses CC-BY-4.0",
"allow": ["CC-BY-4.0"]
}
}
}
Expand Down