grpctui

module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: MIT

README

grpctui

A terminal UI for exploring, calling, and debugging gRPC services — grpcui, but full-screen, keyboard-driven, and living in your terminal instead of a browser tab.

Point it at a gRPC server with reflection enabled and it discovers the entire API surface with zero configuration.

Status: v0.3 — Real Request Forms. Discover a server, fill in a request form generated from the method's input message — nested messages, repeated fields, maps, oneof variants and enums included — send a unary call, and read the highlighted response. Metadata and TLS land in v0.4; streaming in v0.5.

Install

go install github.com/alonshuld/grpctui/cmd/grpctui@latest

Prebuilt binaries for Linux, macOS, and Windows are attached to each GitHub Release.

Usage

grpctui localhost:50051

Pick a method with j/k and enter, tab into the request form, enter to edit a field and esc when you are done with it, then ctrl+s to send. The response arrives as highlighted JSON in the panel below the form; a non-OK call shows its gRPC status code and the server's message in the same place. A response JSON cannot represent — one carrying a google.protobuf.Any whose payload type the server never described — is shown in protobuf's text format instead, labelled as such, rather than reported as a failed call.

Flags:
  -config string      read settings from this file; empty skips it
                      (default "$XDG_CONFIG_HOME/grpctui/config.yaml")
  -log-file string    write logs to this file; empty disables logging
                      (default "$XDG_STATE_HOME/grpctui/grpctui.log")
  -log-level string   log level: debug, info, warn, error (default "error")
  -call-timeout d     give up on a single call after this long (default 1m0s)
  -version            print the version and exit

The target must serve the gRPC server reflection API. In Go, that is one line on the server:

import "google.golang.org/grpc/reflection"

reflection.Register(srv)

grpctui connects in plaintext only. TLS and mTLS arrive in v0.4.

Configuration

Everything works with no configuration at all. If you point grpctui at the same server every day, put its address in $XDG_CONFIG_HOME/grpctui/config.yaml (~/.config/grpctui/config.yaml):

# The address to connect to when none is given on the command line.
target: localhost:50051

A target argument always beats the file, so grpctui other.example:443 still does what it says.

Nothing about the file is guessed at. The default path may be absent — that is the zero-config case — but a path you name with --config has to exist, and an unknown key is an error. A typo is never silently ignored, whether it is in the filename or inside the file.

Request fields

The request form is generated from the method's input message, and it is a tree rather than a list: a row either holds a value you type in or holds other rows you open with enter (or /l) and fold away again with /h.

Shape How you fill it in
string, integers, floats, bytes (base64) enter to edit, esc when done
bool space toggles it
enum open it and pick a value with enter or space
nested message open it and fill in its fields
repeated field, map a adds an item, d removes the one under the cursor
oneof open it and pick a variant with space; typing into one picks it too

a works from anywhere inside a list, not just on the list's own row, so a dozen items are a dozen keystrokes rather than a walk back up each time.

Values are checked as each edit ends, so a typo is reported on the row where it was made rather than when the call goes out. Sending checks the whole form and reports every bad row at once — including proto2 required fields nobody filled in.

A field you never touch is left unset rather than sent as an explicit default. Clearing one you have typed into is different: for a field with explicit presence (proto3's optional, or any proto2 field) that sends an explicit empty value, which is otherwise impossible to express. The same goes for space on a bool — toggling it off sends an explicit false, where never toggling it sends nothing at all.

Those fields say which state they are in: unset for one that will not be sent, "" for one that will be sent empty.

The same problem turns up a level higher: an empty nested message is indistinguishable from one nobody opened, so space on a message row sends it anyway. An item you add to a repeated field is always sent, empty or not — you added it on purpose.

Keybindings

Key Action
/k, /j Move the cursor
ctrl+u, ctrl+d Page up / down
g, G Jump to top / bottom
/l, /h Expand / collapse a service or a request field
L/⇧→, H/⇧← Scroll the response left / right
enter Toggle a service, select a method, edit a field, or open a field that holds others
space Toggle a bool, pick an enum value or a oneof variant, send an empty message
a, d Add / remove an item of a repeated or map field
ctrl+s Send the request
esc Stop editing a field, or cancel a call in flight
tab, shift+tab Switch panel
r Retry after a failed connection
? Toggle the full help
q Quit
ctrl+c Quit, even mid-edit

While a field is being edited every key is a character — q types a q. Only ctrl+c, ctrl+s and the panel switches keep their meaning.

Development

make hooks        # install the pre-push hook — do this first
make test         # go test ./...
make check        # everything CI enforces: fmt, vet, lint, build, race tests
make golden       # regenerate teatest golden files (review the diff!)
make run TARGET=localhost:50051

The codebase is four strictly one-directional layers — transport → domain → UI → main:

Package Responsibility
internal/grpcclient Dial, reflection discovery, dynamic invoke
internal/protoschema Descriptor → form-field tree; values → wire message
internal/config ~/.config/grpctui/config.yaml
internal/ui bubbletea models, panels, keymap, styles
cmd/grpctui Flags, config, tea.Program bootstrap

The UI never imports google.golang.org/grpc, and the transport layer never imports bubbletea. That separation is what makes the reflection/invoke core testable without a terminal.

Logging goes to a file and never to stdout or stderr — a TUI owns the terminal, and any stray write corrupts the render.

Licence

MIT © Alon Shuldiner

Directories

Path Synopsis
cmd
grpctui command
Command grpctui is a terminal UI for exploring, calling, and debugging gRPC services.
Command grpctui is a terminal UI for exploring, calling, and debugging gRPC services.
internal
config
Package config loads grpctui's configuration file.
Package config loads grpctui's configuration file.
grpcclient
Package grpcclient is grpctui's transport layer: it dials a target, asks it what it serves over server reflection, and (from v0.2) invokes methods dynamically.
Package grpcclient is grpctui's transport layer: it dials a target, asks it what it serves over server reflection, and (from v0.2) invokes methods dynamically.
logging
Package logging constructs the single *zap.Logger that the rest of grpctui passes down through constructors.
Package logging constructs the single *zap.Logger that the rest of grpctui passes down through constructors.
protoschema
Package protoschema turns protobuf descriptors into the generic field tree the UI renders, and turns a filled-in tree back into a wire message.
Package protoschema turns protobuf descriptors into the generic field tree the UI renders, and turns a filled-in tree back into a wire message.
ui
Package ui holds grpctui's bubbletea models.
Package ui holds grpctui's bubbletea models.
ui/keys
Package keys holds grpctui's single keymap.
Package keys holds grpctui's single keymap.
ui/panels
Package panels holds grpctui's child models — one file per panel.
Package panels holds grpctui's child models — one file per panel.
ui/styles
Package styles holds grpctui's lipgloss styles.
Package styles holds grpctui's lipgloss styles.
version
Package version reports the build's version.
Package version reports the build's version.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL