Introduction

Slumber is a terminal-based HTTP client, built for interacting with REST and other HTTP clients. It has two usage modes: Terminal User Interface (TUI) and Command Line Interface (CLI). The TUI is the most useful, and allows for interactively sending requests and viewing responses. The CLI is useful for sending quick requests and scripting.

The goal of Slumber is to be easy to use, configurable, and sharable. To that end, configuration is defined in a YAML file called the request collection. Both usage modes (TUI and CLI) share the same basic configuration, which is called the request collection.

Check out the Getting Started guide to try it out, or move onto Key Concepts to start learning in depth about Slumber.

Slumber demo

Install

Slumber binaries are available from the GitHub Releases page. Or if you prefer a managed installation:

cargo

cargo install slumber --locked

cargo binstall

cargo binstall slumber

homebrew

brew install LucasPickering/tap/slumber

sh

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/LucasPickering/slumber/releases/download/v3.3.0/slumber-installer.sh | sh

powershell

powershell -c "irm https://github.com/LucasPickering/slumber/releases/download/v3.3.0/slumber-installer.ps1 | iex"

Shell Completions

After installation, you can optionally install shell completions for TAB-complete of CLI commands. For the full list of supported shells, see the clap docs.

Note: Slumber uses clap's native shell completions, which are still experimental. This issue outlines the remaining work to be done.

To source your completions:

Bash

echo "source <(COMPLETE=bash slumber)" >> ~/.bashrc

Elvish

echo "eval (E:COMPLETE=elvish slumber | slurp)" >> ~/.elvish/rc.elv

Fish

echo "source (COMPLETE=fish slumber | psub)" >> ~/.config/fish/config.fish

Powershell

echo "COMPLETE=powershell slumber | Invoke-Expression" >> $PROFILE

Zsh

echo "source <(COMPLETE=zsh slumber)" >> ~/.zshrc

Getting Started

Quick Start

Once you've installed Slumber, setup is easy.

1. Create a Slumber collection file

Slumber's core feature is that it's source-based. That means you write down your configuration in a file first, then run Slumber and it reads the file. This differs from other popular clients such as Postman and Insomnia, where you define your configuration in the app, and it saves it to a file for you. The goal of being source-based is to make it easy to save and share your configurations.

The easiest way to get started is to generate a new collection with the new subcommand:

slumber new

2. Run Slumber

slumber

This will start the TUI, and you'll see the example requests available. Use tab/shift+tab (or the shortcut keys shown in the pane headers) to navigate around. Select a recipe in the left pane, then hit Enter to send a request.

Going Further

Now that you have a collection, you'll want to customize it. Here's another example of a simple collection, showcasing multiple profiles:

# slumber.yml
profiles:
  local:
    data:
      host: http://localhost:5000
  production:
    data:
      host: https://myfishes.fish

requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    body: !json { "kind": "barracuda", "name": "Jimmy" }

  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

Note: the !request tag, which tells Slumber that this is a request recipe, not a folder. This is YAML's tag syntax, which is used commonly throughout Slumber to provide explicit configuration.

This request collection uses templates and profiles, allowing you to dynamically change the target host.

To learn more about the powerful features of Slumber you can use in your collections, keep reading with Key Concepts.

Key Concepts

There are a handful of key concepts you need to understand how to effectively configure and use Slumber. You can read about each one in detail on its linked API reference page.

Request Collection

The collection is the main form of configuration. It defines a set of request recipes, which enable Slumber to make requests to your API.

Request Recipe

A recipe defines which HTTP requests Slumber can make. A recipe generally corresponds to one endpoint on an API, although you can create as many recipes per endpoint as you'd like.

Template

Templates are Slumber's most powerful feature. They allow you to dynamically build URLs, query parameters, request bodies, etc. using predefined or dynamic values.

Profile

A profile is a set of static template values. A collection can contain a list of profiles, allowing you to quickly switch between different sets of values. This is useful for using different deployment environments, different sets of IDs, etc.

Terminal User Interface

The Terminal User Interface (TUI) is the primary use case for Slumber. It provides a long-lived, interactive interface for sending HTTP requests, akin to Insomnia or Postman. The difference of course is Slumber runs entirely in the terminal.

To start the TUI, simply run:

slumber

This will detect your request collection file according to the protocol. If you want to load a different file, you can use the --file parameter:

slumber --file my-slumber.yml

Auto-Reload

Once you start your Slumber, that session is tied to a single collection file. Whenever that file is modified, Slumber will automatically reload it and changes will immediately be reflected in the TUI. If auto-reload isn't working for some reason, you can manually reload the file with the r key.

Multiple Sessions

Slumber supports running multiple sessions at once, even on the same collection. Request history is stored in a thread-safe SQLite, so multiple sessions can safely interact simultaneously.

If you frequently run multiple sessions together and want to quickly switch between them, consider a configurable terminal manager like tmux or Zellij.

In-App Editing & File Viewing

Editing

Open collection file in vim

Slumber supports editing your collection file without leaving the app. To do so, open the actions menu (x by default), then select Edit Collection. Slumber will open an external editor to modify the file. To determine which editor to use, Slumber checks these places in the following order:

  • editor field of the configuration file
  • VISUAL environment variable
  • EDITOR environment variable
  • Default to vim

The VISUAL and EDITOR environment variables are a common standard to define a user's preferred text editor. For example, it's what git uses by default to determine how to edit commit messages. If you want to use the same editor for all programs, you should set these. If you want to use a command specific to Slumber, set the editor config field.

Slumber supports passing additional arguments to the editor. For example, if you want to open VSCode and have wait for the file to be saved, you can configure your editor like so:

editor: code --wait

The command will be parsed like a shell command (although a shell is never actually invoked). For exact details on parsing behavior, see shell-words.

Paging

You can open request and response bodies in a separate file browser if you want additional features beyond what Slumber provides. To configure the command to use, set the PAGER environment variable or the pager configuration field:

pager: bat

Slumber will check these places in the following order for a command:

  • pager field of the configuration file
  • PAGER environment variable
  • Default to less (Unix) or more (Windows)

The pager command uses the same format as the editor field. The command is parsed with shell-words, then a temporary file path is passed as the final argument.

To open a body in the pager, use the actions menu keybinding (x by default, see input bindings), and select View Body.

Some popular pagers:

Setting a content-specific pager

If you want to use a different pager for certain content types, such as using jless for JSON, you can pass a map of MIME type patterns to commands. For example:

pager:
  json: jless
  default: less

For more details on matching, see MIME Maps.

Data Filtering & Querying

When browsing an HTTP response in Slumber, you may want to filter, query, or otherwise transform the response to make it easier to view. Slumber supports this via embedded shell commands. The query box at the bottom of the response pane allows you to execute any shell command, which will be passed the response body via stdin and its output will be shown in the response pane. You can use grep, jq, sed, or any other text processing tool.

Querying response via jq

Example of querying with jq

Querying response with pipes

Example of using pipes in a query command

Exporting data

Keep in mind that your queries are being executed as shell commands on your system. You should avoid running any commands that interact with the file system, such as using > or < to pipe to/from files. However, if you want to export response data from Slumber, you can do so with the export command palette. To open the export palette, select the Response pane and press the export key binding (: by default). Then enter any shell command, which will receive the response body as stdin.

Note: For text bodies, whatever text is visible in the response pane is what will be passed to stdin. So if you have a query applied, the queried body will be exported. For binary bodies, the original bytes will be exported.

Some useful commands for exporting data:

  • tee > response.json - Save the response to response.json
    • tee takes data from stdin and sends it to zero or more files as well as stdout. Another way to write this would be tee response.json
  • pbcopy - Copy the body to the clipboard (MacOS only - search online to find the correct command for your platform)

Remember: This is a real shell, so you can pipe through whatever transformation commands you want here!

Default command

You can set the default command to query with via the commands.default_query config field. This accepts either a single string to set it for all content types, or a MIME map to set different defaults based on the response content type. For example, to default to jq for all JSON responses:

commands:
  default_query:
    json: jq

Which shell does Slumber use?

By default, Slumber executes your command via sh -c on Unix and cmd /S /C on Windows. You can customize this via the commands.shell configuration field. For example, to use fish instead of sh:

commands:
  shell: [fish, -c]

If you don't want to execute via any shell, you can set it to []. In this case, query commands will be parsed via shell-words and executed directly. For example, jq .args will be parsed into ["jq", ".args"], then jq will be executed with a single argument: .args. This of course means you won't get access to shell features such as |, but it provides better cross-platform portability.

Command Line Interface

While Slumber is primary intended as a TUI, it also provides a Command Line Interface (CLI). The CLI can be used to send requests, just like the TUI. It also provides some utility commands for functionality not available in the TUI. For a full list of available commands see the side bar or run:

slumber help

Some common CLI use cases:

Examples

The Slumber CLI can be composed with other CLI tools, making it a powerful tool for scripting and bulk tasks. Here are some examples of how to use it with common tools.

Note: These examples are written for a POSIX shell (bash, zsh, etc.). It assumes some basic familiarity with shell features such as pipes. Unfortunately I have no shell experience with Windows so I can't help you there :(

Filtering responses with jq

Let's say you want to fetch the name of each fish from your fish-tracking service. Here's your collection file:

requests:
  list_fish: !request
    method: GET
    url: "https://myfishes.fish/fishes"

This endpoint returns a response like:

[
  {
    "kind": "barracuda",
    "name": "Jimmy"
  },
  {
    "kind": "striped bass",
    "name": "Balthazar"
  },
  {
    "kind": "rockfish",
    "name": "Maureen"
  }
]

You can fetch this response and filter it down to just the names:

slumber rq list_fish | jq -r '.[].name'

And the output:

Jimmy
Balthazar
Maureen

Running requests in parallel with xargs

Building on the previous example, let's say you want to fetch details on each fish returned from the list response. We'll add a get_fish recipe to the collection. By default, the fish name will come from a prompt:

chains:
  fish_name:
    source: !prompt
      message: "Which fish?"

requests:
  list_fish: !request
    method: GET
    url: "https://myfishes.fish/fishes"

  get_fish: !request
    method: GET
    url: "https://myfishes.fish/fishes/{{chains.fish_name}}"

We can use xargs and the -o flag of slumber request to fetch details for each fish in parallel:

slumber rq list_fish | jq -r '.[].name' > fish.txt
cat fish.txt | xargs -L1 -I'{}' -P3 slumber rq get_fish -o chains.fish_name={} --output {}.json

Let's break this down:

  • -L1 means to consume one argument (in this case, one fish name) per invocation of slumber
  • -I{} sets the substitution string, i.e. the string that will be replaced with each argument
  • -P3 tells xargs the maximum number of processes to run concurrently, which in this case means the maximum number of concurrent requests
  • Everything else is the slumber command
    • -o chains.fish_name={} chains.fish_name` with the argument from the file, so it doesn't prompt for a name
    • --output {}.json writes to a JSON file with the fish's name (e.g. Jimmy.json)

Subcommands

slumber collections

View and manipulate stored collection history/state. Slumber uses a local database to store all request/response history, as well as UI state and other persisted values. As a user, you rarely have to worry about this. The most common scenario in which you do have to is if you've renamed a collection file and want to migrate the history to match the new path. See here for how to migrate collection files.

See slumber collections --help for more options.

slumber db

Access the local Slumber database file. This is an advanced command; most users never need to manually view or modify the database file. By default this executes sqlite3 and thus requires sqlite3 to be installed.

Open a shell to the database:

slumber db

Run a single query and exit:

slumber db 'select 1'

slumber generate

Generate an HTTP request in an external format. Currently the only supported format is cURL.

Overrides

The generate subcommand supports overriding template values in the same that slumber request does. See the request subcommand docs for more.

See slumber generate --help for more options.

Examples

Given this request collection:

profiles:
  production:
    data:
      host: https://myfishes.fish

requests:
  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true
slumber generate curl --profile production list_fishes
slumber generate curl --profile production list_fishes -o host=http://localhost:8000

slumber history

View and modify your Slumber request history. Slumber stores every command sent from the TUI in a local SQLite database (requests are not stored remotely). You can find the database file with slumber show paths db.

You can use the slumber history subcommand to browse and delete request history.

slumber history list

List requests in a table.

slumber history list # List all requests for the current collection
slumber history list --all # List all requests for all collections
slumber history list login # List all requests for the "login" recipe
slumber history list login -p dev # List all requests for "login" under the "dev" profile

slumber history get

Show a single request/response from history.

slumber history get login # Get the most recent request/response for "login"
slumber history get 548ba3e7-3b96-4695-9856-236626ea0495 # Get a particular request/response by ID (IDs can be retrieved from the `list` subcommand)

slumber history delete

Delete requests from history by ID.

slumber history delete 548ba3e7-3b96-4695-9856-236626ea0495
# Delete multiple requests
slumber history list login --id-only | xargs slumber history delete

slumber import

Generate a Slumber collection file based on an external format.

See slumber import --help for more options.

Disclaimer

Importers are approximate. They'll give the you skeleton of a collection file, but don't expect 100% equivalency. They save a lot of tedious work for you, but you'll generally still need to do some manual work on the collection file to get what you want.

Formats

Supported formats:

Examples

The general format is:

slumber import <format> <input> [output]

Possible inputs are:

  • - for stdin
  • Path to a local file
  • URL to download via HTTP

For example, to import from an Insomnia collection insomnia.json:

slumber import insomnia insomnia.json slumber.yml
# Or, to read from stdin and print to stdout
slumber import insomnia - < insomnia.json

Or to import an OpenAPI spec from a server:

slumber import openapi https://petstore3.swagger.io/api/v3/openapi.json slumber.yml

Requested formats:

If you'd like another format supported, please open an issue.

slumber new

Generate a new Slumber collection file. The new collection will have some example data predefined.

Examples

# Generate and use a new collection at the default path of slumber.yml
slumber new
slumber

# Generate and use a new collection at a custom path
slumber new my-collection.yml
slumber -f my-collection.yml

slumber request

Send an HTTP request. There are many use cases to which the CLI is better suited than the TUI for sending requests, including:

  • Sending a single one-off request
  • Sending many requests in parallel
  • Automating requests in a script
  • Sharing requests with others

See slumber request --help for more options.

Overrides

You can manually override template values using CLI arguments. This means the template renderer will use the override value in place of calculating it. For example:

slumber request list_fishes --override host=https://dev.myfishes.fish

This can also be used to override chained values:

slumber request login --override chains.password=hunter2

Exit Code

By default, the CLI returns exit code 1 if there is a fatal error, e.g. the request failed to build or a network error occurred. If an HTTP response was received and parsed, the process will exit with code 0, regardless of HTTP status.

If you want to set the exit code based on the HTTP response status, use the flag --exit-code.

CodeReason
0HTTP response received
1Fatal error
2HTTP response had status >=400 (with --exit-code)

Examples

Given this request collection:

profiles:
  production:
    data:
      host: https://myfishes.fish

requests:
  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true
slumber request --profile production list_fishes
slumber rq -p production list_fishes # rq is a shorter alias
slumber -f fishes.yml -p production list_fishes # Different collection file

slumber show

Print metadata about Slumber.

See slumber show --help for more options.

Examples

slumber show paths # Show paths of various Slumber data files/directories
slumber show config # Print global configuration
slumber show config --edit # Edit global configuration
slumber show collection # Print collection file
slumber show collection --edit # Edit collection file

Templates

Templates enable dynamic string/binary construction. Slumber's template language is relatively simple, compared to complex HTML templating languages like Handlebars or Jinja. The goal is to be intuitive and unsurprising. It doesn't support complex features like loops, conditionals, etc.

Most string values in a request collection (e.g. URL, request body, etc.) are templates. Map keys (e.g. recipe ID, profile ID) are not templates; they must be static strings.

The syntax for injecting a dynamic value into a template is double curly braces {{...}}. The contents inside the braces tell Slumber how to retrieve the dynamic value.

This guide serves as a functional example of how to use templates. For detailed information on options available, see the API reference.

A note on YAML string syntax

One of the advantages (and disadvantages) of YAML is that it has a number of different string syntaxes. This enables you to customize your templates according to your specific needs around the behavior of whitespace and newlines. See YAML's string syntaxes and yaml-multiline.info for more info on YAML strings.

A Basic Example

Let's start with a simple example. Let's say you're working on a fish-themed website, and you want to make requests both to your local stack and the deployed site. Templates, combined with profiles, allow you to easily switch between hosts:

Note: for the purposes of these examples, I've made up some theoretical endpoints and responses, following standard REST practice. This isn't a real API but it should get the point across.

Additionally, these examples will use the CLI because it's easy to demonstrate in text. All these concepts apply equally to the TUI.

profiles:
  local:
    data:
      host: http://localhost:5000
  production:
    data:
      host: https://myfishes.fish

requests:
  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

Now you can easily select which host to hit. In the TUI, this is done via the Profile list. In the CLI, use the --profile option:

> slumber request --profile local list_fish
# http://localhost:5000/fishes
# Only one fish :(
[{"id": 1, "kind": "tuna", "name": "Bart"}]
> slumber request --profile production list_fish
# https://myfishes.fish/fishes
# More fish!
[
  {"id": 1, "kind": "marlin", "name": "Kim"},
  {"id": 2, "kind": "salmon", "name": "Francis"}
]

Nested Templates

What if you need a more complex chained value? Let's say the endpoint to get a fish requires the fish ID to be in the format fish_{id}. Why? Don't worry about it. Fish are particular. Templates support nesting implicitly. You can use this to compose template values into more complex strings. Just be careful not to trigger infinite recursion!

profiles:
  local:
    data:
      host: http://localhost:5000
      fish_id: "fish_{{chains.fish_id}}"

chains:
  fish_id:
    source: !request
      recipe: create_fish
    selector: $.id

requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    body: !json { "kind": "barracuda", "name": "Jimmy" }

  get_fish: !request
    method: GET
    url: "{{host}}/fishes/{{fish_id}}"

And let's see it in action:

> slumber request -p local create_fish
# http://localhost:5000/fishes
{"id": 2, "kind": "barracuda", "name": "Jimmy"}
> slumber request -p local get_fish
# http://localhost:5000/fishes/fish_2
{"id": "fish_2", "kind": "barracuda", "name": "Jimmy"}

Binary Templates

While templates are mostly useful for generating strings, they can also generate binary data. This is most useful for sending binary request bodies. Some fields (e.g. URL) do not support binary templates because they need valid text; in those cases, if the template renders to non-UTF-8 data, an error will be returned. In general, if binary data can be supported, it is.

Note: Support for binary form data is currently incomplete. You can render binary data from templates, but forms must be constructed manually. See #235 for more info.

profiles:
  local:
    data:
      host: http://localhost:5000
      fish_id: "cod_father"

chains:
  fish_image:
    source: !file
      path: ./cod_father.jpg

requests:
  set_fish_image: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}/image"
    headers:
      Content-Type: image/jpg
    body: "{{chains.fish_image}}"

Chains

Chains are Slumber's most powerful feature. They allow you to dynamically build requests based on other responses, shell commands, and more.

Chains in Practice

The most common example of a chain is with a login request. You can define a recipe to log in to a service using username+password, then get the returned API token to authenticate subsequent requests. Of course, we don't want to store our credentials in Slumber file, so we can also use chains to fetch those. Let's see this in action:

chains:
  username:
    source: !file
      path: ./username.txt
  password:
    source: !file
      path: ./password.txt
  auth_token:
    source: !request
      recipe: login
    selector: $.token

requests:
  # This returns a response like {"token": "abc123"}
  login: !request
    method: POST
    url: "https://myfishes.fish/login"
    body:
      !json {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}",
      }

  get_user: !request
    method: GET
    url: "https://myfishes.fish/current-user"
    authentication: !bearer "{{chains.auth_token}}"

For more info on the selector field, see Data Extraction via JSONPath

Automatically Executing the Upstream Request

By default, the chained request (i.e. the "upstream" request) has to be executed manually to get the login token. You can have the upstream request automatically execute using the trigger field:

chains:
  auth_token:
    source: !request
      recipe: login
      # Execute only if we've never logged in before
      trigger: !no_history
    selector: $.token
---
chains:
  auth_token:
    source: !request
      recipe: login
      # Execute only if the latest response is older than a day. Useful if your
      # token expires after a fixed amount of time
      trigger: !expire 1d
    selector: $.token
---
chains:
  auth_token:
    source: !request
      recipe: login
      # Always execute
      trigger: !always
    selector: $.token

For more detail about the various trigger variants, including the syntax of the expire variant, see the API docs.

Chaining Chains

Chains on their own are powerful enough, but what makes them really cool is that the arguments to a chain are templates in themselves, meaning you can use nested templates to chain chains to other chains! Wait, what?

Let's say the login response doesn't return JSON, but instead the response looks like this:

token:abc123

Clearly this isn't a well-designed API, but sometimes that's all you get. You can use a nested chain with cut to parse this:

chains:
  username:
    source: !file
      path: ./username.txt
  password_encrypted:
    source: !file
      path: ./password.txt
  password:
    source: !command
      command:
  auth_token_raw:
    source: !request
      recipe: login
  auth_token:
    source: !command
      command: ["cut", "-d':'", "-f2"]
      stdin: "{{chains.auth_token_raw}}"

requests:
  login: !request
    method: POST
    url: "https://myfishes.fish/login"
    body:
      !json {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}",
      }

  get_user: !request
    method: GET
    url: "https://myfishes.fish/current-user"
    authentication: !bearer "{{chains.auth_token}}"

This means you can use external commands to perform any manipulation on data that you want.

Data Extraction via JSONPath

Chains support querying data structures to transform or reduce response data. THis is done via the selector field of a chain.

Regardless of data format, querying is done via JSONPath. For non-JSON formats, the data will be converted to JSON, queried, and converted back. This keeps querying simple and uniform across data types.

Querying Chained Values

Here's some examples of using queries to extract data from a chained value. Let's say you have two chained value sources. The first is a JSON file, called creds.json. It has the following contents:

{ "user": "fishman", "pw": "hunter2" }

We'll use these credentials to log in and get an API token, so the second data source is the login response, which looks like so:

{ "token": "abcdef123" }
chains:
  username:
    # Slumber knows how to query this file based on its extension
    source: !file
      path: ./creds.json
    selector: $.user
  password:
    source: !file
      path: ./creds.json
    selector: $.pw
  auth_token:
    source: !request
      recipe: login
    selector: $.token

requests:
  login: !request
    method: POST
    url: "https://myfishes.fish/anything/login"
    body:
      !json {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}",
      }

  get_user: !request
    method: GET
    url: "https://myfishes.fish/anything/current-user"
    query:
      - auth={{chains.auth_token}}

While this example simple extracts inner fields, JSONPath can be used for much more powerful transformations. See the JSONPath docs or this JSONPath editor for more examples.

More Powerful Querying with Nested Chains

If JSONPath isn't enough for the data extraction you need, you can use nested chains to filter with whatever external programs you want. For example, if you want to use jq instead:

chains:
  username:
    source: !file
      path: ./creds.json
    selector: $.user
  password:
    source: !file
      path: ./creds.json
    selector: $.pw
  auth_token_raw:
    source: !request
      recipe: login
  auth_token:
    source: !command
      command: [ "jq", ".token" ]
      stdin: "{{chains.auth_token_raw}}

requests:
  login: !request
    method: POST
    url: "https://myfishes.fish/anything/login"
    body: !json
      {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}"
      }

  get_user: !request
    method: GET
    url: "https://myfishes.fish/anything/current-user"
    query:
      - auth={{chains.auth_token}}

You can use this capability to manipulate responses via grep, awk, or any other program you like.

Collection Reuse & Inheritance

The Problem

Let's start with an example of something that sucks. Let's say you're making requests to a fish-themed JSON API, and it requires authentication. Gotta protect your fish! Your request collection might look like so:

profiles:
  production:
    data:
      host: https://myfishes.fish
      fish_id: 6

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

requests:
  list_fish: !request
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true
    headers:
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

  get_fish: !request
    method: GET
    url: "{{host}}/fishes/{{fish_id}}"
    headers:
      Accept: application/json
    authentication: !bearer "{{chains.token}}"

The Solution

You've heard of DRY, so you know this is bad. Every new request recipe requires re-specifying the headers, and if anything about the authorization changes, you have to change it in multiple places.

You can easily re-use components of your collection using YAML's merge feature.

profiles:
  production:
    data:
      host: https://myfishes.fish

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

# The name here is arbitrary, pick any name you like. Make sure it starts with
# . to avoid errors about an unknown field
.request_base: &request_base
  headers:
    Accept: application/json
  authentication: !bearer "{{chains.token}}"

requests:
  list_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

  get_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes/{{chains.fish_id}}"

Great! That's so much cleaner. Now each recipe can inherit whatever base properties you want just by including <<: *request_base. This is still a bit repetitive, but it has the advantage of being explicit. You may have some requests that don't want to include those values.

Recursive Inheritance

But wait! What if you have a new request that needs an additional header? Unfortunately, YAML's merge feature does not support recursive merging. If you need to extend the headers map from the base request, you'll need to pull that map in manually:

profiles:
  production:
    data:
      host: https://myfishes.fish

chains:
  token:
    source: !file
      path: ./api_token.txt
  fish_id:
    source: !prompt
      message: Fish ID

.request_base: &request_base
  headers: &headers_base # This will let us pull in the header map
    Accept: application/json
  authentication: !bearer "{{chains.token}}"

requests:
  list_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes"
    query:
      - big=true

  get_fish: !request
    <<: *request_base
    method: GET
    url: "{{host}}/fishes/{{chains.fish_id}}"

  create_fish: !request
    <<: *request_base
    method: POST
    url: "{{host}}/fishes"
    headers:
      <<: *headers_base
      Host: myfishes.fish
    body: !json { "kind": "barracuda", "name": "Jimmy" }

Importing External Collections

See the slumber import subcommand.

Database & Persistence

Note: This is an advanced feature. The vast majority of users can use Slumber all they want without even knowing the database exists.

Slumber uses a SQLite database to persist requests and responses. The database also stores UI state that needs to be persisted between sessions. This database exists exclusively on your device. The Slumber database is never uploaded to the cloud or shared in any way. Slumber does not make any network connections beyond the ones you define and execute yourself. You own your data; I don't want it.

Data for all your Slumber collections are stored in a single file. To find this file, run slumber show paths, and look for the Database entry. I encourage you to browse this file if you're curious; it's pretty simple and there's nothing secret in it. Keep in mind though that the database format is NOT considered part of Slumber's API contract. It may change at any time, including the database path moving or tables be changed or removed, even in a minor or patch release.

Controlling Persistence

By default, all requests made in the TUI are stored in the database. This enables the history browser, allowing you to browse past requests. While generally useful, this may not be desired in all cases. However, there are some cases where you may not want requests persisted:

  • The request or response may contain sensitive data
  • The response is very large and impacts app performance

You can disable persistence for a single recipe by setting persist: false for that recipe. You can disable history globally by setting persist: false in the global config file. Note that this only disables request persistence. UI state, such as selection state for panes and checkboxes, is still written to the database.

NOTE: Disabling persistence does not delete existing request history. See here for how to do that.

Slumber will generally continue to work just fine with request persistence disabled. Requests and responses are still cached in memory, they just aren't written to the database anymore and therefore can't be recovered after the current session is closed. If you disable persistence, you will notice a few impacts on functionality:

Unlike the TUI, requests made from the CLI are not persisted by default. This is because the CLI is often used for scripting and bulk requests. Persisting these requests could have major performance impacts for little to no practical gain. Pass the --persist flag to slumber request to persist a CLI request.

Deleting Request History

There are a few ways to delete requests from history:

  • In the TUI. Open the actions menu while a request/response is selected to delete that request. From the recipe list/recipe pane, you can delete all requests for that recipe.
  • slumber history delete can delete one or more commands at a time. Combine with slumber history list for bulk deletes: slumber history list login --id-only | xargs slumber history delete
  • slumber collections delete can delete all history for a single collection. If you have an old collection that you no longer use, you can delete it from the list using this command. Note: If you moved a collection file and want to remove the old file's history, you can also migrate the history to the new file location.
  • Manually modifying the database. You can access the DB with slumber db. While this is not an officially supported technique (as the DB schema may change without warning), it's simple enough to navigate if you want to performance bulk deletes with custom criteria.

Migrating Collections

As all Slumber collections' histories are stored in the same SQLite database, each collection gets a unique UUID generated when it is first accessed. This UUID is used to persist request history and other data related to the collection. This UUID is bound to the collection's path. If you move a collection file, a new UUID will be generated and it will be unlinked from its previous history. If you want to retain that history, you can migrate data from the old ID to the new one like so:

slumber collections migrate slumber-old.yml slumber-new.yml

If you don't remember the path of the old file, you can list all known collections with:

slumber collections list

Request Collection

The request collection is the primary configuration for Slumber. It defines which requests can be made, and how to make them. When running a slumber instance, a single collection file is loaded. If you want to work with multiple collections at once, you'll have to run multiple instances of Slumber.

Collection files are designed to be sharable, meaning you can commit them to your Git repo. The most common pattern is to create one collection per API repo, and check it into the repo so other developers of the API can use the same collection. This makes it easy for any new developer or user to learn how to use an API.

Format & Loading

A collection is defined as a YAML file. When you run slumber, it will search the current directory and its parents for the following default collection files, in order:

  • slumber.yml
  • slumber.yaml
  • .slumber.yml
  • .slumber.yaml

Whichever of those files is found first will be used. For any given directory, if no collection file is found there, it will recursively go up the directory tree until we find a collection file or hit the root directory. If you want to use a different file for your collection (e.g. if you want to store multiple collections in the same directory), you can override the auto-search with the --file (or -f) command line argument. You can also pass a directory to --file to have it search that directory instead of the current one. E.g.:

slumber --file my-collection.yml
slumber --file ../another-project/

Fields

A request collection supports the following top-level fields:

FieldTypeDescriptionDefault
profilesmapping[string, Profile]Static template values{}
requestsmapping[string, RequestRecipe]Requests Slumber can send{}
chainsmapping[string, Chain]Complex template values{}

In addition to these fields, any top-level field beginning with . will be ignored. This can be combined with YAML anchors to define reusable components in your collection file.

Examples

profiles:
  local:
    name: Local
    data:
      host: http://localhost:5000
      user_guid: abc123
  prd:
    name: Production
    data:
      host: https://httpbin.org
      user_guid: abc123

chains:
  username:
    source: !file
      path: ./username.txt
  password:
    source: !prompt
      message: Password
    sensitive: true
  auth_token:
    source: !request
      recipe: login
    selector: $.token

# Use YAML anchors for de-duplication. Normally unknown fields in the
# collection trigger an error; the . prefix tells Slumber to ignore this field
.base: &base
  headers:
    Accept: application/json

requests:
  login: !request
    <<: *base
    method: POST
    url: "{{host}}/anything/login"
    body:
      !json {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}",
      }

  # Folders can be used to keep your recipes organized
  users: !folder
    requests:
      get_user: !request
        <<: *base
        name: Get User
        method: GET
        url: "{{host}}/anything/current-user"
        authentication: !bearer "{{chains.auth_token}}"

      update_user: !request
        <<: *base
        name: Update User
        method: PUT
        url: "{{host}}/anything/current-user"
        authentication: !bearer "{{chains.auth_token}}"
        body: !json { "username": "Kenny" }

Profile

A profile is a collection of static template values. It's useful for configuring and switching between multiple different environments/settings/etc. Profile values are all templates themselves, so nested values can be used.

Fields

FieldTypeDescriptionDefault
namestringDescriptive name to use in the UIValue of key in parent
defaultbooleanUse this profile in the CLI when --profile isn't providednull
datamapping[string, Template]Fields, mapped to their values{}

Examples

profiles:
  local:
    name: Local
    data:
      host: localhost:5000
      url: "https://{{host}}"
      user_guid: abc123

Template

A template is represented in YAML as a normal string, and thus supports all of YAML's string syntaxes. Templates receive post-processing that injects dynamic values into the string. A templated value is represented with {{...}}.

Templates can generally be used in any value in a request recipe (not in keys), as well as in profile values and chains. This makes them very powerful, because you can compose templates with complex transformations.

For more detail on usage and examples, see the user guide page on templates.

Template Sources

There are several ways of sourcing templating values:

SourceSyntaxDescriptionDefault
Profile Field{{field_name}}Static value from a profileError if unknown
Environment Variable{{env.VARIABLE}}Environment variable from parent shell/process""
Chain{{chains.chain_id}}Complex chained valueError if unknown

Escape Sequences

In some scenarios you may want to use the {{ sequence to represent those literal characters, rather than the start of a template key. To achieve this, you can escape the sequence with an underscore inside it, e.g. {_{. If you want the literal string {_{, then add an extra underscore: {__{.

TemplateParses as
{_{this is raw text}}["{{this is raw text}}"]
{_{{field1}}["{", field("field1")]
{__{{field1}}["{__", field("field1")]
{_["{_"] (no escaping)

Examples

# Profile value
"hello, {{location}}"
---
# Multiple dynamic values
"{{greeting}}, {{location}}"
---
# Environment variable
"hello, {{env.LOCATION}}"
---
# Chained value
"hello, {{chains.where_am_i}}"
---
# No dynamic values
"hello, world!"
---
# Escaped template key
"{_{this is raw text}}"

Request Recipe

A request recipe defines how to make a particular request. For a REST API, you'll typically create one request recipe per endpoint. Other HTTP tools often call this just a "request", but that name can be confusing because "request" can also refer to a single instance of an HTTP request. Slumber uses the term "recipe" because it's used to render many requests. The word "template" would work as a synonym here, although we avoid that term here because it also refers to string templates.

Recipes can be organized into folders. This means your set of recipes can form a tree structure. Folders are purely organizational, and don't impact the behavior of their child recipes at all.

The IDs of your folders/recipes must be globally unique. This means you can't have two recipes (or two folders, or one recipe and one folder) with the same associated key, even if they are in different folders. This restriction makes it easy to refer to recipes unambiguously using a single ID, which is helpful for CLI usage and data storage.

Recipe Fields

The tag for a recipe is !request (see examples).

FieldTypeDescriptionDefault
namestringDescriptive name to use in the UIValue of key in parent
methodstringHTTP request methodRequired
urlTemplateHTTP request URLRequired
queryQueryParametersURL query parameters{}
headersmapping[string, Template]HTTP request headers{}
authenticationAuthenticationAuthentication schemenull
bodyRecipeBodyHTTP request bodynull
persistbooleanEnable/disable request persistence. Read moretrue

Folder Fields

The tag for a folder is !folder (see examples).

FieldTypeDescriptionDefault
namestringDescriptive name to use in the UIValue of key in parent
childrenmapping[string, RequestRecipe]Recipes organized under this folder{}

Examples

recipes:
  login: !request
    name: Login
    method: POST
    url: "{{host}}/anything/login"
    headers:
      accept: application/json
    query:
      - root_access=yes_please
    body:
      !json {
        "username": "{{chains.username}}",
        "password": "{{chains.password}}",
      }
  fish: !folder
    name: Users
    requests:
      create_fish: !request
        method: POST
        url: "{{host}}/fishes"
        body: !json { "kind": "barracuda", "name": "Jimmy" }

      list_fish: !request
        method: GET
        url: "{{host}}/fishes"
        query:
          - big=true

Query Parameters

Query parameters are a component of a request URL. They provide additional information to the server about a request. In a request recipe, query parameters can be defined in one of two formats:

  • Mapping of key: value
  • List of strings, in the format <key>=<value>

The mapping format is typically more readable, but the list format allows you to define the same query parameter multiple times. In either format, the key is treated as a plain string but the value is treated as a template.

Note: If you need to include a = in your parameter name, you'll need to use the mapping format. That means there is currently no support for multiple instances of a parameter with = in the name. This is very unlikely to be a restriction in the real world, but if you need support for this please open an issue.

Examples

recipes:
  get_fishes_mapping: !request
    method: GET
    url: "{{host}}/get"
    query:
      big: true
      color: red
      name: "{{name}}"

  get_fishes_list: !request
    method: GET
    url: "{{host}}/get"
    query:
      - big=true
      - color=red
      - color=blue
      - name={{name}}

Authentication

Authentication provides shortcuts for common HTTP authentication schemes. It populates the authentication field of a recipe. There are multiple source types, and the type is specified using YAML's tag syntax.

Variants

VariantTypeValue
!basicBasic AuthenticationBasic authentication credentials
!bearerstringBearer token

Basic Authentication

Basic authentication contains a username and optional password.

FieldTypeDescriptionDefault
usernamestringUsernameRequired
passwordstringPassword""

Examples

# Basic auth
requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    body: !json { "kind": "barracuda", "name": "Jimmy" }
    authentication: !basic
      username: user
      password: pass
---
# Bearer token auth
chains:
  token:
    source: !file
      path: ./token.txt
requests:
  create_fish: !request
    method: POST
    url: "{{host}}/fishes"
    body: !json { "kind": "barracuda", "name": "Jimmy" }
    authentication: !bearer "{{chains.token}}"

Recipe Body

There are a variety of ways to define the body of your request. Slumber supports structured bodies for a fixed set of known content types (see table below).

In addition, you can pass any Template to render any text or binary data. In this case, you'll probably want to explicitly set the Content-Type header to tell the server what kind of data you're sending. This may not be necessary though, depending on the server implementation.

Body Types

The following content types have first-class support. Slumber will automatically set the Content-Type header to the specified value, but you can override this simply by providing your own value for the header.

VariantTypeContent-TypeDescription
!jsonAnyapplication/jsonStructured JSON body; all strings are treated as templates
!form_urlencodedmapping[string, Template]application/x-www-form-urlencodedURL-encoded form data; see here for more
!form_multipartmapping[string, Template]multipart/form-dataBinary form data; see here for more

Examples

chains:
  image:
    source: !file
      path: ./fish.png

requests:
  text_body: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}/name"
    headers:
      Content-Type: text/plain
    body: Alfonso

  binary_body: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}/image"
    headers:
      Content-Type: image/jpg
    body: "{{chains.fish_image}}"

  json_body: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}"
    # Content-Type header will be set automatically based on the body type
    body: !json { "name": "Alfonso" }

  urlencoded_body: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}"
    # Content-Type header will be set automatically based on the body type
    body: !form_urlencoded
      name: Alfonso

  multipart_body: !request
    method: POST
    url: "{{host}}/fishes/{{fish_id}}"
    # Content-Type header will be set automatically based on the body type
    body: !form_multipart
      name: Alfonso
      image: "{{chains.fish_image}}"

Chain

A chain is a intermediate data type to enable complex template values. Chains also provide additional customization, such as marking values as sensitive.

To use a chain in a template, reference it as {{chains.<id>}}.

Fields

FieldTypeDescriptionDefault
sourceChainSourceSource of the chained valueRequired
sensitivebooleanShould the value be hidden in the UI?false
selectorJSONPathSelector to transform/narrow down results in a chained value. See Data Extraction via JSONPathnull
selector_modeSelectorModeControl selector behavior when query returns multiple resultsauto
content_typestringForce content type. Not required for request and file chains, as long as the Content-Type header/file extension matches the data. See here for a list of supported types.
trimChainOutputTrimTrim whitespace from the rendered outputnone

See the ChainSource docs for detail on the different types of chainable values.

Chain Output Trim

This defines how leading/trailing whitespace should be trimmed from the resolved output of a chain.

VariantDescription
noneDo not modify the resolved string
startTrim from just the start of the string
endTrim from just the end of the string
bothTrim from the start and end of the string

Selector Mode

The selector mode controls how Slumber handles returns JSONPath query results from the selector field, relative to how many matches the query returned. The table below shows how each mode behaves for a query that produces no values ($.id) a single value ($[0].name) vs multiple values ($[*].name) for this example data:

[{ "name": "Apple" }, { "name": "Kiwi" }, { "name": "Mango" }]
VariantDescription$.id$[0].name$[*].name
autoIf query returns a single value, use it. If it returns multiple, use a JSON arrayErrorApple["Apple", "Kiwi", "Mango"]
singleIf a query returns a single value, use it. Otherwise, error.ErrorAppleError
arrayReturn results as an array, regardless of count.[]["Apple"]["Apple", "Kiwi", "Mango"]

The default selector mode is auto.

Examples

# Load chained value from a file
username:
  source: !file
    path: ./username.txt
---
# Prompt the user for a value whenever the request is made
password:
  source: !prompt
    message: Enter Password
  sensitive: true
---
# Prompt the user to select a value from a static list
fruit:
  source: !select
    message: Select Fruit
    options:
      - apple
      - banana
      - guava
---
# Use a value from another response
# Assume the request recipe with ID `login` returns a body like `{"token": "foo"}`
auth_token:
  source: !request
    recipe: login
  selector: $.token
---
# Use the output of an external command
username:
  source: !command
    command: [whoami]
    trim: both # Shell commands often include an unwanted trailing newline

See Chain Source for more detailed examples of the various sources, and how they can be combined together.

Chain Source

A chain source defines how a Chain gets its value. It populates the source field of a chain. There are multiple source types, and the type is specified using YAML's tag syntax.

Examples

See the Chain docs for more complete examples.

!request
recipe: login
trigger: !expire 12h
---
!command
command: ["echo", "-n", "hello"]
---
!env
variable: USERNAME
---
!file
path: ./username.txt
---
!prompt
message: Enter Password

Variants

VariantTypeDescription
!requestChainSource::RequestBody of the most recent response for a specific request recipe.
!commandChainSource::CommandStdout of the executed command
!envChainSource::EnvironmentValue of an envionrment variable, or empty string if undefined
!fileChainSource::FileContents of the file
!promptChainSource::PromptValue entered by the user
!selectChainSource::SelectUser selects a value from a list

Request

Chain a value from the body of another response. This can reference either

FieldTypeDescriptionDefault
recipestringRecipe to load value fromRequired
triggerChainRequestTriggerWhen the upstream recipe should be executed, as opposed to loaded from memory!never
sectionChainRequestSectionThe section (header or body) of the request from which to chain a valueBody

Chain Request Trigger

This defines when a chained request should be triggered (i.e. when to execute a new request) versus when to use the most recent from history.

VariantTypeDescription
neverNoneNever trigger. The most recent response in history for the upstream recipe will always be used; error out if there is none
no_historyNoneTrigger only if there is no response in history for the upstream recipe
expireDurationTrigger if the most recent response for the upstream recipe is older than some duration, or there is none
alwaysNoneAlways execute the upstream request

Duration is specified as an integer followed by a unit (with no space). Supported units are:

  • s (seconds)
  • m (minutes)
  • h (hours)
  • d (days)

Examples

!request
recipe: login
trigger: !never # This is the default, so the same as omitting
---
!request
recipe: login
trigger: !no_history
---
!request
recipe: login
trigger: !expire 12h
---
!request
recipe: login
trigger: !expire 30s
---
!request
recipe: login
trigger: !always

Chain Request Section

This defines which section of the response (headers or body) should be used to load the value from.

VariantTypeDescription
bodyNoneThe body of the response
headerTemplateA specific header from the response. If the header appears multiple times in the response, only the first value will be used

Examples

!request
recipe: login
section: !header Token # This will take the value of the 'Token' header

Command

Execute a command and use its stdout as the rendered value.

FieldTypeDescriptionDefault
commandTemplate[]Command to execute, in the format [program, ...arguments]Required
stdinTemplateStandard input which will be piped into the commandNone
username:
  source: !command
    command: [whoami]

Environment Variable

Load a value from an environment variable.

FieldTypeDescriptionDefault
variableTemplateVariable to loadRequired

Examples

current_dir:
  source: !env
    variable: PWD

File

Read a file and use its contents as the rendered value.

FieldTypeDescriptionDefault
pathTemplatePath of the file to load (relative to current directory)Required

Examples

username:
  source: !file
    path: ./username.txt

Prompt

Prompt the user for text input to use as the rendered value.

FieldTypeDescriptionDefault
messageTemplateDescriptive prompt for the userChain ID
defaultTemplateValue to pre-populated the prompt textbox. Note: Due to a library limitation, not supported on chains with sensitive: true in the CLInull

Examples

password:
  source: !prompt
    message: Enter Password
  sensitive: true

Select

Prompt the user to select a defined value from a list.

FieldTypeDescriptionDefault
messageTemplateDescriptive prompt for the userChain ID
optionsSelectOptionsList of options to present to the userRequired

Select Options

The list of options to present to the user. This can be a static list of values or a dynamic template to generate the list of options.

VariantTypeDescription
fixedTemplate[]Fixed list of options
dynamicTemplateTemplate that renders to a JSON array

Examples

fruit:
  source: !select
    message: Select Fruit
    options:
      - apple
      - banana
      - guava
fruit_response:
  source: !request
    recipe: list_fruit
  # Assume this response body looks like:
  # [{ "name": "apple" }, { "name": "guava" }, { "name": "pear" }]
  selector: $[*].name
dynamic_fruit:
  source: !select
    message: Select Fruit
    # This option is powerful when combined with the `selector` field on an
    # upstream chain
    options: "{{chains.fruit_response}}"

Content Type

Content type defines the various data formats that Slumber recognizes and can manipulate. Slumber is capable of displaying any text-based data format, but only specific formats support additional features such as querying and formatting.

Auto-detection

For chained requests, Slumber uses the HTTP Content-Type header to detect the content type. For chained files, it uses the file extension. For other chain sources, or Slumber is unable to detect the content type, you'll have to manually provide the content type via the chain content_type field.

Supported Content Types

Content TypeHTTP HeaderFile Extension(s)
JSONapplication/jsonjson

Configuration

Configuration provides global settings for all of Slumber, as opposed to collection-level settings.

Location & Creation

By default, configuration is stored in a platform-specific configuration directory, according to dirs::config_dir.

PlatformPath
Linux$HOME/.config/slumber/config.yml
MacOS$HOME/Library/Application Support/slumber/config.yml
WindowsC:\Users\<User>\AppData\Roaming\slumber\config.yml

You can also find the config path by running:

slumber show paths config

You can open the config file in your preferred editor with:

slumber show config --edit

If the config directory doesn't exist yet, Slumber will create it automatically when starting the TUI for the first time.

Note: Prior to version 2.1.0, Slumber stored configuration in a different location on Linux (~/.local/share/slumber/config.yml). If that file exists on your system, it will be used in place of the newer location. For more context, see issue #371.

You can change the location of the config file by setting the environment variable SLUMBER_CONFIG_PATH. For example:

SLUMBER_CONFIG_PATH=~/dotfiles/slumber.yml slumber

Fields

The following fields are available in config.yml:

commands.shell

Type: string[]

Default: [sh, -c] (Unix), [cmd, /S, /C] (Windows)

Shell used to execute commands within the TUI. Use [] for no shell (commands will be parsed and executed directly). More info

commands.default_query

Type: string or mapping[Mime, string] (see MIME Maps)

Default: ""

Default query command for all responses. More info

debug

Type: boolean

Default: false

Enable developer information in the TUI

editor

Type: string

Default: VISUAL/EDITOR env vars, or vim

Command to use when opening files for in-app editing. More info

follow_redirects

Type: boolean

Default: true

Enable/disable following redirects (3xx status codes) automatically. If enabled, the HTTP client follow redirects up to 10 times.

ignore_certificate_hosts

Type: string

Default: []

Hostnames whose TLS certificate errors will be ignored. More info

input_bindings

Type: mapping[Action, KeyCombination[]]

Default: {}

Override default input bindings. More info

large_body_size

Type: number

Default: 1000000 (1 MB)

Size over which request/response bodies are not formatted/highlighted, for performance (bytes)

persist

Type: boolean

Default: true

Enable/disable the storage of requests and responses in Slumber's local database. This is only used in the TUI. CLI requests are not persisted unless the --persist flag is passed, in which case they will always be persisted. See here for more.

preview_templates

Type: boolean

Default: true

Render template values in the TUI? If false, the raw template will be shown.

theme

Type: Theme

Default: {}

Visual customizations for the TUI. More info

pager

Alias: viewer (for historical compatibility)

Type: string or mapping[Mime, string] (see MIME Maps)

Default: less (Unix), more (Windows)

Command to use when opening files for viewing. More info

Ignored Fields

In addition to the above fields, any top-level field beginning with . will be ignored. This can be combined with YAML anchors to define reusable components in your config file.

Input Bindings

You can customize all input bindings in the configuration. An input binding is a mapping between an action (a high-level verb) and one or more key combinations.

For example if you want vim bindings (h/j/k/l instead of left/down/up/right):

# config.yaml
input_bindings:
  up: [k]
  down: [j]
  left: [h]
  right: [l]
  scroll_left: [shift h]
  scroll_right: [shift l]
  select_recipe_list: [w] # Rebind from `l`

Each action maps to a list of key combinations, because you can map multiple combinations to a single action. Hitting any of these combinations will trigger the action. By defining a binding in the config, you will replace the default binding for that action. If you want to retain the default binding but add an additional, you will need to include the default in your list of custom bindings. For example, if you want vim bindings but also want to leave the existing arrow key controls in place:

input_bindings:
  up: [up, k]
  down: [down, j]
  left: [left, h]
  right: [right, l]
  scroll_left: [shift left, shift h]
  scroll_right: [shift right, shift l]
  select_recipe_list: [w] # Rebind from `l`

Actions

ActionDefault BindingDescription
left_clickNone
right_clickNone
scroll_upNone
scroll_downNone
scroll_leftshift left
scroll_rightshift right
quitqExit current dialog, or the entire app
force_quitctrl cExit the app, regardless
previous_paneshift tabSelect previous pane in the cycle
next_panetab
upup
downdown
leftleft
rightright
page_uppgup
page_downpgdn
homehome
endend
submitenterSend a request, submit a text box, etc.
togglespaceToggle a checkbox on/off
cancelescCancel current dialog or request
deletedeleteDelete the selected object (e.g. a request)
editeApply a temporary override to a recipe value
resetrReset temporary recipe override to its default
viewvOpen the selected content (e.g. body) in your pager
historyhOpen request history for a recipe
search/Open/select search for current pane
export:Enter command for exporting response data
reload_collectionf5Force reload collection file
fullscreenfFullscreen current pane
open_actionsxOpen actions menu
open_help?Open help dialog
select_collectionf3Open collection select dialog
select_profile_listpOpen Profile List dialog
select_recipe_listlSelect Recipe List pane
select_recipecSelect Recipe pane
select_responsesSelect Request/Response pane
select_requestrSelect Request/Response pane (backward compatibility)

Note: mouse bindings are not configurable; mouse actions such as left_click can be bound to a key combination, which cannot be unbound from the default mouse action.

Key Combinations

A key combination consists of zero or more modifiers, followed by a single key code. The modifiers and the code all each separated by a single space. Some examples:

  • w
  • shift f2
  • alt shift c
  • ctrl alt delete

Key Codes

All single-character keys (e.g. w, /, =, etc.) are not listed; the code is just the character.

  • escape/esc
  • enter
  • left
  • right
  • up
  • down
  • home
  • end
  • pageup/pgup
  • pagedown/pgdn
  • tab
  • backtab (equivalent to shift tab, supported for backward compatibility)
  • backspace
  • delete/del
  • insert/ins
  • capslock/caps
  • scrolllock
  • numlock
  • printscreen
  • pausebreak (sometimes just called Pause; not the same as the Pause media key)
  • menu
  • keypadbegin
  • f1
  • f2
  • f3
  • f4
  • f5
  • f6
  • f7
  • f8
  • f9
  • f10
  • f11
  • f12
  • space
  • play
  • pause (the media key, not Pause/Break)
  • playpause
  • reverse
  • stop
  • fastforward
  • rewind
  • tracknext
  • trackprevious
  • record
  • lowervolume
  • raisevolume
  • mute

Key Modifiers

  • shift
  • alt
  • ctrl
  • super
  • hyper
  • meta

MIME Maps

Some configuration fields support a mapping of MIME types (AKA media types or content types). This allow you to set multiple values for the configuration field, and the correct value will be selected based on the MIME type of the relevant recipe/request/response.

The keys of this map are glob-formatted (i.e. wildcard) MIME types. For example, if you're configuring your pager and you want to use hexdump for all images, fx for JSON, and less for everything else:

pager:
  image/*: hexdump
  application/json: fx
  "*/*": less

Note: Paths are matched top to bottom, so */* should always go last. Any pattern starting with * must be wrapped in quotes in order to be parsed as a string.

  • image/png: matches image/*
  • image/jpeg: matches image/*
  • application/json: matches application/json
  • text/csv: matches */*

Aliases

In addition to accepting MIME patterns, there are also predefined aliases to make common matches more convenient:

AliasMaps To
default*/*
jsonapplication/*json
imageimage/*

Notes on Matching

  • Matching is done top to bottom, and the first matching pattern will be used. For this reason, your */* pattern should always be last.
  • Matching is performed just against the essence string of the recipe/request/response's Content-Type header, i.e. the type/subtype only. In the example multipart/form-data; boundary=ABCDEFG, the semicolon and everything after it is not included in the match.
  • Matching is performed by the Rust glob crate. Despite being intended for matching file paths, it works well for MIME types too because they are also /-delimited

Theme

Theming allows you to customize the appearance of the Slumber TUI. To start, open up your configuration file and add some theme settings:

theme:
  primary_color: green
  secondary_color: blue

Fields

FieldTypeDescription
primary_colorColorColor of most emphasized content
primary_text_colorColorColor of text on top of the primary color (generally white or black)
secondary_colorColorColor of secondary notable content
success_colorColorColor representing successful events
error_colorColorColor representing error messages

Color Format

Colors can be specified as names (e.g. "yellow"), RGB codes (e.g. #ffff00) or ANSI color indexes. See the Ratatui docs for more details on color deserialization.

Neovim Integration

Slumber can be integrated into Neovim to allow you quickly switch between your codebase and Slumber.

Ensure you have which-key and toggleterm installed. Most premade distros (LunarVim, etc) will have these installed by default.

Add this snippet to your Neovim config:

local Slumber = {}
Slumber.toggle = function()
    local Terminal = require("toggleterm.terminal").Terminal
    local slumber = Terminal:new {
        cmd = "slumber",
        hidden = true,
        direction = "float",
        float_opts = {
            border = "none",
            width = 100000,
            height = 100000,
        },
        on_open = function(_)
            vim.cmd "startinsert!"
        end,
        on_close = function(_) end,
        count = 99,
    }
    slumber:toggle()
end

local wk = require("which-key")
wk.add({
    -- Map space V S to open slumber 
    { "<leader>vs", Slumber.toggle, desc = "Open in Slumber"}
})

Logs

Each Slumber session logs information and events to a log file. This can often be helpful in debugging bugs and other issues with the app. All sessions of Slumber log to the same file. Currently there is no easy to way disambiguate between logs from parallel sessions :(

Finding the Log File

To find the path to the log file, hit the ? to open the help dialog. It will be listed under the General section. Alternatively, run the command slumber show paths log.

Once you have the path to a log file, you can watch the logs with tail -f <log file>, or get the entire log contents with cat <log file>.

Increasing Verbosity

In some scenarios, the default logging level is not verbose enough to debug issues. To increase the verbosity, set the RUST_LOG environment variable when starting Slumber:

RUST_LOG=slumber=<level> slumber ...

The slumber= filter applies this level only to Slumber's internal logging, instead of all libraries, to cut down on verbosity that will likely not be helpful. The available log levels are, in increasing verbosity:

  • error
  • warn
  • info
  • debug
  • trace

Lost Request History

If you've lost your request history, there are a few possible causes. In most cases request history is non-essential, but if you really want it back there may be a fix available.

If none of these fixes worked for you, and you still want your request history back, please open an issue and provide as much detail as possible.

Moved Collection File

If history is lost for an entire collection, the most likely cause is that you moved your collection file. To fix this, you can migrate your request history.

Changed Recipe ID

If you've lost request history for just a single recipe, you likely changed the recipe ID, which is the key associated with the recipe in your collection file (the parent folder(s) do not affect this). Unfortunately currently the only way to fix this is to revert to the old recipe ID.

Wrong Profile or Changed Profile ID

Each request+response in history is associated with a specific profile. If you're not seeing your expected request history, you may have a different profile selected than the one used to send the request(s).

Alternatively, you may have changed the ID of the associated profile. If so, unfortunately the only way to fix this is to revert to the old profile ID.

TLS Certificate Errors

If you're receiving certificate errors such as this one:

invalid peer certificate: UnknownIssuer

This is probably because the TLS certificate of the server you're hitting is expired, invalid, or self-signed. The best solution is to fix the error on the server, either by renewing the certificate or creating a signed one. In most cases this is the best solution. If not possible, you should just disable TLS on your server because it's not doing anything for you anyway.

If you can't or don't want to fix the certificate, and you need to keep TLS enabled for some reason, it's possible to configure Slumber to ignore TLS certificate errors on certain hosts.

WARNING: This is dangerous. You will be susceptible to MITM attacks on these hosts. Only do this if you control the server you're hitting, and are confident your network is not compromised.

  • Open your Slumber configuration
  • Add the field ignore_certificate_hosts: ["<hostname>"]
    • <hostname> is the domain or IP of the server you're requesting from

Changelog

You can also browse release history via the GitHub Releases page.

[3.4.0] - 2025-08-17

This release focuses on improving collection history management. The slumber collections subcommand is fairly niche, but is now a bit more powerful and user friendly.

Added

  • Add slumber collections delete subcommand to remove collections from history
  • Add slumber history rm as an alias for slumber history delete
  • Add filter box to collection select dialog. Less scrolling, more typing!

Changed

  • slumber collections migrate now accepts collection IDs in addition to paths
  • slumber collections list now includes an ID column in its output

Fixed

  • Shell completion for the global --file/-f flag will now only .yml/.yaml files
  • slumber collections migrate now accepts paths for files that don't exist on disk (as long as they existed as collections at some point)

[3.3.0] - 2025-07-23

Added

  • Add collection switcher modal #536
    • You can now switch between any collection that you've opened in the past without having to close Slumber
  • Add optional root-level name field to collections
    • This allows you to provide a descriptive name for the collection to show in the new switcher modal
    • The name is purely descriptive and does not need to be unique

Changed

  • Update to Rust 1.88

Fixed

  • Fix empty actions modal queuing when opening actions while another modal is already open
  • Fix crash when a select modal is opened with a very long option

[3.2.0] - 2025-06-20

Added

  • Add config field follow_redirects to enable/disable following 3xx redirects (enabled by default)
    • The behavior has always been to follow redirects, so this adds the ability to disable that globally
    • Reminder: Global configuration is not automatically reloaded. After making changes to your config.yml, you'll need to restart Slumber for changes to take effect
    • See docs for more
  • Add optional target argument to slumber show paths to show just a single path
    • E.g. slumber show paths config prints just the config path
  • Add --edit flag to slumber show config and slumber show collection
    • This will open the global config/collection file in your configured editor, similar to git config --edit
  • slumber import now supports importing from stdin or a URL
    • If no input argument is given, it will read from stdin, e.g. slumber import openapi < openapi.json
    • If a URL is given, the file will be downloaded and imported, e.g. slumber import openapi https://example.com/openapi.json
  • Add OpenAPI v3.1 importer #513

Changed

  • Any top-level fields in the config or collection file beginning with . will now be ignored
    • The goal is to support "hidden" fields to store reusable components. YAML aliases can be used to pull those components into various parts of your collection
    • Previously the field .ignore was specially supported in the collection format for this purpose; this is a generalization of that special case.

Fixed

  • Import JSON bodies from OpenAPI spec operations that don't have an example field
    • Now it will infer a body from the schema definition if no examples are provided
  • Fix occasional hang when opening fx as a pager #506

[3.1.3] - 2025-06-07

Fixed

  • Fix pager view action (regression in 3.1.2)
  • Fully restore terminal before exiting to editor/pager
    • This means any stdout/stderr that the external process writes will be properly formatted in the terminal

[3.1.2] - 2025-05-30

Changed

  • Use a dedicated error state if collection fails to load on TUI launch
  • Update dependency persisted to 1.0
    • A few pieces of your UI state, such as selected tabs, will be lost during the upgrade due to this

Fixed

  • Fix TUI crash when using an empty select list
  • Fix automatic collection reloading in Windows
    • I don't use Windows so I'm not sure exactly what scenarios it may have been broken in, but new unit tests indicate it's working now
  • Fix config loading failing for read-only config files (#504)

[3.1.1] - 2025-04-23

Fixed

  • Persist response query commands separately for each content type
    • This prevents commands from running on the incorrect content type when the response type changes

[3.1.0] - 2025-04-04

This releases focuses on history and data management. A suite of new features and improvements make it easy to disable request persistence and delete past requests from history.

Added

  • Add --persist flag to slumber request
    • By default, CLI-based requests are not stored in the history database. Use this flag to enable persistence for the sent request.
  • Add slumber history delete subcommand for deleting request history
  • Add slumber db subcommand to open a shell to the local SQLite database
  • Add persist field to the global config and individual recipes
    • Both default to true, but you can now set them to false to disable data persistence a single recipe, or all instances of the app. See here for more
  • Add actions to delete requests from the TUI
    • Delete a single request from the history modal or the Request/Response pane
    • Delete all requests for a recipe from the Recipe List/Recipe panes

Changed

  • Upgrade to Rust 1.86 (2024 edition!)
  • Improve functionality of slumber history list
    • recipe argument is optional now. Omit it to show requests for all recipes in the current collection
    • Add --all argument to show requests for all collections
    • Add --id-only flag to print only IDs with no headers. Combine with slumber history delete for great success!
  • Improve format of slumber history list table output

Fixed

  • Fix output format of slumber request --dry-run ... to match slumber request --verbose
  • Fix curl output for URL-encoded and multipart forms
  • Fix selected request not changing when profile changes

[3.0.1] - 2025-02-19

Fixed

  • Text box now scrolls to the cursor when it's off screen
  • Fix panics when the screen gets very small #469

[3.0.0] - 2025-02-15

A major release! The main focus of this release is the introduction of shell commands for data querying and export. Previously, you could query response bodies within the TUI only using JSONPath. This limited querying only to JSON responses, and the limited amount of operators supported by JSON. Now, you can use whatever shell commands you want (such as head, grep, and jq) to filter your reponses bodies, right in the TUI! Check out the docs for more examples.

In addition to the querying change, this release includes a handful of breaking changes, none of which are likely to cause issues for existing users.

Breaking

  • Don't store CLI requests in history
  • Simplify display for slumber request
    • The flags --status, --headers and --no-body have been removed in favor of a single --verbose flag
  • Remove DB migration to upgrade from the pre-1.8.0 DB format
    • This only impacts users upgrading to 3.0.0 from versions before 1.8.0. You'll need to upgrade to an intermediate version first. If you install 3.0.0 and try to start it, you'll see an error message explaining how to fix it.
    • See #306 for more info

Added

  • Replace JSONPath querying with general purpose shell commands for querying response bodies. See docs
    • Now you can access any CLI tools you want for transforming response bodies, such as jq or grep
    • By default, commands are executed via sh (or cmd on Windows), but this is configured via the commands.shell field
  • Add keybind (: by default) to run an "export" command with a response body, allowing you to run arbitrary shell commands to save a response body to a file, copy it to the clipboard, etc. See docs
  • Add slumber history subcommand. Currently it has two operations:
    • slumber history list lists all stored requests for a recipe
    • slumber history get prints a specific request/response
  • Add --output flag to slumber request to control where the response body is written to
  • Support MIME type mapping for pager config field, so you can set different pagers based on media type. See docs
  • Several changes related to keybinds and action menus to make the two feel more cohesive
    • Add "Edit" and "Reset" actions to menus on the recipe pane
      • These don't provide any new functionality, as the e and z keys are already bound to those actions, but it should make them more discoverable
    • Add keybind (v by defualt) to open a recipe/request/response body in your pager
      • Previously this was available only through the actions menu
    • "View Body" and "Copy Body" actions for a recipe are now only available within the Body tab of the Recipe pane
      • Previously they were available anywhere in the Recipe List or Recipe panes. With the addition of other actions to the menu it was started to feel cluttered

Changed

  • Denote templates that have been edited during the current session with italics instead of a faint "(edited)" note
  • Header names in recipes are now lowercased in the UI
    • They have always been lowercased when the request is actually sent, so now the UI is just more representative of what will be sent
  • Accept a directory for the --file/-f CLI argument

Fixed

  • Fix certain recipe-related menu actions being enabled when they shouldn't be

[2.5.0] - 2025-01-06

Added

  • Add REST Importer for VSCode and Jetbrains #122 (thanks @benfaerber)

Changed

  • Update editor-command, which replaces shellish_parse with shell-words for editor and pager command parsing
    • There should be no impact to users
  • Don't show "Loaded collection from ..." notification on initial load

[2.4.0] - 2024-12-27

Added

  • Add filter box to the recipe list
    • This behavior is not necessarily final. Please leave feedback if you think it could be improved.

Changed

  • Wrap long error messages in response pane
  • Include data path in config/collection deserialization errors
    • This should make errors much less cryptic and frustrating
  • Improve UX of query text box
    • The query is now auto-applied when changed (with a 500ms debounce), and drops focus on the text box when Enter is pressed
  • Refactor UI event handling logic
    • This shouldn't have any noticable impact on the user, but if you notice any bugs please open an issue
  • Include request duration in History modal
  • Rename viewer config field to pager
    • The old field name viewer is still supported for backward compatibility, but the docs have been updated to suggest the newer name instead
  • Load pager command from the PAGER environment variable if available, similar to the EDITOR environment variable

Fixed

  • Don't show request cancellation dialog if the selected request isn't building/loading

[2.3.0] - 2024-11-11

Added

  • Add "View Body" action to response bodies, to open a body in an external viewer such as less or fx #404

Changed

  • Preserve key order of objects in JSON responses #405

Fixed

  • Fixed ignore_certificate_hosts and large_body_size fields not being loaded from config
  • Improve performance of large response bodies #356
    • This includes disabling prettyification and syntax highlighting on bodies over 1 MB (this size is configurable, via the large_body_size config field)
    • Loading a large response body should no longer cause the UI to freeze or low framerate

[2.2.0] - 2024-10-22

Added

  • Add shell completions, accessed by enabling the COMPLETE environment variable
    • For example, adding COMPLETE=fish slumber | source to your fish.config will enable completions for fish
    • See docs for more info and a list of supported shells
  • Add slumber gen alias to --help documentation

Fixed

  • Fix error loading requests with empty header values from history #400
  • Fix input bindings involving shift and a character (e.g. shift g) #401

[2.1.0] - 2024-09-27

Added

  • Use SLUMBER_CONFIG_PATH to customize configuration (not collection) file path #370
  • Add a dynamic variant to !select chain type, allowing your collection to present a list of values driven from the output of another chain. (thanks @anussel5559)
  • Cancel in-flight requests with the cancel action (bound to escape by default)
  • Add slumber new subcommand to generate new collection files #376
  • Add default field to profiles
    • When using the CLI, the --profile argument can be omitted to use the default profile
  • Reset edited recipe values to their default using z
  • Add selector_mode field to chains, to control how single vs multiple results from a JSONPath selector are handled
    • Previously, if a selector returned multiple results, an error was returned. Now, the result list will be rendered as a JSON array. To return to the previous behavior, set selector_mode: single in your chain.
    • See docs for more

Changed

  • Update file locations to adhere to XDG spec on Linux #371
    • Move config file to config dir, which remains the same on MacOS/Windows but changes on Linux. For backward compatibility, the previous path (data dir) will be checked and used if present
    • Move log files to state dir on Linux and cache dir on MacOS/Windows
    • Database file remains in data dir on all platforms
  • Create config file on startup if it doesn't exist
  • If config file fails to load during TUI startup, display an error and fall back to the default, rather than crashing
  • De-deprecate {{env.VARIABLE}} template sources
    • They'll remain as a simpler alternative to !env chains

Fixed

  • Updated the Configuration docs to remove the non-existent slumber show dir command (thanks @SVendittelli)
  • Retain all request history when collection file is reloaded
    • Previously, pending and failed requests were lost on reload within a single session. These will still be lost when a session is exited.
  • Fix serialization of query parameter lists
  • Don't update UI for useless events (e.g. cursor moves)

[2.0.0] - 2024-09-06

2.0 is headlined by a highly requested feature: one-off edits to recipes! If you need to tweak a query parameter or edit a body, but don't want to modify your collection file, you can now highlight the value in question and hit e to modify it. The override will be retained until you modify the collection file or exit Slumber, at which point it will revert to its original value.

Aside from the major new feature, there is one breaking change to the escape syntax of templates. The old backslash-based syntax was fraught with edge cases and unpredictable behavior. This new syntax is simpler to use, simpler to implement, and much more bulletproof. This syntax was rare to use to begin with, so most people will be unimpacted by this change.

Here's the full list of changes:

Breaking

  • Replace backslash escape sequence with a simpler scheme based on _
    • For example, previously a key would be escaped as \{{. This introduced complexities around how to handle additional backslashes, and also required doubling up backslashes in YAML
    • The new equivalent would be {_{, which parses as {{
    • The goal of this change is to make escaping behavior simpler and more consistent
    • For more info on the new behavior, see the docs
  • Remove --log CLI argument
    • See note on log files in Changed section for why this is no longer necessary

Added

  • Edit recipe values (query params, headers, etc.) in the TUI to provide one-off values
  • Add editor field to the config, allowing you to customize what editor Slumber opens for in-app editing
  • Add !select chain type, allowing your collection to prompt the user to select a value from a static list (thanks @anussel5559)

Changed

  • !json bodies are now prettified when sent to the server
  • Use vim as default editor if none is configured
  • Move logs back to a shared file
    • They had been split into one file per session, which made them hard to find
    • The file is now eventually deleted once it exceeds a certain size

Fixed

  • Fix basic auth being label as bearer auth in Recipe Authentication pane
  • Use correct binding for search action in the placeholder of the response filter textbox
    • Previously it was hardcoded to display the default of /
  • Fix response body filter not applying on new responses
  • Support quoted arguments in editor commands
  • Fix certain UI values not persisting correctly
  • Propagate unconsumed key events from text boxes
    • E.g. F5 will now refresh the collection while a text box is in focus
  • Redraw TUI when terminal is resized
  • Clamp text window scroll state when window is resized or text changes
  • Fix extraneous input events when exiting Vim #351
  • Improve performance and fix crashes when handling large request/response bodies #356
    • Further improvements for large bodies will be coming in the future

[1.8.1] - 2024-08-11

This release is focused on improving rendering performance. The TUI should generally feel more polished and responsive when working with large bodies, and CPU usage will be much lower.

Added

  • Add debug configuration field, to enable developer information

Fixed

  • Reduce CPU usage while idling
    • Previously, Slumber would re-render every 250ms while idling, which could lead to high CPU usage, depending on what's on the screen. Now it will only update when changes occur, meaning idle CPU usage will be nearly 0
  • Fix backlogged events when renders are slow
    • If renders are being particular slow, it was previously possible for input events (e.g. repeated scrolling events) to occur faster than the UI could keep up. This would lead to "lock out" behavior, where you'd stop scrolling and it'd take a while for the UI to catch up.
    • Now, the TUI will skip draws as necessary to keep up with the input queue. In practice the skipping should be hard to notice as it only occurs during rapid TUI movements anyway.
  • Improve rendering performance for large bodies and syntax highlighting
  • Fix incorrect decoration in folder tree visualization

[1.8.0] - 2024-08-09

The highlight (no pun intended) of this release is syntax highlighting. Beyond that, the release contains a variety of small fixes and improvements.

Added

  • Add syntax highlighting to recipe, request, and response display #264

Changed

  • Change layout of internal database for request and UI state storage
    • This shouldn't have any user impact, it's just a technical improvement. If you notice any issues such as missing or incorrect request history, please let me know
  • Upgrade to Rust 1.80
  • Disable unavailable menu actions #222
  • Support template for header names in the section field of !request chains
  • Expand ~ to the home directory in !file chain sources and when saving response body as a file
  • Ignore key events with additional key modifiers
    • For example, an action bound to w will no longer match ctrl w
  • Actions can now be unbound by specifying an empty binding
    • For example, binding submit: [] will make the submit action inaccessible

Fixed

  • Fix cargo install slumber when not using --locked
  • Don't type in text boxes when modifiers keys (other than shift) are enabled
    • Should mitigate some potential confusing behavior when using terminal key sequences
  • Query parameter and header toggle rows no longer lose their state when switching profiles

[1.7.0] - 2024-07-22

This release focuses on minor fixes and improvements. There are no new major features or added functionality.

Added

  • Add global --log argument to CLI, to print the log file being used for that invocation

Changed

  • Checkbox row state and folder expand/collapse state are now toggled via the spacebar instead of enter
    • Enter now sends a request from anywhere. While this change may be annoying, it will hopefully be more intuitive in the long run.
    • This can be rebound (see docs)
  • Show folder tree in recipe pane when a folder is selected
  • Don't exit body filter text box on Enter #270
  • Show elapsed time for failed requests (e.g. in case of network error)

Fixes

  • Fix latest request not being pre-selected correctly if it's not a successful response
  • Detect infinite loops in chain configuration templates
  • Duplicated chains in a recipe will only be rendered once #118
  • Never trigger chained requests when rendering template previews in the TUI
  • Use a different log file for each session #61

[1.6.0] - 2024-07-07

Added

  • Initial support for importing collections from an OpenAPIv3 specification #106
    • Currently only OpenAPI 3.0 (not 3.1) is supported. Please try this out and give feedback if anything doesn't work.

Changed

  • Allow escaping keys in templates #149
    • While this is technically a breaking change, this is not a major version bump because it's extremely unlikely that this will break anything in practice for a user
    • See docs

Fixed

  • Support TLS certificates in native certificate store #275

[1.5.0] - 2024-06-17

Added

  • Add !env chain source, for loading environment variables
    • This is intended to replace the existing {{env.VARIABLE}} syntax, which is now deprecated and will be removed in the future

Changed

  • "Edit Collection" action now uses the editor set in $VISUAL/$EDITOR instead of whatever editor you have set as default for .yaml/.yml files #262
    • In most cases this means you'll now get vim instead of VSCode or another GUI editor
    • Closing the editor will return you to Slumber, meaning you can stay in the terminal the entire time

Fixed

  • Environment variables in {{env.VARIABLE}} templates are now loaded as strings according to the OS encoding, as opposed to always being decoded as UTF-8

[1.4.0] - 2024-06-11

Added

  • Structured bodies can now be defined with tags on the body field of a recipe, making it more convenient to construct bodies of common types. Supported types are:
  • Support multiple instances of the same query param #245 (@maksimowiczm)
    • Query params can now be defined as a list of <param>=<value> entries
    • See docs
  • Templates can now render binary values in certain contexts

Changed

  • When a modal/dialog is open q now exits the dialog instead of the entire app
  • Upgrade to Rust 1.76

Fixed

  • Fix "Unknown request ID" error showing on startup #238

[1.3.2] - 2024-05-27

Changed

  • Show "Copy URL", "Copy Body" and "Copy as cURL" actions from the Recipe list #224
    • Previously this was only available in the Recipe detail pane
  • Fix Edit Collection action in menu
  • Persist response body query text box contents
    • Previously it would reset whenever you made a new request or changed recipes

[1.3.1] - 2024-05-21

Fixed

  • Fix double key events on Windows #226

[1.3.0] - 2024-05-17

The biggest feature in this release is the ability to browse request history. Slumber has already had the ability to track history, meaning all your history since you started using it will already be there! In addition, this release contains some UI improvements, as well as some pretty major internal refactors to enable these UI changes. These will also make future UI improvements easier and faster to implement.

Added

  • Request history is now browsable! #55
  • Add scrollbars to lists and text windows #220

Changed

  • Merge request & response panes
    • The request pane often isn't needed, so it doesn't deserve top-level space
  • Mouse events (e.g. scrolling) are now sent to unfocused elements

[1.2.1] - 2024-05-11

Fixed

  • Fix profile not being selected on initial startup

[1.2.0] - 2024-05-10

Added

  • Add trim option to chains, to trim leading/trailing whitespace #153

Changed

  • Use colored background for status codes
    • This includes a new theme field, success_color
  • Improve hierarchy presentation of errors
  • Convert profile list into a popup modal

Fixed

  • Exit fullscreen mode when changing panes
  • Support scrolling on more lists/tables

[1.1.0] - 2024-05-05

Added

  • Add section field to !request chain values, to allow chaining response headers rather than body (#184)
  • Add action to save response body to file (#183)
  • Add theme field to the config, to configure colors (#193)
  • Add stdin option to command chains (#190)

Changed

  • Reduce UI latency under certain scenarios
    • Previously some actions would feel laggy because of an inherent 250ms delay in processing some events
  • Search parent directories for collection file (#194)
  • Use thicker borders for selected pane and modals
  • Change default TUI colors to blue and yellow

Fixed

  • Fix Slumber going into zombie mode and CPU spiking to 100% under certain closure scenarios (#136)
  • Fix historical request/response no loading on first render (#199)

[1.0.1] - 2024-04-27

Added

  • Add two new build targets to releases: x86_64-pc-windows-msvc and x86_64-unknown-linux-musl

Fixed

  • Fix build on Windows (#180)
    • I can't guarantee it works on Windows since I don't have a machine to test on, but it at least compiles now

[1.0.0] - 2024-04-25

Breaking

  • Rename collection file parameter on all CLI commands from --collection/-c to --file/-f
    • The goal here is to be more intuitive/predictable, since -f is much more common in similar programs (e.g. docker-compose)

Added

  • Support booleans and numbers for query values (#141)
  • Add default field to !prompt chains, which allows setting a pre-populated value for the prompt textbox

Changed

  • Folders can now be collapsed in the recipe list (#155)
  • Improvements to Insomnia import (#12)
  • Rename import-experimental command to import
    • It's official now! It's still going to get continued improvement though
  • Show WARN/ERROR log output for CLI commands
  • Validate recipe method field during deserialization instead of on request init
    • This means you'll get an error on startup if your method is invalid, instead of when you go to run the request
    • This is not a breaking change because if you had an incorrect HTTP method, the request still didn't work before, it just broke later
  • Arguments to chains are now treated as templates (#151)
    • Support fields are path for !file chains, command for !command chains, and message for !prompt chains
    • This means you can now really chain chains together!

[0.18.0] - 2024-04-18

Breaking

  • All existing recipes must be tagged with !request in the collection file
    • This is necessary to differentiate from the new !folder type
  • Profile values are always treated as templates now
    • Any profile values that were previously the "raw" variant (the default) that contain template syntax (e.g. {{user_id}}) will now be rendered as templates. In reality this is very unlikely, so this probably isn't going to break your setup
    • If you have an existing profile value tagged with !template it won't break, but it will no longer do anything
  • Unknown fields in config/collection files will now be rejected (#154)
    • In most cases this field is a mistake, so this is meant to make debugging easier
    • If you have an intentional unknown field, you can now nest it under .ignore to ignore it
  • Replace slumber show dir with slumber show paths

Added

  • Request recipes can now be organized into folders (#60)
  • Add slumber show config and slumber show collection subcommands

Changed

  • Prevent infinite recursion in templates
    • It now triggers a helpful error instead of a panic
  • Support additional key codes for input mapping, including media keys

Fixed

  • Multiple spaces between modifiers/key codes in a key combination are now ignored

[0.17.0] - 2024-04-08

Breaking

  • All variants of the Chain.source field are now maps
    • This is to support the next request auto-execution feature, as well as future proofing for additional chain configuration
  • Remove send_request keybinding
    • The submit keybinding is now used to send requests from all panes (except the profile pane)
    • This is only a breaking change if you have send_request remapped in your config file

Follow this mapping to update:

# Before
chains:
  auth_token:
    source: !request login
  username:
    source: !command ["echo", "-n", "hello"]
  username:
    source: !file ./username.txt
  password:
    source: !prompt Enter Password
---
# After
chains:
  auth_token:
    source: !request
      recipe: login
  username:
    source: !command
      command: ["echo", "-n", "hello"]
  username:
    source: !file
      path: ./username.txt
  password:
    source: !prompt
      message: Enter Password

Added

  • Chained requests can now be auto-executed according to various criteria (#140)
  • Add Authentication tab to recipe pane (#144)

Changed

  • Don't print full stack trace for failed CLI commands
  • Disable formatting and highlighting for response bodies over 1MB (size threshold customizable in the config)

Fixes

  • Improve performance of handling large response bodies

[0.16.0] - 2024-04-01

Added

  • Add support for custom keybindings (#137)

Fixed

  • Fix request body not updating in UI when changing recipe

[0.15.0] - 2024-03-24

Added

  • Add horizontal scrolling to response body (#111)
    • Use shift+left and shift+right
  • Add app version to help modal
  • Add "Copy as cURL" action to recipe menu (#123)
  • Add hotkeys to select different panes
  • Add pane for rendered request
  • Show response size in Response pane (#129)

Changed

  • Run prompts while rendering request URL/body to be copied
  • Improve UI design of profile pane
  • Show raw bytes for binary responses

Fixed

  • Reset response body query when changing recipes (#133)

[0.14.0] - 2024-03-18

Added

  • Add config option ignore_certificate_hosts (#109)
  • Add menu action to open collection file in editor (#105)
  • Add authentication field to request recipe (#110)

Fixed

  • Fix prompt in TUI always rendering as sensitive (#108)
  • Fix content type identification for extended JSON MIME types (#103)
  • Use named records in binary blobs in the local DB
    • This required wiping out existing binary blobs, meaning all request history and UI state will be lost on upgrade
  • Fix basic auth in Insomnia import

[0.13.1] - 2024-03-07

Changed

  • Move checkbox to left side of toggle tables

Fixed

  • Fix scrolling on response body pane

[0.13.0] - 2024-02-21

Added

  • New informational flags to slumber request
    • --exit-status to set exit code based on response status (#97)
    • --status, --headers, and --no-body to control printed output
  • Filter response via JSONPath (#78)

[0.12.1] - 2024-01-22

Changed

  • Improved styling of toggled table rows

[0.12.0] - 2024-01-07

Added

  • Move app-level configuration into a file (#89)
    • Right now the only supported field is preview_templates
  • Toggle query parameters and headers in recipe pane (#30)
    • You can easily enable/disable parameters and headers without having to modify the collection file now
  • Add Copy URL action, to get the full URL that a request will generate (#93)

Changed

  • Show profile contents while in the profile list (#26)
  • Remove settings modal in favor of the settings file
    • Supporting changing configuration values during a session adds a lot of complexity

[0.11.0] - 2023-12-20

Added

  • Add action to copy entire request/response body (#74)
  • Persist UI state between sessions (#39)
  • Text window can be controlled with PgUp/PgDown/Home/End (#77)
  • Add back manual reload keybinding (R)
    • Mostly for development purposes
  • Add collection ID/path to help modal (#59)
    • Also add collection ID to terminal title
  • Add new docs for templates and collection reuse (#67)

Changed

  • [BREAKING] Key profiles/chains/requests by ID in collection file
  • [BREAKING] Merge request history into a single DB file
    • Request history (and UI state) will be lost
  • [BREAKING] show subcommand now takes a target argument
    • Right now the only option is slumber show dir, which has the same behavior as the old slumber show (except now it prints the bare directory)
  • [BREAKING] Remove option to toggle cursor capture
    • Turns out it's not that useful, since most terminals provide override behavior
  • Filter request history by profile (#74)
  • Hide sensitive chain values in preview
  • Change fullscreen keybinding from F11 to F
    • F11 in some cases is eaten by the IDE or OS, which is annoying

Fixed

  • Don't require collection file to be present for show subcommand (#62)
  • Fix state file being created in root Slumber directory if collection file is invalid (#71)
  • Fix pane cycling while in fullscreen (#76)

[0.9.0] - 2023-11-28

Added

  • Add setting to toggle cursor capture
  • Add help modal
  • Add cursor navigation

Changed

  • Always show help text in footer, regardless of notification state
  • Add highlight border to fullscreened pane
  • Allow exiting fullscreen mode with ESC

[0.8.0] - 2023-11-21

Added

  • Add slumber show subcommand

Changed

  • Remove keybinding to reload collection
    • Not useful now that the TUI has automatic reloading
  • Move to stable Rust channel and add MSRV of 1.74

Fixed

  • Don't panic if the collection file is invalid on first startup #34
    • The TUI will now show an empty screen, and watch the collection file for changes
  • Fix long status code reasons getting cut off in response header #40
  • Trim leading/trailing newlines from header values to prevent validation error

[0.7.0] - 2023-11-16

Added

  • Added recursive templates for profile values, using the !template tag before a value

Changed

  • Parse templates up front instead of during render
  • Switch to nom for template parsing
    • Parse errors should be better now

[0.6.0] - 2023-11-11

Added

  • Add ability to preview template values. This will show the rendered value under current settings #29
    • This includes a new modal to toggle the setting on/off, via the X key
  • Add command source type for chained values, which uses stdout from an executed subprocess command #31

Changed

  • HTTP method is now a plain string, not a template string. This simplifies some internal logic, and I don't think there was a compelling reason to make a template in the first place.

[0.5.0] - 2023-11-07

Added

  • Add top-level collection id field
    • Needed in order to give each collection its own history file
  • Disable mouse capture to allow text highlighting #17
  • Add keybinding (F2) to send request from any view

Fixed

  • Differentiate history between different collections #10
  • Ensure ctrl-c can't get eaten by text boxes (it guarantees exit now) #18

Changed

  • Adjust size of profile list dynamically based on number of profiles
  • Use structured table display format for query parameters and headers
  • Tweak list and tab styling

[0.4.0] - 2023-11-02

Added

  • Request and response panes can now be fullscreened and scrolled #14

Removed

  • Removed Chain.name field in config

Changed

  • All modals now use a shared queue

Fixed

  • Initially selected recipe loads most recent response #13

[0.3.1] - 2023-10-22

Initial distributed release!