Mocking APIs is one of those tasks that starts simple and quickly turns into a chore. You begin with the best intentions, hand-crafting a few JSON fixtures for your frontend tests. But microservices evolve, payloads change, and soon you’re maintaining a massive directory of stale mock files. You find yourself trying to remember if user ID 42 was the one that returns a 404, or the one that simulates a slow response.
When you’re building with gRPC or ConnectRPC, this problem gets both easier and harder. It’s easier because you have a strict schema (the Protobuf contract) to guide you. It’s harder because writing binary payloads or mock servers that conform to those schemas by hand is tedious.
In FauxRPC, I built a tool to generate fake data from Protobuf schemas dynamically. But I wanted to go further. I wanted to make mocks a natural byproduct of running your development environment. That is why I introduced Proxy Mode and Auto-Recording.
By placing FauxRPC in front of a real upstream service, it acts as a smart proxy: intercepting traffic, forwarding it to the upstream server, and writing out reusable mock stubs to disk. It even generates intelligent matching rules automatically.
Here is how it works under the hood.
The Auto-Recording Workflow
Rather than thinking about mocks as a separate coding chore, FauxRPC integrates mock generation directly into your existing development flow:
- Start FauxRPC in proxy mode in front of a staging or local backend server.
- Interact with your application normally in the browser or via API clients.
- Capture traffic automatically as FauxRPC records every request and response into the
stubs/directory. - Commit the generated stubs to Git along with your code changes.
- Run your CI test suite against FauxRPC using those recorded stubs, ensuring fast, offline, and reproducible test runs.
Zero-Configuration Reflection
Normally, running a mock server requires you to supply the schema files. You have to pass paths to your .proto files or compiled descriptor sets (.binpb). That’s fine for a CI environment, but during local development, dragging files around and keeping them in sync is a pain.
If you start FauxRPC in proxy mode without specifying a schema:
fauxrpc run --proxy-to=localhost:8080 --record-dir=stubs/
FauxRPC uses gRPC Server Reflection to discover the schema dynamically from the upstream server on startup.
Behind the scenes, FauxRPC performs a few key steps during this schema discovery phase:
- It connects to the upstream and queries the reflection service using
ListServicesto list all endpoints. - For each service, it fetches the file descriptors using
FileContainingSymbol. - It recursively collects the raw file descriptors (
FileDescriptorProto), deduplicating common dependencies (likegoogle/protobuf/timestamp.proto). - It compiles the gathered descriptors into a
FileDescriptorSetand registers them in FauxRPC’s registry.
This means FauxRPC is ready to handle, translate, and mock any method defined on the upstream server with absolutely zero configuration.
Because the schema is discovered directly from the running service, the proxy automatically stays in sync with upstream changes. No descriptor files need to be exported, committed, or manually refreshed—as your API evolves, FauxRPC adapts instantly.
The Multi-Protocol Proxy Engine
FauxRPC is designed to work as a multi-protocol translator. A frontend might communicate using ConnectRPC (over JSON), while the upstream service is a pure gRPC service using binary Protobuf.
To achieve this, FauxRPC sets up a ConnectRPC client using a custom dynamicProtoCodec for proxying. This codec uses Go’s protobuf reflection (dynamicpb.Message) to encode and decode payloads on the fly using the schemas retrieved during the reflection phase.
- Unary calls: The proxy reads the request, forwards it using the client, intercepts the response, and writes it back.
- Streaming calls (client, server, and bidi): To handle streams, FauxRPC uses
FrameTrackerobjects. The tracker forwards frames in real time so there is no added latency, but it keeps a copy of the sequence in memory for logging and recording. - Metadata and Headers: When forwarding headers via
copyHeaders, FauxRPC filters out protocol-level headers (likecontent-type,grpc-status, and Connect-specific protocol headers) to let the underlying transport layer handle them cleanly. It also automatically masks sensitive headers (e.g.Authorization,Cookie,X-API-Key) with*****to avoid leaking production keys or user credentials into logs or stubs.
Graceful Fallbacks for Parallel Teams
In a fast-moving team, schemas are often updated before the code is ready. A backend engineer might merge a .proto change adding a new endpoint, but the actual implementation won’t land for days. Normally, this blocks the frontend engineers who need that endpoint to build the UI.
FauxRPC’s Fallback Mode solves this problem directly.
If the upstream server returns an Unimplemented error code, the proxy doesn’t just pass that error to the client. Instead, it enters fallback mode:
- It checks the local stub database to see if a mock stub matches the request.
- If no stub is found, it automatically generates realistic fake data (leveraging
protovalidateannotations if they are present in the schema). - The fake response is returned to the client.
To the frontend, the endpoint looks and acts like it’s fully implemented. The frontend team keeps coding, completely unblocked by backend delays.
Auto-Recording Stubs
Writing mock stubs by hand is the worst part of API mocking. In proxy mode, FauxRPC can automate this entirely when you pass the --record-dir flag:
stubs/
└── connectrpc.eliza.v1.ElizaService/
├── Say.json
└── Introduce.json
As you interact with your upstream service, FauxRPC intercepts the requests and responses, automatically writing them to disk as structured JSON or YAML files matching your service hierarchy.
For example, a recorded stub might look like this:
{
"id": "c85d8869-ad10-449e-ba63-2287f7401c10",
"target": "connectrpc.eliza.v1.ElizaService/Say",
"active_if": "req.sentence == \"hello\"",
"content": {
"sentence": "Hello! How can I help you today?"
}
}
Because these files are saved directly in your codebase, you can commit them to Git and immediately use them as your mock suite in CI/CD pipelines or integration tests. There is no extra setup: just run your application, click around your frontend, and your API mock stubs are generated for you.
A Protobuf-Native Dashboard for Curation
Having an automated directory of stubs is a massive time saver, but you still need a way to easily curate and manage them. The developer dashboard in FauxRPC (served at http://localhost:6660/fauxrpc when running with --dashboard) gives you exactly that.
The dashboard captures your live traffic history so you can view the raw JSON payload of any logged request. From there, you can copy a pre-compiled FauxRPC YAML stub, along with its generated active_if matcher, directly to your clipboard. This gives you the ability to carefully curate your stub directory while still completely bypassing the need to hand-write them yourself.

This dashboard interface is powered by Protodocs, which displays native protobuf schemas directly inside FauxRPC. Because it understands protobuf natively, you can view your packages, messages, and services, and use a built-in API explorer to run test calls (including unary and streaming APIs) directly from the browser without needing complex local Envoy or gRPC-Web proxy configurations.
Generating Intelligent CEL Matchers
When you are curating these recorded stubs, they are only useful if they match the right request parameters. If you search for user ID 12, you want the stub that returns Alice. If you search for user ID 42, you want the stub that returns Bob.
Whether auto-saved to disk or copied from the dashboard, FauxRPC automatically compiles request shapes into Common Expression Language (CEL) matching rules by examining the request metadata and payload:
- It ranges over the fields set on the request message.
- It ignores complex fields (nested messages, lists, and maps) to keep the generated rules readable.
- For primitive fields (like strings, booleans, and integers), it formats the values into CEL literals.
- It joins these field checks with
&&.
For example, if you send a request where name is “Alice” and age is 30, FauxRPC automatically generates:
active_if: req.name == "Alice" && req.age == 30
When a subsequent request comes in, FauxRPC compiles and evaluates this expression against the request payload. If it evaluates to true, the stub is served.
Recorded Stub Formats
FauxRPC formats these stubs into three distinct kinds based on the call type:
Unary / Client-Streaming Success
For successful unary calls, it records the response payload:
{
"id": "c85d8869-ad10-449e-ba63-2287f7401c10",
"target": "connectrpc.eliza.v1.ElizaService/Say",
"active_if": "req.sentence == \"hello\"",
"content": {
"sentence": "Hello! How can I help you today?"
},
"priority": 10
}
Unary / Client-Streaming Error
If the upstream returned an error, FauxRPC records the status code and message so you can mock error states:
{
"id": "18fd7d9e-108c-4a37-bcfc-fa8d7a12ad4f",
"target": "connectrpc.eliza.v1.ElizaService/Say",
"active_if": "req.sentence == \"trigger error\"",
"error_code": 3,
"error_message": "invalid sentence structure",
"priority": 10
}
Server-Streaming / Bidirectional Streaming
For streaming APIs, it records the sequence of frames, mimicking latency with a default delay, and captures trailing errors:
{
"id": "a97df11b-7a31-4b10-8b4b-6f81e3a1f810",
"target": "connectrpc.eliza.v1.ElizaService/Introduce",
"active_if": "req.name == \"Bob\"",
"stream": {
"items": [
{ "content": { "sentence": "Hi Bob!" }, "delay": "100ms" },
{ "content": { "sentence": "I am Eliza." }, "delay": "100ms" },
{ "error": { "code": 5, "message": "session lost" } }
]
},
"priority": 10
}
Summary
FauxRPC’s proxy and recording capabilities bridge the gap between static mock servers and real-world backend services. By discovering schemas through reflection, handling dynamic protocol translation, falling back to fake data, and generating intelligent CEL matchers automatically, it takes the busywork out of API mocking.
Traditional mocks drift because they are maintained separately from the systems they represent. By recording real traffic, discovering schemas automatically, and falling back to generated data when necessary, FauxRPC keeps mock environments aligned with production behavior while dramatically reducing the amount of manual work required. It turns mocks from a maintenance chore into a natural byproduct of running your development environment.
