Get the most out of Insomnia to effectively test your API

TL;DR Don’t just write requests in Insomnia, use some of its best capabilities to keep your stuff maintainable and be more efficient.

Stephane Geraud
TheFork Engineering Blog
10 min readJul 2, 2021

--

We won’t talk about sleep disorders. Grab a coffee if you need some REST. Sorry for this bad joke.

What is Insomnia?

Insomnia is a great software to easily send requests. It’s the perfect tool to test your REST or GraphQL APIs.

It has been acquired by Kong in late 2019 and is available on Linux, macOS and Windows.

In this article, I would like to show you how to get the best of it. This will make you gain some time, avoid repeating yourself and keep your workspace maintainable.

Disclaimer: I will show the keyboard shortcuts for macOS version. If you’re using another operating system, please find the corresponding shortcuts in the Keyboard tab of the Insomnia Preferences dialog.

Staying organized

The more you will work with Insomnia, the more you will have requests. The first thing to do to ease your work and maintainability is to keep things organized. Insomnia offers two ways to do it: request collections and folders. Both are useful and can be combined.

A screenshot of the Dashboard view, listing a few Request Collections
The Dashboard view lists all your Request Collections

Request Collections (or Workspaces)

Request Collections are the top level unit of organization. You can start with a single one, but at the end you will find it easier to have a collection per domain, application or even microservice. One of the benefit is to make it quicker to export a subset of the requests.

A hint to find out how you should use request collections is to keep in mind that variables and environments will be stored at request collection-level. We will talk about variables and environments later in this article.

Folders

Folders are the second level unit to organize your requests. If you are using a single workspace for your entire organization, you can put the requests for each of your products into a dedicated folder. If you are using a workspace per product, you can create a folder to group requests of the same domain.

Requests of a folder will continue to have access to the environment variables of the collection they are part of.

An example of requests organization in Insomnia sidebar
Insomnia sidebar — An example of how to arrange the requests for an Elasticsearch cluster

Template Tags

Template tags act like placeholders and their value results from the execution of a function, should it be a predefined one or your own one.

They can be used almost everywhere in Insomnia: request body, auth, query parameters, headers…

Use ^Space to open the template tags autocomplete palette, or start typing {% followed by a space character or the tag name.

Here are some examples of the most useful template tags:

  • UUID: generates a v1 or v4 UUID, often used as a unique resource identifier in request body ;
  • Timestamp: outputs the current date and time in various formats — it even accepts Moment.js formatting options so you can select Custom format, click the tag and type something like YYYY-MM-DD in the edit window ;
  • Cookie: gets the value from the cookie jar, a feature of Insomnia to manage cookies.

If the provided tags are not enough for you, consider writing your own plugin to match your needs. It’s pretty straightforward if you are familiar with Node.js modules.

Environment

In Insomnia, environments are a place to store data as a JSON object. You can modify them with the shortcut ⌘E.

Variables are defined as properties (or sub properties thanks to JSON format) of an environment: the base environment or a sub environment.

Variables will be read directly as a property of the current environment. Use ^Space to open the variables autocomplete palette, or start typing {{ followed by a space character or the variable name. You can use variables anytime a template tags is allowed.

Base Environment

This is the root environment containing data available to the current request collection, regarding what is the selected sub environment. There is only one base environment per workspace.

You should put in this base environment every data that are not dependent to the execution environment (development, staging, production…).

For example, you could have the following base environment:

{
"serviceName": "foo-service",
"healthCheckPath": "/monitor"
}

If the service name and the monitoring endpoint are the same whether you reach your staging or production website, then the base environment is the perfect place to store those variables.

Sub Environment

A sub environment behaves exactly like the base environment, with the exception you can create many of them. However, you can have only one active at the same time.

You will usually create one per execution environment: one for local development, one for staging, one for production, and so on.

The base environment data will be merged with the active sub environment data, with the priority for the later. Other inactive sub environments will be ignored.

Let’s say we have the following base environment:

{
"foo": 0,
"path": "/"
}

the following staging environment:

{
"foo": 1,
"name": "staging"
}

and the following production environment:

{
"foo": 2,
"name": "production"
}

If you select the production sub environment, then these variables will available:

{
"foo": 2,
"name": "production",
"path": "/"
}

Easy, right? Now let’s go a step further!

Using variables in environment definitions

— Wait, what? Didn’t you say the goal of an environment was to create variables?
— That’s correct. But here comes the magic part of Insomnia!

Because environment variables will be evaluated dynamically when they are used (mainly when a request is executed), nothing prevents you from using a variable you know will be defined at a point of time.

Let’s create a basic sub environment:

{
"protocol": "https",
"domain": "example.com"
}

You need to close the environment definition window and make this sub environment.

Now we will edit the base environment, add a name property to the object, and thanks to the magic of template tags (⌘^Space), you can use the variables in your environment definition:

An example of environment making use of template tags
Using template tags in an environment definition

Pretty cool, right? Look at the example on the screenshot how we made use of variables coming from both the base environment and production environment.

If you select a sub environment that do not define the required properties, Insomnia will warn you by displaying the template tags in red:

An example of environment making use of template tags but the variable does not exist
Variables “protocol” and “domain” have not been defined in the active environment

Response attributes

Suppose we have an API that let us manage a collection of books, which includes two basic endpoints:

  • POST /books to create a new book ;
  • GET /books/<id> to get a book by its id.

If the API is responsible to generate a unique identifier at the creation of a book, you can’t know in advance how to retrieve this book as its identifier is not known yet. That’s where using response attributes to chain requests is useful.

Here is an example of a POST /books response:

{
"_id": "9b34080b-2636-4edf-b92f-bc48d124b1dc",
"title": "Twenty Thousand Leagues Under the Seas"
}

Use a template tag Response => Body Attribute in the URL field of the GET /books/<id> request:

Highlighting the Body Attribute item of the template tag autocompletion field
The template tag autocompletion field suggests “Body Attribute” among many other options

Edit the tag filter to set the path of the identifier attribute ($._id) and adapt the trigger behavior to your needs:

Editing a Body Attribute tag to get the correct property from another request response
Edit the Body Attribute tag to get the correct property from another request response

That’s it! Now you don’t have to worry about the identifier that has been assigned at the object creation.

URL using environment variables and template tags
An example of URL using both environment variables and template tags

Tips

  • Copy as Curl: copy to the clipboard the code to perform the request with the curl command line tool. It is accessible through the menu after a click on the arrow that appears when hovering a request.
Command menu of a request, highlighting “Cupy as Curl” item
Copy as Curl — a useful command to get the code to perform a request with curl

Something similar to this will be copied to the clipboard:

curl --request POST \
--url https://api.example.com/v1/books \
--header 'Content-Type: application/json' \
--data '{
"title": "Twenty Thousand Leagues Under the Seas"
}'
  • Import/Export: you can import/export one or many collections. This is especially useful to backup your workspace or share a collection with someone else. It is accessible via the Data tab of the Insomnia Preferences (⌘,) dialog.
Data tab of the Insomnia Preferences window, highlighting the item to export a collection
Import Data / Export Data — a convenient way to backup or share an Insomnia collection

Bonus: Generating Insomnia Request Collections

Disclaimer: This is an advanced part about the usage of Insomnia. It is about using programming skills to create stuff instead of the user interface.

A few months ago, I had to test a lot of microservices while they were being migrated to AWS. In the middle of the migration, using CI to test the entire behavior was not possible. For most of them, I did not know which endpoints or JSON-RPC methods they expose. Fortunately, all our microservices have a private endpoint which return a JSON-formatted documentation of methods they expose and events they send or listen to. Instead of creating requests for each microservice and method I wanted to test, I wrote up a basic script which converted those JSON-formatted documentations into a dedicated Insomnia Request collection. Then I just had to import the generated file into Insomnia, launch the requests, and that’s it!

I don’t want to deep dive into the conversion process because it was very specific to my use case. However, you may find interesting to see how we can create requests and collections on-the-fly.

Request template

Let’s create a request template that will be used to generate a single request. This is basically a JSON file in which I use some placeholders that will be replaced by the corresponding value later on. For example, we will generate a random UUID for each request that will be set in place of the #{req_uuid} placeholder.

{
"_id": "req_#{req_uuid}",
"parentId": "wrk_#{wrk_uuid}",
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"url": "{{ service.#{safe_service_name}.url }}",
"name": "#{method_name}",
"description": "#{method_desc}",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "#{method_body}"
},
"parameters": [],
"headers": [
{
"id": "pair_#{pair_uuid}",
"name": "Content-Type",
"value": "application/json"
}
],
"authentication": {},
"metaSortKey": -1000000000,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
}

Request Collection template

Let’s do the same for the request collection. It consists of several resources:

  • the workspace itself, that is the object describing the collection ;
  • the requests ;
  • a sub environment (we will create a production sub environment here) ;
  • other objects left empty for the sake of simplicity : the base environment, a cookie jar, and an API spec.
{
"_type": "export",
"__export_format": 4,
"__export_date": "#{date_iso}",
"__export_source": "insomnia.desktop.app:v2020.4.1",
"resources": [
#{requests},
{
"_id": "wrk_#{wrk_uuid}",
"parentId": null,
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"name": "#{wrk_name}",
"description": "",
"scope": null,
"_type": "workspace"
},
{
"_id": "env_#{env_id}",
"parentId": "wrk_#{wrk_uuid}",
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"name": "Base Environment",
"data": {},
"dataPropertyOrder": null,
"color": null,
"isPrivate": false,
"metaSortKey": #{timestamp_ms},
"_type": "environment"
},
{
"_id": "jar_#{jar_id}",
"parentId": "wrk_#{wrk_uuid}",
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"name": "Default Jar",
"cookies": [],
"_type": "cookie_jar"
},
{
"_id": "spc_#{spec_uuid}",
"parentId": "wrk_#{wrk_uuid}",
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"fileName": "#{wrk_name}",
"contents": "",
"contentType": "yaml",
"_type": "api_spec"
},
{
"_id": "env_#{env_uuid}",
"parentId": "env_#{env_id}",
"modified": #{timestamp_ms},
"created": #{timestamp_ms},
"name": "production",
"data": {
"restaurant": {
"restaurantUuid": "#{restaurant_uuid}"
},
"service": {
"#{safe_service_name}": {
"url": "http://#{service_name}.example.com"
}
}
},
"dataPropertyOrder": {
"&": [
"restaurant",
"service"
],
"&~|restaurant": [
"restaurantUuid"
],
"&~|service": [
"#{safe_service_name}"
],
"&~|service~|#{safe_service_name}": [
"url"
]
},
"color": null,
"isPrivate": false,
"metaSortKey": #{timestamp_ms},
"_type": "environment"
}
]
}

The complex part is the generation of the environment. We keep the base environment empty and use the production sub environment to set a few variables which will look like this at the end:

{
"restaurant": {
"restaurantUuid": "c4f35aef-f4de-4d29-ba08-e1acc2070fe2"
},
"service": {
"foo_service": {
"url": "http://foo-service.example.com"
}
}
}

Putting things altogether

I chose Node.js to write the generation script mainly because of its native capability to work with JSON format.

Basically it consists of reading the template file we described above, replacing placeholders with corresponding values, and writing the result in a new file:

(patterns, inputPath, outputPath) => {
const rs = fs.createReadStream(inputPath);
const ws = fs.createWriteStream(outputPath);
rs.on('readable', () => {
const chunk = rs.read();
if (chunk !== null) {
const content = replacePatterns(patterns, chunk);
ws.write(content);
} else {
ws.end();
}
});
};

To replace the placeholders, a mapping object between the placeholder name and a function to get its value is enough.

In the extract below, I generate a UUID for each request and use the serviceName variable I got from the JSON-formatted documentation of my microservice.

const requestPatterns = {
'#{req_uuid}': () => uuid(),
'#{service_name}': () => serviceName,
'#{safe_service_name}': () => serviceName.replace(/[^\w]/g, '_'),
// ... many other patterns
};
function replacePatterns(patterns, buffer) {
let content = buffer.toString();
Object.keys(patterns).forEach((pattern) => {
content = content.replace(new RegExp(pattern, 'g'), patterns[pattern]());
});
return content;
}

A great thing is that you can use the template tags we mentioned earlier. For instance, to let Insomnia generate a UUID itself we can use this syntax:

{% uuid 'v4' %}

This is the gist of how I generated Insomnia collections on-the-fly. If you’re interested, you can find the whole code source on its Github repository.

Conclusion

We have seen some of the features Insomnia provides to make us more productive. I hope this article made you discover a few! There are still plenty of options, settings or possibilities to explore. If you want to go further, you can check out Insomnia documentation.

--

--