Tutorial

How to Create Documentation for Your REST API with Insomnia

APIDevelopment

The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

Introduction

In this tutorial, you will document your API using the OpenAPI specification (v3). An OpenAPI file is a JSON or YAML file that follows the OpenAPI specification. This specification defines what fields your JSON/YAML file must contain and how it will be reflected on the documentation service you’ll use to host it. Many services support OpenAPI, so you can pick and choose, or even use multiple services, without having to change your API documentation’s format.

To create the documentation, you’ll use Insomnia, a free and open-source application that allows you to test your API and design the documentation with a real-time side-by-side preview. Insomnia doesn’t support JSON, but it does make it easy to write YAML. YAML is a good choice for API documentation because these documents can get very large, and a JSON document would get cluttered and hard to read.

Finally, you’ll host the API documentation with Redoc, an open-source application used by many companies. Redoc takes the OpenAPI document you generated and gives you an HTML page that displays a nice-looking and interactive version of your documentation. You’ll also deploy your Redoc generated site to GitHub Pages, which is a free website hosting solution by GitHub.

In this tutorial, you will learn more about OpenAPI, document your API according to the OpenAPI Spec in Insomnia, and host this documentation on GitHub Pages with Redoc.

Prerequisites

To follow this tutorial, you will need:

Step 1 — Understanding Your API

In this step, you’ll note the routes your API accepts and their relevant parameters and responses. Since you’ll be documenting the API for others, and because you may also refer back to this documentation in the future, it’s important to note everything you need to document. OpenAPI allows you to define request bodies, headers, cookies, and even possible responses for each API route.

This tutorial will use the JSON Placeholder API, which is a free mock API. Since this API is quite large, you will only be documenting the /posts section in this tutorial.

The below table shows the method, route path, and description of each of the five routes you will document in this tutorial. Making a table or something similar is helpful so that you don’t forget any route (which can happen if the API is big).

Method Route Description
GET /posts Get all posts
GET /posts/:id Get a single post
POST /posts Create a post
PUT/PATCH /posts/:id Update a post
DELETE /posts/:id Delete a post

Now that you know what your API can do, it’s time to begin documenting it with Insomnia.

Step 2 — Creating an Insomnia Project

In this step, you’ll create an Insomnia project. An Insomnia project contains the OpenAPI document, any tests you write for your API, and any requests you’ve created. The interface is split into three tabs: Design, Test, and Debug. You’ll focus on the design tab for this tutorial.

Open the Insomnia app and go to your dashboard. Create a new Design Document by clicking the Create button on the top right of the Insomnia window and give it a name. For this tutorial, you can use json-placeholder-docs.yaml.

Note: YAML by design only accepts spaces as indentation. Insomnia, however, indents with tabs by default. This will be fixed in a later update, but for now, open your Preferences by clicking the cogwheel icon on the top right, or by pressing Ctrl/Cmd + ,. In the Font section, uncheck Indent with Tabs and close the Preferences window. This will make Insomnia use spaces instead of tabs.

You should now see three panes, as shown in the following screenshot below. The first pane shows an overview of your document, such as the routes of your API and components you’ve defined (you’ll learn more about those later). The middle pane contains the code editor that you’ll use to write the OpenAPI document in YAML. This editor also detects errors automatically and notifies you of them at the bottom. Finally, the last pane on the right is a real-time preview of the document. You’ll see an error because you still have to tell Insomnia which version of OpenAPI you’ll be using.

Screenshot of Insomnia showing three panes. The first two panes are blank. The third pane on the right shows an error: "Unable to render this defintion".

In the code editor of the Design tab, add the line: openapi: 3.0.3. This indicates the version of the OpenAPI spec you will be using. At the time of writing, the latest version is 3.0.3. Feel free to change this to a later version if you’d like.

Your screen should look similar to this:

Screenshot of Insomnia showing one line added to the center pane, which is the code editor.

Now that you’re familiar with the Insomnia interface, you can begin writing your documentation.

Step 3 — Getting Started With the OpenAPI Specification

In this step, you’ll learn more about the OpenAPI Specification. An API Specification can be a JSON or YAML file, but Insomnia only supports YAML. It should have a key called openapi that specifies the version of the OpenAPI Specficiation you’re using.

According to the specification, here are the fields that can be present at the root of the document:

Name Type Description
openapi string REQUIRED. Version of the OpenAPI schema.
info Info Object REQUIRED. An object containing information about the API.
servers Array of Server Objects An array containing objects that provide connectivity options to an API server.
paths Paths objects REQUIRED. An object containing the routes provided by the API, methods, request-bodies, parameters and responses. This is the most important part of the document.
components Components Object Contains reusable components, meant to reduce file size and keep the docs clean.
security Array of Security Objects Contains a list of authentication mechanisms for the API. Outside the scope of this tutorial.
externalDocs External Documentation Object Contains any external documentation for the API

Don’t worry if this is too much to take in. You’ll be diving deeper into each property, except for security, since that’s outside the scope of this tutorial. The security field defines authentication methods (e.g., username/password, JSON Web Token, or oauth) for a route, but JSONPlaceholder doesn’t have any authentication features.

Step 4 — Adding the info Object

In this step, you’ll use the table from Step 1 to begin write your API’s documentation using Insomnia. You’ll start with the info object.

The info object contains information about the API you’re documenting. This includes things like the title, version of the API, the API’s description, links to its knowledge base (documentation), and its terms-of-service (tos).

According to the specification, this is what an info object should look like:

Name Type Description
title string REQUIRED. The title of the API.
description string A short description of the API. Markdown can be used here.
termsOfService string A URL to the Terms of Service for the API.
contact Contact Object The contact information for the exposed API.
license License Object The license information for the exposed API.
version string REQUIRED. The version of the documentation, not the OpenAPI spec.

The info field has two required properties: the title of the document and the version of the documentation, which should be equal to the version of your API application. The other fields are present for informing the user about your API.

Now you will add an info object to your documentation using the three most-used fields: title, description, and version. In the Insomnia app, add the following YAML code to Design tab editor:

info:
  title: JSONPlaceholder
  description: Free fake API for testing and prototyping.
  version: 0.1.0

This is a random version number since JSONPlaceholder doesn’t expose a version number. Feel free to add any other fields to the info object, following the specification from the previous step.

Warning: YAML is very picky about its indentation. It has to be indented with spaces, and the indent size must be consistent throughout the document.

Now that you’ve added the info object with basic information about your API, you’ll add the next object: externalDocs.

Step 5 — Adding the externalDocs Object

In this step, you will add the externalDocs object. This object contains the link to any other documentation the API might have. An OpenAPI document just defines any routes your API has along with its parameters and responses, so it is usually used as a reference. It is recommended to include separate, human-generated docs that explain each action and guides the user. In JSONPlaceholder’s case, there is a guide.

According to the specification, here’s what the externalDocs object should look like:

Field Name Type Description
description string A short description of the target documentation. Markdown can be used.
url string REQUIRED. The URL for the target documentation.

In your YAML document, add an externalDocs object that points to JSONPlaceholder’s guide:

externalDocs:
  description: "JSONPlaceholder's guide"
  url: https://jsonplaceholder.typicode.com/guide

You should see the changes reflected in the preview pane on the right side of Insomnia.

Screenshot of Insomnia's preview, showing **JSONPlaceholder**.

You have now linked to external documentation for your API. Next, you’ll add the servers array.

Step 6 — Adding the servers Array

In this step, you’ll add the servers array, which contains any URLs that the API will be hosted at. The documentation you’re creating will be hosted on a different domain from the placeholder API (that is, your documentation will not be hosted on jsonplaceholder.typicode.com). Because of this, you can’t implicitly get the URL for the Try It Out buttons next to the API routes shown in the Insomnia preview.

To fix this, OpenAPI provides a servers field. Add the following lines to your YAML document:

servers:
- url: https://jsonplaceholder.typicode.com
  description: JSONPlaceholder

With that, you now have a way to call the API.

Step 7 — Adding the paths Object

In this step, you will add the paths object, which is the heart of your documentation. This object contains all of the routes that are provided by the API. It also contains any parameters, the method, the request body, and all responses of the route.

Each key of the paths object will be a route (/posts) and the value will be the Path Item object.

According to the OpenAPI specification, this is what the Path Item object will look like:

Name Type Description
summary string An optional summary of this route.
description string An optional description of what the route can do.
get/post/put/patch/delete/etc Operation Object A definition of an operation (method) on this route.
servers Array of Server Objects An alternative server array to service all operations in this path.
parameters An array of Parameter Object Parameters that are applicable for all operations on this path. These parameters can be on the querystring, header, cookie, or the path itself.

The Path Item object has a number of fields. The summary and description fields, as their names suggest, provide a short summary and longer description of the path. The servers object is the same as the one in the main OpenAPI document. It defines alternative servers. The parameters object defines any path or query parameters for that path. Each Path Item object can have an operation object. The operation object documents an HTTP method that can be used on this API route.

The operation object has many items, but for this tutorial, you’ll focus on a smaller set:

Name Type Description
tags Array of strings A list of tags for API documentation control. Tags can be used for grouping similar routes.
summary string A short summary of what the operation does.
description string A description of the operation. Markdown can be used here.
externalDocs External Documentation Object Additional external documentation for this operation. Same as externalDocs on the main object.
parameters Array of Parameter Objects Same as parameters in the Path Item object.
requestBody Request Body Object The body of the request. This can NOT be used when the method GET or DELETE.
responses Responses Object REQUIRED. The list of possible responses returned by the API for this operation.

The tags property groups similar paths. Paths with the same tag will end up in one group. The summary and description fields are the same as the ones in the path object. They allow you to add a short summary and a longer description, respectively. The externalDocs property is the same as that in the main document: it allows you to define any external documentation for that operation.

The parameters object is the same as the one in the path object. It allows you to define path, query, header, or cookie parameters that have to be sent with the request. The requestBody also allows you to define parameters, but in the body of the request. This requestBody field is only available in POST, PUT and PATCH requests, as defined in the HTTP/1.1 protocol, RFC7231.

The /posts Route

Now you will document an API route by creating an object in the paths object. First, you’ll document the /posts route. Begin by adding these lines to your YAML document:

paths:
  "/posts":

Note: /posts is in quotes because it contains special symbols (/). This is required by YAML so it doesn’t misinterpret the line.

Next, you need to add a field whose key will be the HTTP method, and whose value will be the Path Item object. Document the GET /posts route, which returns an array of all posts, by adding the highlighted lines:

paths:
  "/posts":
    get:
      tags: ["posts"]
      summary: Returns all posts.

The tags field groups similar operations together. (Notice how the accordion in the preview is called posts.)

Next, document the responses one can get back. The only response you’ll get from this API is a 200 response containing an array of all posts.

An example post that can be returned by JSONPlaceholder will look like this:

{
  "userId": 1,
  "id": 1,
  "title": "A post's title",
  "body": "The post's content"
}

Since you’ll be reusing this pretty frequently, you can create a reusable component for this post. This can be done using the components object. You can define the post as a schema in the schemas object, which will be inside the components object. This schema is similar to the schema in a JSON Schema file.

Add the post schema to your YAML file. Please note that the components object must be placed in the root of the document (without any indentation), not in the paths object.

components:
  schemas:
    post:
      type: object
      properties:
        id:
          type: number
          description: ID of the post
        title:
          type: string
          description: Title of the post
        body:
          type: string
          description: Body of the post
        userId:
          type: number
          description: ID of the user who created the post

The above schema is an object, denoted by type: object. It has four properties: id, title, body, and userId. That is how a schema is defined. Now you can use $ref in any object to reference this schema. This is defined as per the specification for URI syntax, RFC3986.

Now that you have the schema, you can add the responses object. This object has items whose value is the status code returned, or default, to catch all other statuses, and the value is a response object. This object contains the description of the response, any headers that are returned, and the response body, along with the Content-Type in the content object.

Add the responses object to the get operation of the /posts path by copying the highlighted lines:

paths:
  "/posts":
    get:
      tags: ["posts"]
      summary: Returns all posts.
      responses:
        "200": # 200 Status Code
          description: All went well
          content:
            application/json: # Reponse is returned in JSON
              schema:

Be sure to enclose 200 in quotes to make it a string and not a number.

Here, you’re defining a response that gets returned with the 200 status code, and has a Content-Type header of application/json. In this schema object, you need to pass a reference to the post schema you just created. That can be done with $ref.

Aside from schema, the application/json object can also contain any examples you wish to give.

For now, add a reference to the post schema in schema.

$ref: "#/components/schemas/post"

# refers to the root of the document. Since the post schema is located in components/schemas/post, that’s how you should write it. And since # is a reserved symbol in YAML, you need to enclose the ref in quotes.

Your Insomnia Design tab should look similar to this:

Screenshot of Insomnia showing YAML in the center pane and a preview in the right pane.

You can see that insomnia has rendered a preview of your document. The /posts route has been grouped into a posts section, because of the tag, and the correct response is also showing, as you defined in the schema. The same content-type you defined and the same schema you defined are previewed on the right.

You can try changing something, like the tag or the responses of the path, and see it update in real time. Be sure to change it back after you’re done.

Note: Press the Try It Out button in the Path operation in the preview, and then the Execute button to call the JSONPlaceholder API and receive a response.

With the GET route documented, it’s time to document the POST /posts route. This will be quite similar to the previous operation, but this time, it will be a POST request, hence the object’s key is post (highlighted below). Add the following lines to your YAML file:

paths:
  "/posts":
    # ...
    post:
      tags: ["posts"]
      summary: Create a new post
      responses:
        "200":
          description: A post was created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"

You’ve just defined another operation. This time, it is a POST request, with the same tag, so it gets grouped along with the GET request you defined earlier. The response also has the same schema since that is what will be returned by JSONPlaceHolder.

There’s still one thing missing: the post method also accepts a request body. It hasn’t been documented yet, so add the requestBody object to the post operation. The request body is similar to the response object. There’s a description and content field, which are the same as the response object, and there’s also a required field, which is a boolean. This field governs whether a body is required for this request or not. In this case, it is, so add the requestBody object to your operation.

paths:
  "/posts":
    # ...
    post:
      tags: ["posts"]
      summary: Create a new post
      requestBody:
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/post"
    required: true
      responses:
        "200":
          description: A post was created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"

At this point, your paths object should look like this:

paths:
  "/posts":
    get:
      tags: ["posts"]
      summary: Returns all posts
      responses:
        "200":
          description: All went well
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"
    post:
      tags: ["posts"]
      summary: Create a new post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/post"
        required: true
      responses:
        "200":
          description: A post was created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"

In this section, you documented the GET and POST operations available in the /posts route. Next, you’ll document the /posts/:id route, which is used to read, modify, or delete a single post.

The /posts/:id Route

Next, you’ll document the /posts/:id route. This route has three operations: GET, PUT, and DELETE. They get a single post, update a post, and delete a post. :id is a dynamic parameter that can be a number (for example: /posts/1, /posts/2, etc.). In OpenAPI, this is denoted as {id}, as shown in the following example:

paths:
  "/posts":
  # ...
  "/posts/{id}":
  # TODO

The in property defines where the parameter will be placed. This can be in the query string, in the cookie, in the header, or as a part of the path itself. The description is a description of the parameter. The required field is a boolean that indicates if the parameter is required. In the case of path parameters, required has to be true, since the parameter is a part of the path itself.

Path parameters are special, so they’re defined in the path using braces ({}). The name of the parameter is enclosed in the braces and must match the name in the name field. First, you need to define id as a parameter object in the parameters array. Here’s how you’ll do it:

paths:
  "/posts/{id}":
    parameters:
    - name: id # Must be same as the value in the {}.
      in: path
      description: ID of the post
      # Since this is in the path, the parameter HAS to be required
      required: true
      # Defining the type of the parameter
      schema:
        # In this case, it is just a string
        type: string

Be sure to include the hyphen (-), otherwise the parameters array would become an object

The last thing to document are the operations and their responses and request bodies, if they have any. This is similar to what you did in the previous section.

First, add the GET /posts/:id operation, which gets a single post.

get:
  tags: ["post"]
  summary: Get a single post
  responses:
    "200":
      description: All went well
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/post"
    "404":
      description: Post not found
      content:
        application/json:
          schema:
            type: object
            properties: {}

Notice that this time, there is a 404 response. This is because the GET request can return a 404 error if the post is not found. The properties: {} in the above code is how you’d define an empty object in YAML.

Next, add the PUT /posts/:id operation, which updates a post. This method combines the GET and POST methods above, since it has both a requestBody and a 404 response.

put:
  tags: ["post"]
  summary: Update a post
  requestBody:
    content:
      application/json:
        schema:
          $ref: "#/components/schemas/post"
    required: true
  responses:
    "200":
      description: All went well
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/post"
    "404":
      description: Post not found
      content:
        application/json:
          schema:
            type: object
            properties: {}

JSONPlaceholder doesn’t really validate the data you send it, so there is no 400 or 422 response, but if the API you’re documenting does something like that (which it should), be sure to document those responses as well. To avoid repeating yourself, you can create response components, as you did in the previous section.

And finally, add the DELETE /posts/:id operation, which deletes a post. This is the same as the GET method, since it returns a 404, but this time, the operation is delete.

delete:
  tags: ["post"]
  summary: Delete a post
  responses:
    "200":
      description: All went well
      content:
        application/json:
          schema:
            type: object
            properties: {}
    "404":
      description: Post not found
      content:
        application/json:
          schema:
            type: object
            properties: {}

Note that the DELETE method only returns an empty object ({}), even on a 200 response.

And with that, you’ve successfully documented the /posts route of JSONPlaceholder. Here’s the full YAML document.

openapi: 3.0.3

info:
  title: JSONPlaceholder
  description: Free fake API for testing and prototyping.
  version: 0.1.0

externalDocs:
  description: "JSONPlaceholder's guide"
  url: https://jsonplaceholder.typicode.com/guide

servers:
- url: https://jsonplaceholder.typicode.com
  description: JSONPlaceholder

paths:
  "/posts":
    get:
      tags: ["posts"]
      summary: Returns all posts
      responses:
        "200":
          description: All went well
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"
    post:
      tags: ["posts"]
      summary: Create a new post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/post"
        required: true
      responses:
        "200":
          description: A post was created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"
  "/posts/{id}":
    parameters:
    - name: id # Must be same as the value in the {}.
      # Location of the parameter.
      # Can be `path`, `cookie`, `query` or `header`
      in: path
      description: ID of the post
      # Since this is in the path, the parameter HAS to be required
      required: true
      # Defining the type of the parameter
      schema:
        # In this case, it is just a string
        type: string
    get:
      tags: ["post"]
      summary: Get a single post
      responses:
        "200":
          description: All went well
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"
        # But this time, you can also get a 404 response,
        # which is an empty JSON object.
        "404":
          description: Post not found
          content:
            application/json:
              schema:
                type: object
                properties: {}
    put:
      tags: ["post"]
      summary: Update a post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/post"
        required: true
      responses:
        "200":
          description: All went well
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/post"
        "404":
          description: Post not found
          content:
            application/json:
              schema:
                type: object
                properties: {}
    delete:
      tags: ["post"]
      summary: Delete a post
      responses:
        "200":
          description: All went well
          content:
            application/json:
              schema:
                type: object
                properties: {}
        # But this time, you can also get a 404 response,
        # which is an empty JSON object.
        "404":
          description: Post not found
          content:
            application/json:
              schema:
                type: object
                properties: {}

components:
  schemas:
    post:
      type: object
      properties:
        id:
          type: number
          description: ID of the post
        title:
          type: string
          description: Title of the post
        body:
          type: string
          description: Body of the post
        userId:
          type: number
          description: ID of the user who created the post

In the above document, you’ve documented all /posts routes provided by JSONPlaceholder, and you’ve also covered all HTTP methods that are supported. you’ve also learned about parameters, request bodies, and different responses.

With the OpenAPI document complete, the next step is to make it available to users.

Step 8 — Using Redoc to Display API Documentation

While Insomnia does have a nice-looking Preview pane, you can’t expect all of your users to have Insomnia installed, so you’ll use Redoc to display the OpenAPI YAML file in a nice readable way.

To build Redoc, you need to have NodeJS installed. (Please note that you don’t need to know any NodeJS or JavaScript to build Redoc.)

Create a new folder anywhere on your computer. You’ll be building Redoc in this folder and deploy it to GitHub.

First, you’ll need to save your current OpenAPI document to this folder. Create a new file called openapi.yaml in the current folder and copy-paste the contents in Insomnia’s Design tab to this file. Redoc can now use this file to generate your API documentation

Next, open a terminal in that folder and run the below command to build Redoc.

  • npx redoc-cli --output index.html bundle openapi.yaml

npx is the NPM (Node Package Manager)’s CLI tool to fetch a CLI-installable package and run it. This allows you to run redoc-cli without actually installing it to your global $PATH. Instead, it will be available via npx. Be sure to type y if asked to install redoc-cli or not. Next, you’re telling Redoc to bundle the openapi.yaml file you just created into a zero-dependency HTML file, which in this case, will be index.html, since you passed the --output flag.

This should create a new file called index.html in that directory. This file is the documentation, powered by Redoc. Open the file in your browser and inspect it to make sure that every route you’ve defined is covered.

Screenshot of the Redoc documentation displayed in a browser.

Now that you have your documentation site generated, it’s time to make it available to others using GitHub Pages.

Step 9 — Deploying to GitHub Pages

Now that you have a documentation website, you can use GitHub Pages to deploy it for the world to see. As part of the prerequisites, you created a new GitHub Repository. Copy the clone URL shown in Quick Setup. You’ll use the git command to push your openapi.yaml and index.html files to GitHub. Be sure to run the below commands in the folder that contains these two files.

First, you’ll initialize the git repository and commit all of your files:

  • git init
  • git add .
  • git commit -m "First commit" # Feel free to change this message

Next, you’ll deploy your changes to GitHub. First, you need to tell git that your GitHub repository should be the remote repository. The remote repository is usually stored under the name origin.

  • git remote add origin YOUR_GITHUB_REPO_URL

And finally, push your changes to GitHub with this command:

  • git push origin main

Note: Git has changed the name of the default branch from master to main, so main is used in the command above. Feel free to replace main with master if you like.

Refresh GitHub, and you should see your two files there.

Screenshot of two files on GitHub

Now you need to enable GitHub Pages. Go to your repository’s settings and click the Pages button on the Sidebar. Change the source branch to your default branch (usually main or master) and the folder to / (root) and click Save.

Finally, visit https://your_username.github.io/your_repo_name, you should see the Redoc page you’ve just created. You can view my version published to GitHub Pages here.

With that, your API is now available for anyone to see with the URL above.

Conclusion

In this tutorial, you documented the /posts route of the JSONPlaceholder API. You documented path parameters as well as request bodies and possible responses. You have also learned to reduce boilerplate using reusable components. Feel free to check out the source code and the live version.

As a next step, try to document the other routes that JSONPlaceholder offers (e.g., /users), or try this out with your own API. You can go forward from here by using tools like Docasaurus to add documentation that explains and guides the user. Remember to keep your API Spec DRY and easy to read so you can make changes to it in the future. Insomnia also has other features, like the ability to test and debug your API, so be sure to check those out by reading the documentation.

Creative Commons License