--- tags: api, pratical, deprecated, swagger --- # Documenting your API: Introduction to Swagger Swagger is an Interface Description Language for describing RESTful APIs expressed using JSON. Infomation about the API and endpoints is stored in a json file, and then then served through our API. To get started with swagger, we will be adding documentation to the [have-i-fed-the-cat-app](https://hackmd.io/k9O1pQE0TuOVN4lcgpAlYQ) we wrote in a previous session. If you don't have a working app handy, then there is an example app you can use [here](https://github.com/MCRcodes/have-i-fed-the-cat-demo). Clone the app and follow the instructions to get it running. You should also install the [OpenAPI Lint](https://marketplace.visualstudio.com/items?itemName=mermade.openapi-lint) extension for VS Code. This will help us spot any issues in our JSON and offer suggestions for the correct format. ## Basic Swagger Template Create a file called `swagger.json` in the root of your project, this is where we will be doing most of the work to write our documentation. ```json= { "swagger":"2.0", "info": { "version": "1.0.0", "title": "", "description": "", "license": { "name": "MIT", "url": "https://opensource.org/licenses/MIT" } }, "basePath": "/", "consumes": [ "application/json" ], "produces": [ "application/json" ], "schemes": [ "http", "https" ], "tags": [], "paths": {}, "definitions": {} } ``` Make sure to fill in the title and the description to suit your app. ## Install Swagger UI To display our swagger documents, we will be using the `swagger-ui-express module`. Install it as a dependency: ```bash= npm i -S swagger-ui-express ``` To use it, will need to require it in our `app.js`, along with our `swagger.json`: ```javascript= // src/app.js const swaggerUi = require('swagger-ui-express'); const swaggerDoc = require('../swagger.json'); ``` Next we will need to create an enpoint to server our swagger docs from: ```javascript= // src/app.js app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc)); ``` Now if we start the app and go to `localhost:3000/api-docs` in the browser: ![](https://i.imgur.com/ZTJ2zRq.png) Now we just need to define our paths. ## Defining Paths and Methods We can do this by adding a `"/cats"` object to the `"paths"` section of our document. Inside there we define the request method like so: ```json= ... "paths": { "/cats": { "post": { "tags": [], "summary": "creates a new cat in the database", "parameters": [ { "name": "PostCatRequest", "in": "body", "description": "post a cat to the database", "schema": {} } ], "responses": { "201": { "description": "cat successfully created", "schema": {} } }, "produces": [ "application/json" ] } } } ... ``` We can see the results when we refresh the browser: ![](https://i.imgur.com/jW4Y6pA.png) ## Tagging the Paths Looks good, but there's a few more details we can add. For starters we can add a tag to our path. This groups paths and helps organize large apps. We define the tag at teh top level of the document like so: ```json= ... "tags": [ { "name": "Cats", "description": "Endpoints for interacting with cats" } ], ... ``` Then just reference the tags in the path object: ```json= ... "/cats": { "post": { "tags": [ "Cats" ], ... ``` Now we can reload and see that our `POST` route is in the `Cats` category: ![](https://i.imgur.com/3QLMJIC.png) ## Adding a Schema You may have noticed the `schema` section in the `path` and `response`. This is where we can define the data that our app consumes and produces. We can do this either in the path or the `definitions` section. As this app is small, we can stick to in-path definition. ```json= ... "schema": { "type": "object", "required": [ "name", "breed", "markings" ], "properties": { "name": { "type": "string" }, "breed": { "type": "string" }, "markings": { "type": "string" } } } ... ``` We will also need to define a schema for the `201` response, we will probably want to reuse this, so lets define a schema in the `definitions` section and use `$ref` to reference it.: ```json= ... "responses": { "201": { "description": "cat successfully created", "schema": { "type": "object", "$ref": "#/definitions/CatObject" } } }, ... "definitions": { "CatObject": { "required": [ "id", "name", "breed", "markings", "lastFed", "createdAt", "updatedAt" ], "properties": { "id":{ "type": "integer" }, "name": { "type": "string" }, "breed": { "type": "string" }, "markings": { "type": "string" }, "lastFed": { "type": "string" }, "createdAt": { "type": "string" }, "updatedAt": { "type": "string" } } } } ``` Now we can refresh the browser and see the results: ![](https://i.imgur.com/FSmkak5.png) ## Adding another method That's the documentation for our `POST /cats` endpoint done. Let's try adding another method to the `/cats` path. We can re-use the schema from out `POST` endpoint, but as we are returning an array, we can wrap it in another schema like so: ```json= ... "get": { "summary": "reads cats from the database", "tags": [ "Cats" ], "parameters": [], "responses": { "200": { "description": "returns array of cats", "schema": { "$ref": "#/definitions/CatObjects" } } } } } ... "definitions": { "CatObjects": { "properties": { "cats": { "type": "array", "items": { "$ref": "#/definitions/CatObject" } } } }, } ``` Another difference from our last endpoint is that this one accepts a `query string`. We can define that in our swagger doc by adding the follwing to the the parameters for this path: ```json= ... "parameters": [ { "name": "name", "type": "string", "in": "query" }, { "name": "breed", "type": "string", "in": "query" }, { "name": "markings", "type": "string", "in": "query" }, { "name": "lastFed", "type": "string", "in": "query" } ], ... ``` If everything is correct, then your docs should contain the following: ![](https://i.imgur.com/MQnk3hP.png) ## Adding another path The remaining endpoints in our api are on the path `/cats/:catId`. We can represent the route parameter as `"/cats/{catId}"`. Add this new path to your swagger document along with a `get` method: ```json= ... "/cats/{catId}": { "get": { "summary": "read a single cat from the database", "tags": [ "Cats" ], "parameters": [ { "in": "path", "type": "number", "name": "catId", "required": true } ], "responses": { "200": { "description": "returns requested cat from database", "schema": { "$ref": "#/definitions/CatObject" } } } } } ... ``` Note that this time we are stating that the `catId` parameter is in the `path` of the request. We are also reusing the `CatObject` schema in our response body. Also note that in this simple app, we're not performing a `null` check before sending the response back to the user. If we were, we would also want to document the possibility of a `404` response. If you refresh the browser, your `GET /cats/{catId}` should look like this: ![](https://i.imgur.com/Pe1SkKq.png) ## Challenge: PATCH /cats/:catId The next method that requires documentation is our `PATCH` controller. This will require a combination of route and body paramaters. You should be able to use what you have already written to generate the correct documentation. Your `PATCH /cats/:catId` documentation should looks like this: ![](https://i.imgur.com/1G8XUMP.png) ## Challenge: DELETE /cats/:catId This one will be very similar to the `PATCH` documentation. It should look like this when you are done: ![](https://i.imgur.com/XYlaJHC.png) ## Challenge: PATCH /feed/:catId Finally, can you add documentation for the `PATCH /feed/:catId` endpoint? It should end up looking like this: ![](https://i.imgur.com/WxziuM7.png) ## Optional: Converting swagger.json to markdown The swagger.json can be used to generate markdown. This is ideal for adding to your README.md, and can be done easily with the `widdershins` module: ```bash= npx widdershins --code swagger.json -o swagger.md ``` This will send the output to a new file called `swagger.md`.