Neovim 30 Jul 2026 12 hours ago

Testing APIs in Neovim with Kulala

Testing APIs in Neovim with Kulala

Kulala is a Neovim REST client that speaks the .http file format — the same one VS Code's REST Client uses. One file, one buffer, full control over your API testing without leaving the editor.

Prerequisites

1. Environment setup

Put shared static variables in http-client.env.json at your project root. This keeps secrets out of your request files and lets you switch between dev/staging/prod.

{
  "default": {
    "baseUrl": "http://localhost:3000",
    "email": "test@test.com"
  },
  "prod": {
    "baseUrl": "https://api.example.com",
    "email": "admin@example.com"
  }
}

Reference them as {{baseUrl}}, {{email}} anywhere in your .http files. Use http-client.private.env.json for secrets (add it to .gitignore).

2. Making requests

Each request should contain a request description that looks like ### Request description before you provide the actual request below in the form METHOD <path..> for example:

GET

This will send a get request to list all products from your baseUrl. Remember that baseUrl is from http-client.env.json-- a static variable shared across all .http files.

### List products
GET {{baseUrl}}/api/v1/products

If you do not have the http-client.env.json you can provide the variable at the top of the request but it will be available only for that request. If you want it for the entire buffer (the http file), add it to the begining of the file.

@baseUrl=http://localhost:3000
### List products
GET {{baseUrl}}/api/v1/product

POST with body

### Create a product
POST {{baseUrl}}/api/v1/products
Content-Type: application/json

{
  "name": "MacBook Pro",
  "price": 2499
}

The body goes after the headers, separated by a blank line. Kulala sends it as-is.

3. Sharing data in the same buffer

Name a request with # @name, then reference its response in subsequent requests:

### Login
# @name LOGIN
POST {{baseUrl}}/api/v1/auth/login
Content-Type: application/json

{
  "email": "{{email}}",
  "password": "testuser"
}

### Get profile (uses token from login above)
GET {{baseUrl}}/api/v1/auth/profile
Authorization: Bearer {{LOGIN.response.body.$.accessToken}}

Add # @kulala-vscode-restclient-compat at the top of the file to enable the response reference syntax. The $.accessToken is a JSONPath expression into the response body.

4. Sharing data globally across files

Response references ({{LOGIN.response...}}) only work within the same file. To share a token across multiple .http files, use client.global.set() in a post-request script. Note that accessToken and refreshToken are results from your login endpoint. So make sure to replace with your own response field.

### Login
# @name LOGIN
POST {{baseUrl}}/api/v1/auth/login
Content-Type: application/json

{
  "email": "{{email}}",
  "password": "testuser"
}

> {%
  client.global.set("ACCESS_TOKEN", JSON.parse(response.body).accessToken)
  client.global.set("REFRESH_TOKEN", JSON.parse(response.body).refreshToken)
%}

Now any .http file in the project can use {{ACCESS_TOKEN}}:

### Get products
GET {{baseUrl}}/api/v1/products
Authorization: Bearer {{ACCESS_TOKEN}}

5. Sharing static data like common headers

Add a $kulalaShared section to http-client.env.json for headers that should go on every request:

{
  "$kulalaShared": {
    "$kulalaDefaultHeaders": {
      "Content-Type": "application/json",
      "Accept": "application/json"
    }
  },
  "default": {
    "baseUrl": "http://localhost:3000"
  }
}

No need to write Content-Type: application/json on every request anymore — it's automatic.

6. Prompting for input per-request

Use # @prompt just below the request description to ask for values at runtime:

### Create a product
# @prompt name Product name
POST {{baseUrl}}/api/v1/products
Content-Type: application/json
Authorization: Bearer {{ACCESS_TOKEN}}

{
  "name": "{{name}}",
}

Kulala pops an input dialog before sending.

7. Directory structure

A typical setup:

project-root/
├── http-client.env.json         # shared env vars + default headers
├── http-client.private.env.json # gitignored secrets
└── collections/
    ├── auth.http                # login + token script
    └── products.http            # uses {{ACCESS_TOKEN}} globally

Key bindings

| Action | Key | |--------|-----| | Send request | <leader>R (or :KulalaSend) | | Re-run last | <leader>r |