grpcui

package module
v0.0.0-...-aab9d3f Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 33 Imported by: 0

README

grpcui-ng

grpcui-ng is a fork of fullstorydev/grpcui, based on upstream v1.5.2. It keeps grpcui's browser-based gRPC client and adds shareable, deep-linkable requests with an optional, explicitly enabled auto-run mode.

The fork has its own Go module path, github.com/Svovoniks/grpcui-ng, so it can be installed directly from GitHub. Applications that embed the fork must update upstream imports to the new module path.

How this fork differs

Capability Upstream grpcui This fork
Select service and method from a URL Query parameters Query parameters plus a stable fragment format
Restore request JSON No Yes
Restore request metadata and timeout No Yes
Copy the configured request as a link No Yes
Invoke an RPC when opening a link No Opt-in with both -allow-link-invoke and invoke=true
Install with go install github.com/fullstorydev/grpcui/... github.com/Svovoniks/grpcui-ng/...
Deep-link browser tests No Yes, including async schema loading and safety cases
Dark-mode response metadata Low-contrast values Readable header and trailer values

All normal grpcui behavior is preserved by default. A request link prepares the form but does not invoke anything unless the server operator explicitly starts grpcui with -allow-link-invoke and the link contains invoke=true.

Quick start

Run this fork from the repository:

go run ./cmd/grpcui -plaintext -port 8080 localhost:9019

Then open http://127.0.0.1:8080. Replace localhost:9019 with the address of your gRPC server. Omit -plaintext when connecting with TLS.

To permit trusted links to invoke an RPC as soon as they finish loading:

go run ./cmd/grpcui \
  -plaintext \
  -allow-link-invoke \
  -port 8080 \
  localhost:9019

Enabling auto-invocation is a security decision: opening a crafted link can execute a state-changing RPC. grpcui removes invoke=true from the address bar once the link is loaded, so reloading the page does not run the RPC again.

About grpcui

grpcui is a command-line tool that lets you interact with gRPC servers via a browser. It's sort of like Postman, but for gRPC APIs instead of REST.

In some ways, this is like an extension to grpcurl. Whereas grpcurl is a command-line interface, grpcui provides a web/browser-based GUI. This lets you interactively construct requests to send to a gRPC server.

With this tool you can also browse the schema for gRPC services, which is presented as a list of available endpoints. This is enabled either by querying a server that supports server reflection, by reading proto source files, or by loading in compiled "protoset" files (files that contain encoded file descriptor protos). In fact, the way the tool transforms JSON request data into a binary encoded protobuf is using that very same schema. So, if the server you interact with does not support reflection, you will either need the proto source files that define the service or need protoset files that grpcui can use.

This repo also provides two library packages

  1. github.com/Svovoniks/grpcui-ng: This package contains the building blocks for embedding a gRPC web form into any Go HTTP server. It has functions for accessing the HTML form, the JavaScript code that powers it, as well as a sample CSS file, for styling the form.
  2. github.com/Svovoniks/grpcui-ng/standalone: This package goes a step further and supplies a single, simple HTTP handler that provides the entire gRPC web UI. You can just wire this handler into your HTTP server to embed a gRPC web page that looks exactly like the one you see when you use the grpcui command-line program. This single handler uses the above package but also supplies the enclosing HTML page, some other script dependencies (jQuery and jQuery-UI), and additional CSS and image resources.

Upstream grpcui features

grpcui supports all kinds of RPC methods, including streaming methods. However, it requires you to construct the entire stream of request messages all at once and then renders the entire resulting stream of response messages all at once (so you can't interact with bidirectional streams the way that grpcurl can).

grpcui supports both plain-text and TLS servers and has numerous options for TLS configuration. It also supports mutual TLS, where the client is required to present a client certificate.

As mentioned above, grpcui works seamlessly if the server supports the reflection service. If not, you can supply the .proto source files or you can supply protoset files (containing compiled descriptors, produced by protoc) to grpcui.

The web UI allows you to set request metadata in addition to defining the request message data. When defining request message data, it uses a dynamic HTML form that supports data entry for all possible kinds of protobuf messages, including rich support for well-known types (such as google.protobuf.Timestamp), one ofs, and maps.

In addition to entering the data via HTML form, you can also enter the data in JSON format, by typing or pasting the entire JSON request body into a text form.

Upon issuing an RPC, the web UI shows all gRPC response metadata, including both headers and trailers sent by the server. And, of course, it shows a human-comprehensible response body, in the form of an HTML table.

Installation

This fork

Install the current fork directly from GitHub:

go install github.com/Svovoniks/grpcui-ng/cmd/grpcui@master

After the fork publishes a semantic-version tag newer than the inherited upstream v1.5.2 tag, install the latest tagged release with:

go install github.com/Svovoniks/grpcui-ng/cmd/grpcui@latest

Alternatively, clone this repository and build or install it from source:

git clone git@github.com:Svovoniks/grpcui-ng.git
cd grpcui-ng
make install

You can also run it without installing:

go run ./cmd/grpcui -plaintext localhost:9019

Library consumers should import github.com/Svovoniks/grpcui-ng or github.com/Svovoniks/grpcui-ng/standalone. The exported APIs remain based on upstream grpcui, but the import-path change is intentionally breaking.

Upstream releases

The following commands install the original upstream grpcui, not the additional features in this fork.

Homebrew (macOS or Linux)

Install with homebrew:

brew install grpcui
Go install

You can use the go tool to install grpcui:

go install github.com/fullstorydev/grpcui/cmd/grpcui@latest

This installs the command into the bin sub-folder of wherever your $GOPATH environment variable points. If this directory is already in your $PATH, then you should be good to go.

Usage

The usage doc for the tool explains the numerous options:

grpcui -help

Most flags control how the program connects to the gRPC server. The -port flag controls the HTTP port used by the web UI; without it, grpcui selects an ephemeral port. This fork also provides -allow-link-invoke, which permits trusted request links containing invoke=true to run automatically.

Web Form

When you run grpcui, it will show you a URL to put into a browser in order to access the web UI.

$ grpcui -plaintext localhost:12345
gRPC Web UI available at http://127.0.0.1:60551/...

When you navigate to this URL, you are presented with the user interface:

web UI screenshots

The top two listboxes allow you to select the service and method of the RPC to issue. Once a selection is made, the panel below will show a form that allows you to define an RPC request. The form is constructed, dynamically, based on the actual request message structure of the selected RPC.

You'll notice a second tab that lets you view (and edit) the raw JSON value for the request data. This can be useful to copy+paste a large request message, without having to point-and-click to define each field value, one at a time.

The third tab shows the response data. This tab is grayed out and disabled until you actually click the "Invoke" button, which can be found at the bottom of the page.

Deep-linkable requests

The Copy request link button creates a URL that prepares the currently configured request. Request links use a client-side URL fragment with this format:

#service=<encoded-service>&method=<encoded-method>&request=<encoded-json>&metadata=<encoded-json>&timeout=<seconds>&invoke=true

For example, the decoded values in a link could identify service helloworld.Greeter, method SayHello, request {"name":"Ada"}, metadata [{"name":"x-trace-id","value":"demo"}], and timeout 10. The optional invoke=true field requests automatic invocation. Values use normal URL percent encoding; code that constructs links should use URLSearchParams instead of concatenating them directly. Empty or unchanged optional values may be omitted.

Fragments are handled entirely by the browser and are not sent to grpcui's HTTP server. However, request links can still contain sensitive request data or metadata: they may be exposed through clipboard contents, screenshots, bookmarks, browser extensions, or anyone with whom the link is shared. By default, opening a request link only selects and populates the request and shows a "Request loaded from link" notice; it does not invoke the RPC.

Automatic invocation is available as an explicit opt-in. Start grpcui with -allow-link-invoke and add invoke=true to a request fragment that also contains a valid service and method:

#service=helloworld.Greeter&method=SayHello&request=%7B%22name%22%3A%22Ada%22%7D&invoke=true

Without -allow-link-invoke, the same link only prepares the request and shows that automatic invocation is disabled. Invalid fragment values always prevent automatic invocation. Enabling this option means that merely opening a crafted link can execute an RPC, including a state-changing RPC, so it should only be used when links and grpcui users are trusted. After loading, invoke=true is removed from the address bar so that reloading or navigating back does not invoke the RPC a second time.

The Copy request link action never adds invoke=true; copied links are safe to open in the default configuration. It also leaves out the values of metadata that look like credentials (authorization, proxy-authorization, cookie, and names containing token, secret, password, or api-key/api_key); the names are kept so the recipient can fill in their own values. Add invoke=true deliberately when an auto-running link is required.

Development and tests

JavaScript behavior is tested with Node's test runner, jQuery, and jsdom. The Go suite also verifies that the opt-in is rendered into embedded and standalone web forms correctly.

make test    # Go tests
make jstest  # web form tests (requires Node.js and npm)
Request Form

The first thing to note about the form is that it will generally be a table, where each row is a field. The table has three important columns:

  1. The first column shows the name and type of the field.
  2. The second columns indicates the "cardinality" of the field. Typical fields are optional. The second column for optional fields is a checkbox indicating whether the field is present or not. If the field is not present, its default value is assumed. Repeated fields show buttons in this column for adding and deletig values. The green "+" allows you to add values to the repeated field. The red "x" next to a value will remove that value. Finally, if the field is required (only supported in syntax "proto2"), the column will contain only an asterisk.
  3. The third column shows the value of the field. If the field is absent, this will show the default value for the field. Fields that are nested messages show "unset", but scalar types show their default (usually the type's zero value, but default values are configurable when using syntax "proto2"). Absent fields are also not editable -- you must first check the box in column two to make the field present before editing its value. Repeated fields show a nested table that occupies columns two and three and allows for adding and removing values.

Fields whose type is a nested message will include a nested table in column three. This nested table has its own three columns and one row per field therein.

One-ofs are rendered a little differently. Instead of two columns indicating the presence and value of the field, they include a nested table showing all of the possible fields in the one-of. However, the middle column is a radio button instead of a checkbox, so that only one of the fields can be present at any given time. In addition to a row for each field in the one-of, there is also an option named None, which indicates a one-of where no value is set.

Here's an example form for a message that has two required fields (id and name), one repeated field (labels), a handful of normal optional fields, and a single one-of that has four options. In the first image, no values are present (except, of course, for the required fields at the top). In the second, several field values are present.

web UI message fields web UI message fields, with some values

For RPCs that accept a stream of requests, the web form allows the user to define multiple messages in the stream. It defaults to a single request, but the user can remove it to send none or can send many. A stream resembles a repeated field, but the repeated "thing" is the entire request:

web UI request stream

That last example also shows how well-known message types get special treatment. In that example, the request type is google.protobuf.StringValue. Instead of showing a form for a message with a single field named value with type string, the UI is simple and the "boxing" ceremony is elided. It instead just shows a simple textbox for entering the string value.

A more interesting example of how well-known message types are treated is google.protobuf.Timestamp, where a date picker is shown:

web UI request stream

Raw Request JSON

The second tab lets you view the JSON representation of the request data you have defined on the first tab. You can also directly edit the JSON data -- including pasting in an entire JSON message.

The JSON representation uses the standard JSON mapping for Protocol Buffers.

web UI request JSON

When working with an RPC that has a streaming request, the JSON data will be a JSON array, where each element is a single message in the stream.

Responses

When the "Invoke" button is pressed, the request data is sent to the server and the selected RPC method is invoked. The web form will then navigate to the third tab to show the server's response.

The response tab has three sections:

  1. Response Headers: Any response header metadata is shown here.
  2. Response Data: Any response messages are shown here as are any error messages. RPC methods with a streaming response may show both message data and an error. Error messages show the gRPC status code and the server-defined message text.
  3. Response Trailers: Finally, any response trailer metadata is shown.

web UI response

Each of these three sections is a table of data. Response messages are the most interesting, and their structure closely resembles how messages are structured on the "Request Form" tab. Fields that have nested messages will include a nested table.

Descriptor Sources

The grpcui tool can operate on a variety of sources for descriptors. The descriptors are required, in order for grpcui to understand the RPC schema, translate inputs into the protobuf binary format as well as translate responses from the binary format into text. The sections below document the supported sources and what command-line flags are needed to use them.

Server Reflection

Without any additional command-line flags, grpcui will try to use server reflection.

Examples for how to set up server reflection can be found here.

Proto Source Files

To use grpcui on servers that do not support reflection, you can use .proto source files.

In addition to using -proto flags to point grpcui at the relevant proto source file(s), you may also need to supply -import-path flags to tell grpcui the folders from which dependencies can be imported.

Just like when compiling with protoc, you do not need to provide an import path for the location of the standard protos included with protoc (which contain various "well-known types" with a package definition of google.protobuf). These files are "known" by grpcui as a snapshot of their descriptors is built into the grpcui binary.

Protoset Files

You can also use compiled protoset files with grpcui. Protoset files contain binary encoded google.protobuf.FileDescriptorSet protos. To create a protoset file, invoke protoc with the *.proto files that define the service:

protoc --proto_path=. \
    --descriptor_set_out=myservice.protoset \
    --include_imports \
    my/custom/server/service.proto

The --descriptor_set_out argument is what tells protoc to produce a protoset, and the --include_imports argument is necessary for the protoset to contain everything that grpcui needs to process and understand the schema.

Documentation

Overview

Package grpcui provides a gRPC web UI in the form of HTTP handlers that can be added to a web server.

This package provides multiple functions which, all combined, provide a fully functional web UI. Users of this package can use these pieces to embed a UI into any existing web application. The web form sources can be embedded in an existing HTML page, and the HTTP handlers wired up to make the form fully functional.

For users that don't need as much control over layout and style of the web page, instead consider using standalone.Handler, which is an all-in-one handler that includes its own HTML and CSS as well as all other dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AllFilesViaInProcess

func AllFilesViaInProcess() ([]*desc.FileDescriptor, error)

AllFilesViaInProcess returns a slice that contains all file descriptors known to this server process. This collects descriptors for all files registered with protoregistry.GlobalFiles, which includes all compiled proto files linked into the current program.

func AllFilesViaReflection

func AllFilesViaReflection(ctx context.Context, cc grpc.ClientConnInterface) ([]*desc.FileDescriptor, error)

AllFilesViaReflection returns a slice that contains the file descriptors for all methods exposed by the server on the other end of the given connection. This returns an error if the server does not support service reflection. (See "google.golang.org/grpc/reflection" for more on service reflection.)

func AllMethodsForServer

func AllMethodsForServer(svr *grpc.Server) ([]*desc.MethodDescriptor, error)

AllMethodsForServer returns a slice that contains the method descriptors for all methods exposed by the given gRPC server.

func AllMethodsForServices

func AllMethodsForServices(descs []*desc.ServiceDescriptor) []*desc.MethodDescriptor

AllMethodsForServices returns a slice that contains the method descriptors for all methods in the given services.

func AllMethodsViaInProcess

func AllMethodsViaInProcess(svr reflection.GRPCServer) ([]*desc.MethodDescriptor, error)

AllMethodsViaInProcess returns a slice that contains the method descriptors for all methods exposed by the given server. This automatically skips the reflection service, since it is assumed this is not a desired inclusion.

func AllMethodsViaReflection

func AllMethodsViaReflection(ctx context.Context, cc grpc.ClientConnInterface) ([]*desc.MethodDescriptor, error)

AllMethodsViaReflection returns a slice that contains the method descriptors for all methods exposed by the server on the other end of the given connection. This returns an error if the server does not support service reflection. (See "google.golang.org/grpc/reflection" for more on service reflection.) This automatically skips the reflection service, since it is assumed this is not a desired inclusion.

func RPCInvokeHandler

func RPCInvokeHandler(ch grpc.ClientConnInterface, descs []*desc.MethodDescriptor) http.Handler

RPCInvokeHandler returns an HTTP handler that can be used to invoke RPCs. The request includes request data, header metadata, and an optional timeout.

The handler accepts POST requests with JSON bodies and returns a JSON payload in response. The URI path should name an RPC method ("/service/method"). The format of the request and response bodies matches the formats sent and expected by the JavaScript client code embedded in WebFormContents.

The returned handler expects to serve "/". If it will instead be handling a sub-path (e.g. handling "/rpc/invoke/") then use http.StripPrefix.

Note that the returned handler does not implement any CSRF protection. To provide that, you will need to wrap the returned handler with one that first enforces CSRF checks.

func RPCInvokeHandlerWithOptions

func RPCInvokeHandlerWithOptions(ch grpc.ClientConnInterface, descs []*desc.MethodDescriptor, options InvokeOptions) http.Handler

RPCInvokeHandlerWithOptions is the same as RPCInvokeHandler except that it accepts an additional argument, options. This can be used to add extra request metadata to all RPCs invoked.

func RPCMetadataHandler

func RPCMetadataHandler(methods []*desc.MethodDescriptor, files []*desc.FileDescriptor) http.Handler

RPCMetadataHandler returns an HTTP handler that can be used to get metadata for a specified method.

The handler accepts GET requests, using a query parameter to indicate the method whose schema metadata should be fetched. The response payload will be JSON. The format of the response body matches the format expected by the JavaScript client code embedded in WebFormContents.

func WebFormContents

func WebFormContents(invokeURI, metadataURI string, target string, descs []*desc.MethodDescriptor) []byte

WebFormContents returns an HTML form that can be embedded into a web UI to provide an interactive form for issuing RPCs.

For a fully self-contained handler that provides both an HTML UI and the needed server handlers, see grpcui.UIHandler instead.

The given invokeURI and metadataURI indicate the URI paths where server handlers are registered for invoking RPCs and querying RPC metadata, respectively. Handlers for these endpoints are provided via the RPCInvokeHandler and RPCMetadataHandler functions:

// This example uses "/rpcs" as the base URI.
pageHandler := func(w http.ResponseWriter, r *http.Request) {
  webForm := grpcui.WebFormContents("/rpcs/invoke/", "/rpcs/metadata", descs)
  webFormJs := grpcui.WebFormScript()
  generateHTMLPage(w, r, webForm, webFormJs)
}

// Make sure the RPC handlers are registered at the same URI paths
// that were used in the call to WebFormContents:
rpcInvokeHandler := http.StripPrefix("/rpcs/invoke", grpcui.RPCInvokeHandler(conn, descs))
mux.Handle("/rpcs/invoke/", rpcInvokeHandler)
mux.Handle("/rpcs/metadata", grpcui.RPCMetadataHandler(descs))
mux.HandleFunc("/rpcs/index.html", pageHandler)

The given descs is a slice of methods which are exposed through the web form. You can use AllMethodsForServices, AllMethodsForServer, and AllMethodsViaReflection helper functions to build this list.

The returned HTML form requires that the contents of WebFormScript() have already been loaded as a script in the page.

func WebFormContentsWithOptions

func WebFormContentsWithOptions(invokeURI, metadataURI string, target string, descs []*desc.MethodDescriptor, opts WebFormOptions) []byte

WebFormContentsWithOptions is the same as WebFormContents except that it accepts an additional argument, options. This can be used to toggle the JS code into debug logging and can also be used to define the set of metadata to show in the web form by default (empty if unspecified).

func WebFormSampleCSS

func WebFormSampleCSS() []byte

WebFormSampleCSS returns a CSS stylesheet for styling the HTML web form returned by WebFormContents. It is possible for uses of the web form to supply their own stylesheet, but this makes it simple to use default styling.

func WebFormScript

func WebFormScript() []byte

WebFormScript returns the JavaScript that powers the web form returned by WebFormContents.

The returned JavaScript requires that jQuery and jQuery UI libraries already be loaded in the container HTML page. It includes JavaScript code that relies on the "$" symbol.

Note that the script, by default, does not handle CSRF protection. To add that, the enclosing page, in which the script is embedded, can use jQuery to configure this. For example, you can use the $.ajaxSend() jQuery function to intercept RPC invocations and automatically add a CSRF token header. To then check the token on the server-side, you will need to create a wrapper handler that first verifies the CSRF token header before delegating to a RPCInvokeHandler.

Types

type InvokeOptions

type InvokeOptions struct {
	// The set of metadata to add to all outgoing RPCs. If the invocation
	// request includes conflicting metadata, these values override, and the
	// values in the request will not be sent.
	ExtraMetadata []string
	// The set of HTTP header names that will be preserved. These are HTTP
	// request headers included in the invocation request that will be added as
	// request metadata when invoking the RPC. If the invocation request
	// includes conflicting metadata, the values in the HTTP request headers
	// will override, and the values in the request will not be sent.
	PreserveHeaders []string
	// Whether or not default values should be emitted in the JSON response
	EmitDefaults bool
	// If verbosity is greater than zero, the handler may log events, such as
	// cases where the request included metadata that conflicts with the
	// ExtraMetadata and PreserveHeaders fields above. It is an int, instead
	// of a bool "verbose" flag, so that additional logs may be added in the
	// future and the caller control how detailed those logs will be.
	Verbosity int
}

InvokeOptions contains optional arguments when creating a gRPCui invocation handler.

type WebFormOptions

type WebFormOptions struct {
	// AllowLinkInvoke allows a URL fragment with invoke=true to invoke the
	// configured RPC after the request schema and fragment values are loaded.
	// It is disabled by default. Enabling it lets anyone who can get a user to
	// open a link send an RPC of their choosing.
	AllowLinkInvoke bool
	// The set of metadata to show in the web form by default. Each value in
	// the slice should be in the form "name: value"
	DefaultMetadata []string
	// If non-nil and true, the web form JS code will log debug information
	// to the JS console. If nil, whether debug is enabled or not depends on
	// an environment variable: GRPC_WEBFORM_DEBUG (if it's not blank, then
	// debug is enabled).
	Debug *bool
	// Any options that will be rendered before grpcurl in the grpccurl/raw request box
	GRPCurlOptions []string
}

WebFormOptions contains optional arguments when creating a gRPCui web form.

Directories

Path Synopsis
cmd
grpcui command
Command grpcui starts a simple web server that provides a web form for making gRPC requests.
Command grpcui starts a simple web server that provides a web form for making gRPC requests.
Package standalone provides a stand-alone HTTP handler that provides a complete gRPC web UI that includes both the web page and relevant server-side handlers.
Package standalone provides a stand-alone HTTP handler that provides a complete gRPC web UI that includes both the web page and relevant server-side handlers.
testing
cmd/testsvr command
Command testsvr is a gRPC server for testing grpcui.
Command testsvr is a gRPC server for testing grpcui.

Jump to

Keyboard shortcuts

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