Documenting a RESTful API in Symfony via Swagger version 4.x which is being called officially OpenAPI.
Swagger: an open source API documentation interface.
What is being used?
- Skeleton Symfony project version 5.2.6
Create a new Symfony project via composer by typing the following statement in CMD:
composer create-project symfony/website-skeleton my_project_name
How to do that?
Via the NelmioApiDocBundle bundle, which allows us to generate documentation in the OpenAPI (Swagger) format and provides an interactive environment with the APIs.
What bundles are required?
NelmioApiDocBundle
Install via the command
composer require nelmio/api-doc-bundle
Register the bundle
Done via Flex in Symfony 5 and above, also when the version of the nelmio/api-doc-bundle is below 4.
Register the documentation route
To browse the documentation with Swagger UI, register the following route in config/routes.yaml:
app.swagger_ui:
path: /doc
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger_ui }
Moreover, to get a Jason format of the documentation, we have to added the following route settings:
app.swagger:
path: /doc.json
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger }
NelmioApiDocBundle Configuration file
By default, the file config/packages/nelmio_api_doc.yaml will be generated by Flex. We just have to adapt it to our project.
nelmio_api_doc:
documentation:
info:
title: My App
description: This is an awesome app!
version: 1.0.0
areas:
path_patterns: # an array of regexps
- ^/api(?!/doc$)
Documenting an API
Added the API to the documentation:
The default configuration of the file config/packages/nelmio_api_doc.yaml will require us to add the prefix api/ before the route of the API in order to put it in the documentation. However, we can reconfigure that for displaying all the endpoints without the prefix by set the value of the path_patterns as this: ^/[a-z]
Note: we can keep the Symfony annotation beside the OpenAPI annotation, and all together will be used to generate the required documentation.
Documenting the request:
* @OA\RequestBody(
* description="Creates user and profile at the same time",
* @OA\JsonContent(
* @OA\Property(type="string", property="userID"),
* @OA\Property(type="string", property="password"),
* @OA\Property(type="string", property="userName"),
* @OA\Property(type="string", property="email")
* )
* )
Request Body Object
Describes a single request body. It contains two fields, description, and content.
- Description: may contain a brief description of the request body, or an example.
- Content: refers to the type of the content of the request body. As we see from the provided example, the type of the content of the request body for the endpoint is JsonContent, and to represent that we used the Json Content Object of OpenAPI.
To also represent the fields of the json request we used the Property object.
Documenting the response
We may need to represent the content of the response. For example, to represent a response in Jason format with three fields: status_code, msg, and Data, as the following image shows. And the last field, in return, may contain other Jason data.
* @OA\Response(
* response=200,
* description="Returns the new user's role",
* @OA\JsonContent(
* @OA\Property(type="string", property="status_code"),
* @OA\Property(type="string", property="msg"),
* @OA\Property(type="object", property="Data",
* @OA\Property(type="array", property="roles",
* @OA\Items(example="user")),
* @OA\Property(type="object", property="createdAt")
* )
* )
* )
As we can note from annotations, we represent the json content of the response in the same way we did for the content of the request previously. Except we used Items object in order to represent the array property which is called “roles”. We use the Items object as long as the type of the property is of type Array.
Group specific endpoints together
In OpenAPI we use the Tag object to group specific operations together. When we want to list all the APIs that are related to the user profile, for example, under one title, we can add a tag annotation to each one of them as this:
@OA\Tag(name="UserProfile")
The Tag object takes three fields: name, description, and externalDocs, which is of the type External Documentation Object.
Documenting bearer-authentication API
Some endpoints require bearer authentication because they depend on the token being sent in the header. So, we have to document the api as it requires a bearer token. In order to do that we have first to define a security scheme in the config/packages/nelmio_api_doc.yaml
We added the following part to the file in the documentation section of the file config/packages/nelmio_api_doc.yaml:
components:
securitySchemes:
Bearer:
type: http
scheme: bearer
bearerFormat: JWT
security:
- Bearer: []
In OpenAPI 3.0, Bearer authentication is a security scheme with type: http and scheme: bearer. We first need to define the security scheme under components/securitySchemes, then we use the security keyword to apply this scheme to the desired scope – global (as in the example above) or specific operations.
The optional setting bearerFormat specifies how the bearer token is formatted.
In the example above, Bearer authentication is applied globally to the whole APIs. If we need to apply it to just a few operations, add security on the operation level instead of doing this globally:
paths:
/something:
get:
security:
- bearerAuth: []
However, we could use annotation instead of the previous configuration like this:
@Security(name="Bearer")
Rendering the documentation
Using Angular application:
Displaying the API documentation via a simple Angular project and by reading the response of the route /doc.json and displaying it in an Angular View. That’s done by follow this:
Note: we have to make sure that installed Angular CLI is of version 12 at least.
After creating new angular project by running the command
ng new app_name
Install swagger-ui-dist
npm install swagger-ui-dist --save
Next, we have to include the swagger-ui.css in our project by adding it to the style option in the angular.json besides the required scripts like this:
"styles": [
"node_modules/swagger-ui-dist/swagger-ui.css",
"src/styles.css"
],
"scripts": [
"node_modules/swagger-ui-dist/swagger-ui-bundle.js",
"node_modules/swagger-ui-dist/swagger-ui-standalone-preset.js"
]
app.component.ts in src/app of our project will take this code:
import { Component, OnInit } from '@angular/core';
declare const SwaggerUIBundle: any;
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'swagger-ui-dist-angular';
constructor() { }
ngOnInit(): void {
const ui = SwaggerUIBundle({
dom_id: '#swagger-ui',
layout: 'BaseLayout',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
url: 'http://161.35.29.131/doc.json',
docExpansion: 'none',
operationsSorter: 'alpha'
});
}
}
url option as we can note takes the URL of OpenAPI documentation in the Jason format which is available at the route /doc.json as we mentioned earlier.
Meanwhile, app.component.html in src/app will just take this line:
<div id="swagger-ui"></div>
Finally, running the Angular server and opening the url of the homepage, in our example is localhost:8000/4200, we can see the documentation presented in a formatted style
References
https://symfony.com/doc/current/bundles/NelmioApiDocBundle/index.html
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
https://www.npmjs.com/package/open-swagger-ui
https://stackoverflow.com/questions/44894013/adding-swagger-ui-to-angular-app
Leave a Reply