- Published on
Simple API Design with OpenAPI - 2
- Authors
- Name
- Christian Miño
- @Christian_sm91
The OpenAPI Specification is a standardized language for describing HTTP APIs, enabling developers to define their APIs in a consistent and easily understandable manner. It serves as a powerful tool that allows users to comprehend API functionalities, configure infrastructure, generate client code, and create test cases effectively. By adopting OpenAPI, you gain control over your APIs, facilitating seamless communication within and beyond your organization while comprehending the entire API lifecycle.
Moreover, OpenAPI forms an extensive ecosystem of tools that contribute to the development of robust and interactive RESTful API services. Building upon the foundation laid in the previous article of this series, 'Simple API Design With OpenAPI,' we now proceed to implement our users' service. In this article, we will delve into the user service model and explore its implementation further.
Following the previous article of the this serie of Simple API Design With OpenAPI in this article we are going to implement our users service. Let's take a look at our users service Model
AML specifications can sometimes be overwhelming due to the numerous properties they may contain, making it challenging to remember all of them. However, YAML files have become the preferred choice for writing specifications in a declarative manner nowadays. They are widely used in popular software tools like Kubernetes, Ansible, Docker Compose, and more.
Thankfully, there is always documentation available to guide us on how to write these files effectively. This documentation proves to be invaluable when navigating through the complexities of YAML specifications.
To begin with our OpenAPI specification, let's start by creating the basic structure. I often use comments to enhance the file's understandability and to serve as a helpful guide while working on it. If you get lost somewhere or want to look at the final file this is the GitHub repo https://github.com/Grifo89/users-openapi
OAS Skeleton 🦴🦴
openapi: 3.0.3
#-------------------------------
# METADATA
#-------------------------------
info:
servers:
tags:
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
# PATHS
#-------------------------------
paths:
#-------------------------------
# SECURITY
#-------------------------------
security:
Those are the principal item object of our spec, first of all let's write the Metadata
.
Metadata 📝
openapi: 3.0.3
#-------------------------------
# METADATA
#-------------------------------
info:
title: User API design
description: Users API service specification
version: 1.0.0
servers:
- url: http://localhost:8000
description: Local development server
tags:
- name: Users
description: User service
The above item properties are the ones I called metadata, this includes the info property where we specified the title, we give a description and the API version. This section is mandatory for a valid OAS (OpenAPI Specification).
Components 🧩🧩
Next, it's a good practice to follow for the components
section. In this section we're going to declare reusable components whithin our OAS like endpoints `, schemas, errors, request bodies, securitySchemes and examples. These are the ones we are going to declare but there are many other you can find at the documentation
Components
section could be one of the lagest in the OAS so we are going to break it down.
schemas
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
schemas:
Error:
type: object
properties:
message:
type: string
required:
- message
User:
type: object
required:
- name
- emailAdress
- roles
properties:
name:
type: string
emailAdress:
type: string
role:
type: array
items:
type: string
enum:
- admin
- customer
- developer
userCreated:
type: object
allOf:
- $ref: "#/components/schemas/User"
properties:
id:
type: string
format: uuid
#-------------------------------
The format to specify a schema in the components sections is the following although you can find more item properties at the documentation
schemas:
schemaName:
type: object # Usually
properties:
property1:
type: property1Type
property2:
type: property2Type
Responses
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
NotFound:
description: The specified resource was not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
UnprocessableEntity:
description: The payload contains invalid values.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
The format is the following:
responseName:
description: Description of this reponse
content:
application/json: # There are many types of `, in this case is a json
schema:
# This follows the schema object we reviwed before.
Request Bodies
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
requestBodies:
UserCreate:
description: Registers a new user
required: true
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/User"
- type: object
properties:
password:
type: string
format: password
UserUpdate:
description: Updates an active user
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/User"
#-------------------------------
The format is the same as the responses
one.
Security schemes
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
securitySchemes:
openId:
type: openIdConnect
openIdConnectUrl: https://coffeemesh-dev.eu.auth0.com/.well-known/openid-configuration
oauth2:
type: oauth2
flows:
clientCredentials:
tokenUrl: https://coffeemesh-dev.eu.auth0.com/oauth/token
scopes: {}
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
Security schemes are specific to each authentication and authorization flow, but in the lastest version the securitySchemes
object follow a pattern; the above is an example but if you want more information you can relay on the documentation.
Parameters
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
parameters:
UserId:
name: id
in: path
required: true
schema:
type: string
format: uuid
The item object within the paramaters that defines the parameter type is the item object in
which could receives either, path
, query
or cookie
Finally to finish the component object the last item object is examples
, this porperty is required in order to make funtional our mock server that we're going to run afterwards to implement our TDD (Test Driven Design) and work in parallel with the Front-End team.
#-------------------------------
# COMPONENTS
#-------------------------------
components:
#-------------------------------
examples:
arrayOfusers:
summary: Array of users
value:
- id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user1"
emailAdress: "test@email.com"
roles: ["admin"]
- id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user2"
emailAdress: "test2@email.com"
roles: ["customer"]
- id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user3"
emailAdress: "test3@email.com"
roles: ["admin"]
- id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user4"
emailAdress: "test4@email.com"
roles: ["developer"]
- id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user5"
emailAdress: "test5@email.com"
roles: ["developer"]
sigleUser:
summary: Single User
value:
id: "82195850-cec4-42bd-b216-3cfa598aa4af"
name: "user5"
emailAdress: "test5@email.com"
roles: ["developer"]
Huug! it was a lot, as you can notice there are a lot of properties but don't be afraid, you don't need to remember each one, you just need to know the general structure and de minimum required structure.
Paths 🛣️
paths
is the object where we specified the endpoints URIs, each URI is a sub item object and each URI has a parameters
, tags
, summary
, and examples
.
#-------------------------------
# PATHS
#-------------------------------
paths:
#-------------------------------
/users:
get:
tags:
- Users
operationId: getUsers
summary: List of users
responses:
'200':
description: Successfull operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
examples:
arrayOfusers:
$ref: "#/components/examples/arrayOfusers"
'404':
$ref: '#/components/`/NotFound'
'422':
$ref: '#/components/`/UnprocessableEntity'
post:
tags:
- Users
operationId: createUser
summary: Creates a new User
requestBody:
required: true
$ref: "#/components/requestBodies/UserCreate"
responses:
'201':
description: User Created
content:
application/json:
schema:
$ref: '#/components/schemas/userCreated'
examples:
singleUsers:
$ref: "#/components/examples/sigleUser"
'404':
$ref: '#/components/`/NotFound'
'422':
$ref: '#/components/`/UnprocessableEntity'
#-------------------------------
/users/{id}:
get:
parameters:
- $ref: '#/components/parameters/UserId'
tags:
- Users
operationId: getUser
summary: Fetch user by id
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/userCreated'
examples:
singleUsers:
$ref: "#/components/examples/sigleUser"
'404':
$ref: '#/components/`/NotFound'
'422':
$ref: '#/components/`/UnprocessableEntity'
put:
parameters:
- $ref: '#/components/parameters/UserId'
tags:
- Users
summary: Update active user
operationId: updateUser
requestBody:
required: true
$ref: "#/components/requestBodies/UserCreate"
responses:
'201':
description: User Updated
content:
application/json:
schema:
$ref: '#/components/schemas/userCreated'
examples:
singleUsers:
$ref: "#/components/examples/sigleUser"
'404':
$ref: '#/components/`/NotFound'
'422':
$ref: '#/components/`/UnprocessableEntity'
delete:
parameters:
- $ref: '#/components/parameters/UserId'
tags:
- Users
operationId: deleteUser
responses:
'204':
description: The resource was deleted successfully
The operationId
is a property that adds an alias to each endpoint to point to later in other objects like security
.
Security 🔒🔐
#-------------------------------
# SECURITY
#-------------------------------
security:
- oauth2:
- deleteUser
- updateUser
- getUser
- getUsers
- createUser
- openId:
- deleteUser
- updateUser
- getUser
- getUsers
- createUser
Conclusion 🌟🔚🎉
I'm sure you're thinking it's been a lot just for one API service, but this OAS is supposed to be specify for each service and this spec was thought taking into account microservice architecture, that means, each service could be isolated to each other.
In conclusion, implementing the OpenAPI Specification (OAS) for our service has been a transformative experience. The comprehensive documentation and standardized interface have not only streamlined our development process but also fostered better collaboration between teams. With a clear understanding of the API contract, both internal and external stakeholders can now communicate effectively, leading to faster integration and reduced time-to-market for new features. As we embrace the benefits of this well-defined specification, we are confident in our ability to adapt and scale our service to meet the ever-changing needs of our users and industry. By adhering to the OAS principles, we ensure a robust and future-proof foundation for our service, enabling us to deliver high-quality solutions and exceptional experiences to our customers. Moving forward, we remain committed to refining and expanding our OAS to drive continuous improvement and innovation in our product offerings. 🌟🚀