Send requests from files. Keyboard-first. Multi-protocol.
A Neovim plugin and Rust CLI for executing HTTP, Redis, SQL (PostgreSQL / MySQL / SQLite) requests from plain text files. Inspired by JetBrains HTTP Client, with focus on keyboard-driven workflows and dataset manipulation.
- File-based requests — Define requests in
.http/.rest,.sql,.sqlite,.redis,.mongofiles - Environment variables — JetBrains-style
env.jsonwith{{var}}substitution - Named connections —
connections.jsonfor database credentials; supports env var references - Keyboard-first — Execute at cursor, navigate results with Vim keys, never leave home row
- Multi-protocol — HTTP, Redis, PostgreSQL, MySQL, SQLite (MongoDB/AMQP stubs)
- SQL dataset buffer — Paginated results, cell navigation (hjkl), vim-style search/filter, sorting, inline editing
- Dataset manipulation — Edit cells inline, generate DML (INSERT/UPDATE/DELETE) from changes, commit with transaction support
- Data import/export — CSV, JSON, SQL INSERT statements
- DB browser — Tree-view of schemas, tables, columns; generate SELECT/DESCRIBE queries; DDL table operations
- Completion — HTTP methods/headers/values (nvim-cmp or blink.cmp); SQL keywords/identifiers/columns (blink.cmp)
- Assertions & scripts — Inline
> {% ... %}assertions, pre/post-request scripts - Request chaining —
{{RequestName.res.body.X}}to extract values from prior responses
-- lazy.nvim
{
"beyondlex/poste",
dependencies = {
"saghen/blink.cmp",
"stevearc/dressing.nvim",
"beyondlex/finder",
},
config = function()
require("poste").setup()
end,
}# Rust CLI (optional — for standalone execution or to enable context-aware features)
cargo install --path crates/poste-clirequests/api.http:
### List users
GET {{api_base}}/users
Authorization: Bearer {{api_token}}
### Create user
POST {{api_base}}/users
Content-Type: application/json
{"name": "John", "email": "john@test.com"}requests/queries.sql:
-- @connection pg-dev
SELECT * FROM users WHERE active = true;requests/cache.redis:
# @connection redis://localhost:6379
### Get user session
GET session:user:42
### Set cache
SET post:latest "active"
EXPIRE post:latest 3600
env.json (walk-up discovery from file directory):
{
"dev": {
"api_base": "http://localhost:8080",
"api_token": "dev-token-xxx",
"db_host": "127.0.0.1",
"db_port": "5432",
"db_user": "app_user",
"db_pass": "local-pass"
},
"prod": {
"api_base": "https://api.example.com",
"api_token": "prod-token-xxx"
}
}connections.json (walk-up discovery; supports {{var}}):
{
"pg-dev": {
"dialect": "postgres",
"host": "{{db_host}}",
"port": "{{db_port}}",
"database": "myapp",
"user": "{{db_user}}",
"password": "{{db_pass}}"
},
"my-blog": {
"dialect": "mysql",
"host": "localhost",
"port": 3306,
"database": "blog",
"user": "root",
"password": ""
}
}In Neovim, open any supported file. With cursor on a request block, press <CR> to execute. Results open in a side panel.
Default keymaps →
| Key | Action |
|---|---|
<CR> |
Execute request at cursor |
]] / [[ |
Jump next/previous block |
gd |
Go to definition |
grr |
Go to references |
gs |
Symbol outline (Telescope fallback to vim.ui.select) |
<leader>rp |
Paste curl from clipboard |
<leader>rc |
Copy request as curl |
| Key | Action |
|---|---|
q |
Close |
B / I |
View Body / Verbose |
A / S |
View Assertions / Script logs |
<Tab> / <S-Tab> |
Next/previous tab |
| Key | Action |
|---|---|
K |
Show DDL for table under cursor |
<leader>db |
Toggle DB browser |
<leader>cr |
Clear filter/search in dataset |
<C-Space> |
Trigger completion (i mode) |
| Key | Action |
|---|---|
q |
Close |
h/j/k/l |
Move cell left/down/up/right |
0/$ |
First/last column |
gg/G |
First/last row |
H/L |
Previous/next page |
K |
Preview cell content in float |
yy / yc |
Yank cell / yank column |
s |
Sort by current column (toggle asc/desc) |
zh |
Toggle cell highlight |
zH |
Toggle header float |
zN |
Toggle row numbers |
R |
Re-run query |
<Tab>/<S-Tab> |
Next/previous result tab |
n/N |
Next/previous search match |
<leader>/ |
Search |
<leader>cr |
Clear search/filter |
<leader>fc |
Find column |
<leader>ce |
Filter by current cell |
<leader>hh/<leader>ll |
First/last page |
<leader>pa |
Toggle pagination |
<leader>gp |
Toggle raw mode |
i / a |
Enter edit mode (insert/append) |
dd |
Delete row |
o / O |
Insert row below/above |
u |
Undo edit |
<leader>w / :W |
Commit changes (generate & execute DML) |
<leader>ec |
Export as CSV |
<leader>ej |
Export as JSON |
<leader>es |
Export as SQL INSERT |
| Key | Action |
|---|---|
<CR> |
Toggle node expand/collapse |
x |
Open context menu (node-specific actions) |
r |
Refresh node |
/ |
Search filter |
s |
Generate SELECT * query |
d |
Generate DESCRIBE query |
q |
Close |
ma/mr/md/mt |
Table ops: add/rename/drop/alter column |
| Command | Description |
|---|---|
:PosteRun |
Execute request at cursor |
:PosteEnv [name] |
Switch or show current environment |
:PostePasteCurl |
Paste curl from clipboard |
:PosteCopyAsCurl |
Copy request as curl |
:PosteCmpStatus |
Show HTTP completion status |
:PosteSQLCmpStatus |
Show SQL completion status |
:PosteImport <file> |
Import data from CSV/JSON file into dataset |
:PosteExport {csv|json|sql} |
Export current dataset |
require("poste").setup({
-- Binary path (default: stdpath("data")/poste/bin/poste)
poste_binary = vim.fn.stdpath("data") .. "/poste/bin/poste",
-- Default environment
default_env = "dev",
-- Response window
split_direction = "vertical", -- "vertical" | "horizontal"
split_size = 80, -- columns (vertical) or rows (horizontal)
-- Log file (set to "" to disable)
log_file = vim.fn.stdpath("cache") .. "/poste.log",
-- Customize keymaps — set to false to disable
keymaps = {
source_buffer = {
run = "<CR>",
jump_next = "]]",
jump_prev = "[[",
goto_definition = "gd",
goto_references = "grr",
quickfix_next = "]q",
quickfix_prev = "[q",
paste_curl = "<leader>rp",
copy_as_curl = "<leader>rc",
show_symbols = "gs",
},
http_response = {
close = "q",
view_body = "B",
view_verbose = "I",
view_assertions = "A",
view_script_logs = "S",
next_tab = "<Tab>",
prev_tab = "<S-Tab>",
},
sql_source = {
run = "<CR>",
show_ddl = "K",
clear_filter = "<leader>cr",
toggle_db_browser = "<leader>db",
trigger_completion = "<C-Space>",
},
sql_dataset = {
close = "q",
move_left = "h", move_down = "j", move_up = "k", move_right = "l",
prev_page = "H", next_page = "L",
first_col = "0", last_col = "$",
first_row = "gg", last_row = "G",
preview_cell = "K",
yank_cell = "yy", yank_column = "yc",
sort_column = "s",
toggle_cell_highlight = "zh",
toggle_header_float = "zH",
toggle_row_numbers = "zN",
toggle_raw_mode = "<leader>gp",
next_tab = "<Tab>", prev_tab = "<S-Tab>",
rerun = "R",
goto_first_page = "<leader>hh", goto_last_page = "<leader>ll",
toggle_pagination = "<leader>pa",
find_column = "<leader>fc", filter_by_cell = "<leader>ce",
show_search = "<leader>/", clear_filter_search = "<leader>cr",
next_search = "n", prev_search = "N",
-- Editing
edit_insert = "i",
edit_append = "a",
edit_delete_row = "dd",
edit_insert_row_above = "O",
edit_insert_row_below = "o",
edit_undo = "u",
edit_commit = "<leader>w",
-- Export
export_csv = "<leader>ec",
export_json = "<leader>ej",
export_sql = "<leader>es",
},
sql_table_ops = {
select_all = "ma",
refresh_all = "mr",
describe_all = "md",
toggle_menu = "mt",
},
db_browser = {
toggle_node = "<CR>",
context_menu = "x",
refresh_node = "r",
search_filter = "/",
select_query = "s",
describe_query = "d",
close = "q",
},
introspect_float = {
close = "q",
close_alt = "<Esc>",
},
},
-- Override highlight group colors
highlights = {
-- Example: change HTTP method colors
-- PosteMethodGET = { fg = "#00ff00", bold = true },
-- PosteMethodPOST = { fg = "#ffff00", bold = true },
-- PosteMethodDELETE = { fg = "#ff0000", bold = true },
--
-- Example: customize SQL dataset look
-- PosteSqlHeader = { fg = "#ff8800", bold = true },
-- PosteSqlCellSelected = { bg = "#334455", fg = "#ffffff", bold = true },
},
})Full list of highlight groups you can override: PosteLatency, PosteSpinner, PosteSuccess, PosteError, PosteSeparator, PosteRequestName, PosteVarRef, PosteMagicVar, PosteMethodGET[...]
Poste provides context-aware completions.
- HTTP methods —
GET,POST,PUT,DELETE, etc. - Header names —
Content-Type,Authorization,Accept-Encoding, etc. - Header values —
application/json,Bearer,gzip, etc. - Variables / env vars —
{{...}}references from env.json
Works with both nvim-cmp and blink.cmp. Registration is automatic.
- SQL keywords —
SELECT,FROM,WHERE,JOIN, etc. - Tables, columns, schemas — introspected from your database
- Functions — aggregate and scalar functions per dialect
- Connection-aware — completions reflect the actual schema
Requires blink.cmp. Auto-registers as poste_sql source provider.
:PosteCmpStatus " HTTP completion status
:PosteSQLCmpStatus " SQL completion statusConnections are defined in connections.json (walked up from the SQL file). Reference them in your .sql files:
-- @connection pg-dev
-- @connection my-blogThe USE database; statement switches the active database for parsing/completion context.
Query results render in a rich dataset buffer with:
- Cell navigation — hjkl to move between cells
- Sorting — press
son any column - Search & filter —
<leader>/for search,<leader>ceto filter by cell value - Pagination — configurable page size,
<leader>pato toggle - Multi-result tabs — each statement gets its own tab,
<Tab>/<S-Tab>to switch - Raw mode —
<leader>gpto toggle compact view
Inline editing directly in the dataset buffer:
- Enter edit mode —
ito insert before cursor,ato append after - Edit cells — Type to modify,
<Esc>to exit edit,<CR>to confirm - Manage rows —
ddto delete,o/Oto insert below/above - Undo changes —
uto undo edits (before commit) - Generate & commit DML —
<leader>wor:Wto auto-generate UPDATE/INSERT/DELETE statements and execute with transaction
Press <leader>db in a SQL file to open the database tree browser. Navigate schemas, tables, and columns. Press s to generate a SELECT * query or d for DESCRIBE.
Export current dataset:
<leader>ec— CSV (RFC 4180 compliant)<leader>ej— JSON (array of objects)<leader>es— SQL INSERT statements (dialect-aware)
Import data:
:PosteImport <file>— Load CSV/JSON data into a new result tab
# Start test databases (PG 16 on 15432, MySQL 8.0 on 13306)
cd tests/sql && docker compose up -d
# Run queries
cargo run -- run tests/sql/queries/postgres.sql --line 4 --env dev
# Run Lua tests
tests/run.sh# Execute a specific request by line number
poste run requests/api.http --line 4 --env dev
# Introspect database schema
poste introspect --connection pg-dev --env dev
# List available connections
poste connection list --env devposte/
├── crates/
│ ├── poste-core/ # Request parsing, SQL parsing, env management (no I/O)
│ ├── poste-exec/ # Protocol execution, SQL connection/dialect, response
│ └── poste-cli/ # CLI binary (poste run / connection / introspect)
├── lua/
│ └── poste/
│ ├── http/ # HTTP protocol handling
│ └── sql/ # SQL protocol handling (buffer, editor, export, import, etc.)
└── tests/
├── run.sh # Lua tests
└── sql/ # Docker Compose + SQL integration tests
Progress: 34/38 steps completed (~90%)
| Phase | Description | Status |
|---|---|---|
| 1A | Rust infrastructure | ✅ Complete |
| 1B | Lua dataset panel | ✅ Complete |
| 1C | MySQL/SQLite executors | ✅ Complete |
| 2 | Connection & context management | ✅ Complete |
| 3 | DB structure browser | ✅ Complete |
| 4 | Table operations + DDL + completion | ✅ Complete |
| 5 | Import/export + pagination | ✅ Complete |
| 6 | Advanced features (editor, transactions) | ✅ Complete |
Tests: 300+ passing (230 Rust + 70 Lua)
MIT