-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feature/article-devops-as-a-culture
- Loading branch information
Showing
46 changed files
with
1,918 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
--- | ||
name: Dmitri Sirobokov | ||
avatar: /authors/dmitri-sirobokov.jpg | ||
occupation: Software Developer (Full stack) | ||
linkedin: https://www.linkedin.com/in/dmitri-sirobokov/ | ||
github: https://github.com/dmitri-sirobokov | ||
--- | ||
|
||
Dmitri is a passionate software developer with many years of experience in backend and frontend development. He is always excited to learn more about Software Design Patterns and Algorithms. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
name: Roy Berris | ||
avatar: /authors/roy-berris.jpg | ||
occupation: Back-end Developer | ||
linkedin: https://www.linkedin.com/in/roy-berris/ | ||
github: https://github.com/royberris | ||
website: https://berris.dev/ | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
name: Tim Dujardin | ||
avatar: /authors/tim-dujardin.jpg | ||
occupation: Frontend developer | ||
twitter: https://twitter.com/timdujardin | ||
linkedin: https://www.linkedin.com/in/timdujardin/ | ||
github: https://github.com/timdujardin | ||
--- |
Large diffs are not rendered by default.
Oops, something went wrong.
221 changes: 221 additions & 0 deletions
221
data/blog/frontend-loves-openapi/let-me-introduce-you-to-openapi.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
--- | ||
title: 'Let me introduce you to OpenAPI' | ||
date: '2023-01-11' | ||
tags: ['frontend', 'API', 'Architecture'] | ||
summary: 'OpenAPI allows us to improve effeciency between teams and interdependent projects. It allows us to describe our API in a single document. In this part of the series, we will take you through the most important parts of an OpenAPI document.' | ||
authors: ['maarten-van-hoof'] | ||
serie: 'frontend-loves-openapi' | ||
--- | ||
|
||
[In the previous part of this series](openapi-a-contract-for-rest-ful-apis), we introduced you to the purpose of contracts between API providers and consumers. This visibility allows us to improve efficiency between teams and interdependent projects, wether that be internal or external projects. | ||
|
||
Let us walk you through a few sections of an OpenAPI document, the ones that will most likely the largest part of your document and where most of the information is stored. | ||
|
||
## Main sections | ||
|
||
OpenAPI files can be written in both JSON and YAML format. For brevity, we will use the YAML format. | ||
|
||
```yaml | ||
# Specification version | ||
openapi: ... | ||
|
||
# General API metadata | ||
info: ... | ||
|
||
# Server information. BaseURLs, environments, ... | ||
servers: ... | ||
|
||
# Available paths & operations | ||
paths: ... | ||
|
||
# Datamodel abstraction | ||
components: ... | ||
|
||
# Security mechanisms can be used across the API | ||
security: ... | ||
|
||
# Grouping of paths & components | ||
tags: ... | ||
|
||
# Additional external documentation | ||
external docs: ... | ||
|
||
# Webhook operations, similar to paths, only API is now a consumer. | ||
webhooks: ... | ||
``` | ||
On the first level, we describe a few general sections and properties. First, we need to declare our **OpenAPI version**. This is to ensure compatibility with certain types of tooling. | ||
Next, we can declare some **general information** with the **info** property. Who has written this document, where to contact the authors, etc. | ||
In the **servers** section, we can declare multiple base URLs on which the API will be available. For instance, when you have various environments available. | ||
The first major section of a typical OpenAPI document is the **paths** section. Here we describe our operations, which are a combination of paths and HTTP methods. These operations describe the required data that the consumer has or can provide, like query string parameters, URL parameters, request bodies and the data that the API can respond with, status codes, content types, and data formats. | ||
The second major section is the **components** section. The OpenAPI specification extends the JSON schema specification. It allows us to reuse parts of our internal and external documents with the power of JSON schema references. In the components section, we can abstract and define data models that we can refer to from our operations. | ||
We define the security mechanisms to which our API validates the consumer in the **security** section, and link each mechanism to the operations needing a specific mechanism. | ||
In the **tags** section, we can add taxonomy to group our operations, provide links to external documentation and declare webhooks where the API now becomes a consumer itself. | ||
Furthermore, we can add **external documentation** to our API if the format of this document does not suffice your needs, and we can declare **webhooks** where the API now becomes a consumer itself. | ||
## Paths | ||
```yaml | ||
paths: | ||
/pets: | ||
get: | ||
summary: List all pets | ||
#... | ||
post: | ||
summary: Create a pet | ||
#... | ||
|
||
/pets/{petId}: | ||
get: | ||
summary: Info for a specific pet | ||
#... | ||
``` | ||
|
||
In paths, we first declare the URL the operation is available on, next the HTTP method. The combination of a URL and an HTTP method is called an **operation** in the OpenAPI context. | ||
|
||
### Operations | ||
|
||
```yaml | ||
paths: | ||
/pets: | ||
get: | ||
# A unique identifier for this operation. Mostly used in OpenAPI tooling. | ||
operationId: listPets | ||
# A short summary of what the operation does | ||
summary: List all pets | ||
# A list of tags for API documentation control. Tags can be used for logical grouping of operations by resources or any other qualifier. | ||
tags: | ||
- pets | ||
parameters: | ||
- name: limit | ||
# It is a query string parameter | ||
in: query | ||
# A more thorough description of what this parameter does to a request | ||
description: Limit how many pets this API will return. | ||
# It is not required | ||
required: false | ||
# It must be an 32-bit integer | ||
schema: | ||
type: integer | ||
format: int32 | ||
|
||
# description | ||
# requestBody | ||
# security | ||
# ... | ||
|
||
responses: | ||
#... | ||
``` | ||
|
||
In an operation, we can declare the parameters or requestBody it should or can receive and how it should respond. We can also declare an **operationId**, which most tooling uses as an identifier for other functionalities, a **summary** or **description** to better describe the functionality this operation offers, which **security schemes** this operation has to adhere to, etc. | ||
|
||
### Responses | ||
|
||
```yaml | ||
paths: | ||
/pets: | ||
get: | ||
#... | ||
|
||
responses: | ||
'200': | ||
description: Expected response to a valid request | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Pets' | ||
default: | ||
description: unexpected error | ||
content: | ||
application/json: | ||
schema: | ||
$ref: '#/components/schemas/Error' | ||
``` | ||
We declare an operation **response** by stating a **status code** or the keyword **default** for a default response an operation should return when the defined status codes don't suffice. Very handy to declare a default error response that produces the same error format for all erroneous status codes. | ||
Then we declare the **Content-Type** with which the provider will respond. RESTful APIs aren't limited by responding only in JSON. XML, plain text, HTML, and also binaries are possible Content-Types one can define. | ||
Next, we'll define the data model with which our API provider will respond. This can be the entire definition of a component at once or a reference to a component defined in the root-level section Components. | ||
The OpenAPI specification extends the JSON schema specification and allows us to use one of its powerful features: References. With references, we can refer to other parts of the document or refer to parts of external documents. References are declared with the `$ref` keyword. | ||
|
||
I'll explain more about the declaration of data models in the next section, but for now, we'll just use a reference to a component. | ||
|
||
## Components | ||
|
||
In the [components](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object) object, we can describe data models we can reuse throughout our document thanks to JSON schema references' power with the $ref keyword. [More information about the $ref keyword can be found here](https://json-schema.org/understanding-json-schema/structuring.html#ref). It allows us to keep our OpenAPI document a bit cleaner, [with less repetition and more DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). | ||
|
||
```yaml | ||
components: | ||
schemas: | ||
Pet: | ||
Pets: | ||
responses: | ||
ErrorResponse: | ||
requestBodies: | ||
NewPet: | ||
headers: | ||
Limit: | ||
Offset: | ||
Pagination: | ||
``` | ||
|
||
The Components object has several [fixed fields](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#fixed-fields-6) to subcategorise the type of component we declare. | ||
|
||
- `schemas`: Input and output data types. | ||
- `responses`: Response objects | ||
- `parameters`: Operation parameters; path, query-string, ... | ||
- `examples`: Example objects that can describe more realistic data | ||
- `requestBodies`: RequestBody objects | ||
- `headers`: Header objects | ||
- `securitySchemes`: securitySchemes Objects | ||
... | ||
|
||
### Schemas | ||
|
||
In the schema object, we describe a document's most atomic level of data objects: our responses' input and output types, requestBodies, parameters, etc. | ||
|
||
```yaml | ||
components: | ||
schemas: | ||
Pet: | ||
type: object | ||
properties: | ||
id: | ||
type: integer | ||
format: int64 | ||
name: | ||
type: string | ||
image: | ||
type: string | ||
tag: | ||
type: string | ||
required: | ||
- id | ||
- name | ||
Pets: | ||
type: array | ||
items: | ||
$ref: '#/components/schemas/Pet' | ||
``` | ||
|
||
For example, we describe our Pet as an object. It has the properties id, name, image, and tag. All, except id, are described as values of the type string. Id is defined as a 64-bit integer. Id and name are described as required, meaning API consumers should consider that the image and tag value could not be in the returned data. We can reuse our Pet component to create a Pets component, an array of the Pet component. | ||
|
||
## Conclusion | ||
|
||
In this article, we've seen how to define an OpenAPI document. We've seen how to define the metadata of our API, the **operations** it offers, the **responses** it can return, and how to define and reuse the data models it uses. | ||
|
||
In the next part of this series, we'll show you ways of integrating OpenAPI in to your team and project workflows. |
86 changes: 86 additions & 0 deletions
86
...nd-loves-openapi/let-your-restful-api-development-soar-with-open-api-tooling.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
--- | ||
title: 'Let your RESTful API development soar with OpenAPI tooling' | ||
date: '2023-02-01' | ||
tags: ['frontend', 'API', 'Architecture'] | ||
summary: 'Because the OpenAPI specification is a specification, just like the EcmaScript, HTML or CSS specification, we can reliably build tooling upon it. This tooling allows us to optimise our OpenAPI workflows and let us save precious time.' | ||
authors: ['maarten-van-hoof'] | ||
serie: 'frontend-loves-openapi' | ||
--- | ||
|
||
In the previous parts of this series, we introduced you to the OpenAPI specification, how to describe an API provider in an OpenAPI document, the approaches we can take incorporating it on workflows helping us build and integrate with RESTful APIs. In this part, we will discuss the tooling that is available to optimise our OpenAPI workflows. | ||
|
||
<br/> | ||
|
||
![OpenAPI tooling](/articles/frontend-loves-openapi/frontend-loves-openapi-tools-all.svg) | ||
|
||
## Documentation | ||
|
||
The most popular type of tooling for the OpenAPI specification is documentation generation. It allows us to take the information of an OpenAPI document and present it in a tailored fashion. For example, non-technical users might not be familiar with reading JSON or YAML-based files. With documentation generation, we can give every stakeholder of our project insights into how the data from and to an API provider flows. | ||
|
||
Some examples of OpenAPI documentation generators: | ||
|
||
- [Swagger UI](https://github.com/swagger-api/swagger-ui): The most well-known one. The green documentation site. Maintained by Swagger, the original author of the OpenAPI specification, formerly known as the Swagger specification. | ||
- [Stoplight Elements](https://github.com/stoplightio/elements): A fresh take on OpenAPI documentation, focusing more on the metadata, like titles and descriptions, rather than the technical information. It also includes a code snippet generator with snippets for dozens of programming languages and their flavours. | ||
- [ReDoc](https://github.com/Redocly/redoc): A generator that creates full-screen OpenAPI documentation experiences. Code snippets and examples live next to the section of the operation described. | ||
|
||
## Code generation | ||
|
||
With the Code First workflow, the API provider will likely generate the OpenAPI document. For the Design First workflow, we can reverse this by generating our communication layer of providers and/or consumers. | ||
|
||
With code generation, we don't have to write API clients anymore. The entire API description is translated into tailored SDKs for our API providers and consumers. | ||
|
||
Paths, endpoints, parameters, request bodies, etc., are transformed into methods, functions, types, interfaces, etc., of our desired programming language. | ||
|
||
### Cross-programming language end-to-end type safety | ||
|
||
![OpenAPI tooling](/articles/frontend-loves-openapi/frontend-loves-openapi-tools-sdk-langs.svg) | ||
|
||
Because an OpenAPI document also describes the types and formats of our dataflow, we have the opportunity to translate this to types and interfaces for typed programming languages. The OpenAPI specification is, therefore, a way to create **full cross-programming language end-to-end type safety** between API providers and consumers, just like GraphQL and tRPC give us. | ||
|
||
The OpenAPI code generator community is very diverse, and there is no clear one solution to rule them all. However, https://openapi-generator.tech/ comes pretty close. This flavour of code generator is a Java-based templating engine that supports [all kinds of different API client configurations](https://openapi-generator.tech/docs/generators), even from other programming languages, ranging from TypeScript to PHP and everything in between. It even allows you to generate a whole GraphQL server that resolves to the described API operations. | ||
|
||
There are also generators built on different programming languages. For example, I like [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen/), which is built into TypeScript itself. I can include it as a build tool in front-end projects or repositories and have API client code generation as part of the build process. | ||
|
||
On [OpenAPI.Tools](https://openapi.tools/#sdk), you can discover all sorts of OpenAPI tools and generators to add to and hone in your OpenAPI workflow. | ||
|
||
## Mocking | ||
|
||
When developing clients that are dependent on RESTful APIs, you may face a great deal of challenges communicating with the API provider. | ||
|
||
- The provider and consumer are developed in parallel. | ||
- I need a reliable, easy to influence API for integration testing. | ||
- Connections to the data provider are not optimised, possibly slowing development efficiency. | ||
|
||
In these cases, we want to recreate a separate local API provider we can develop against, a mocking server. For this, we could build one with the information coming from the OpenAPI description. But we don't need to. | ||
|
||
Because Stoplight provides us with a mocking server tool, [Prism](https://github.com/stoplightio/prism). Prism takes an OpenAPI document and creates a fully-fledged local server that listens to requests on described paths and methods, validates the data for the types and formats you send to it and will respond with the correct types and formats. The values of the data respect the types described but will be of a random value. | ||
|
||
[Stoplight Prism](https://github.com/stoplightio/prism) is a Node-based command line tool that you can run via NPM or Docker. It's as easy as running the command with the configuration that ingests the desired OpenAPI document, and within seconds it will start a server to which we can make requests. | ||
|
||
This mocking automation allows teams building API consumers to develop against non-existing API dependencies or to build in parallel with planned API dependencies. It dramatically increases the independence within an API provider/consumer relation. | ||
|
||
## Static Code Analysis | ||
|
||
With the Design First approach, each stakeholder can work or propose alterations to the data flow by editing the OpenAPI document. We could have specific API patterns or best practices in place to ensure a certain degree of quality for our API. | ||
|
||
Analogue to how we would lint our JavaScript code with ESLint, we can analyse our OpenAPI document with [Stoplight Spectral](https://github.com/stoplightio/spectral). This linter validates your OpenAPI document against specific rules. For example, are all paths kebab-cased? Do the examples adhere to the described schema? | ||
|
||
## Automation | ||
|
||
As with every software project, automation can benefit our workflow. | ||
|
||
![OpenAPI tooling](/articles/frontend-loves-openapi/frontend-loves-openapi-tools-automation.svg) | ||
|
||
We already mentioned putting an OpenAPI document in a Git repository to enable **versioning** of our API description. By utilising [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) and adding our OpenAPI document repository to our provider's and consumer's repositories as a submodule, we can have a form of language-agnostic dependency management via Git. Each commit of the provider or consumer repository is tied precisely to a specific commit of the OpenAPI repository. Pull or Merge Requests are linted by CI/CD to check for code quality before merging it into our Single Source of Truth. | ||
|
||
We can automate the generation of API client code at each start of the development environment so that our team is always using the latest version of the described API. Our IDEs immediately notice breaking changes, compilations steps, or CI/CD builds. | ||
|
||
Our development environments include a step that will automatically run a local mocking server, or a shared mocking server is automatically deployed when the main version of the OpenAPI document has changed. | ||
|
||
## Conclusion | ||
|
||
Treating an OpenAPI document as a Single Source of Truth, utilising the right tools and providing automation with those tools gives our teams superpowers. We will spend less time trying to integrate and more time on the things that matter, like User Experiences, for example. | ||
|
||
By using OpenAPI, we create full cross-programming language end-to-end type safety between API providers and consumers. | ||
|
||
In next part of this series, we will take a look of a real-world example of how we can use OpenAPI in a front-end project. |
Oops, something went wrong.