sondahub / gRPC-Web & Connect
gRPC-Web and Connect test server
Every mock API here is also a protobuf service: seven of them, with .proto files to generate clients from, unary calls over gRPC-Web and Connect — binary, text or JSON — and a server stream of live events. Free, no key, callable from a browser.
The services
One protobuf package per API — sondahub.{api}.v1 — with List, Get, Create, Update and Delete for every collection, and Watch, a server stream of the API's live events. Fields are proto3 optional where a value may be missing; JSON-shaped fields are google.protobuf.Value.
| Service | Definition | |
|---|---|---|
sondahub.store.v1.StoreService | 46 methods | store.proto |
sondahub.fleet.v1.FleetService | 26 methods | fleet.proto |
sondahub.bank.v1.BankService | 31 methods | bank.proto |
sondahub.social.v1.SocialService | 26 methods | social.proto |
sondahub.helpdesk.v1.HelpdeskService | 26 methods | helpdesk.proto |
sondahub.flights.v1.FlightsService | 21 methods | flights.proto |
sondahub.identity.v1.IdentityService | 16 methods | identity.proto |
- Base URL
- https://api.sondahub.com/grpc
- A method
- https://api.sondahub.com/grpc/sondahub.store.v1.StoreService/GetProduct
- gRPC-Web
- application/grpc-web+proto, application/grpc-web-text
- Connect
- application/json, application/proto; application/connect+json or +proto for streams; GET for reads
Try it
curl "https://api.sondahub.com/grpc/sondahub.store.v1.StoreService/GetProduct?encoding=json&message=%7B%22id%22%3A1%7D"
curl -X POST https://api.sondahub.com/grpc/sondahub.store.v1.StoreService/ListProducts \
-H "Content-Type: application/json" \
-d '{"limit": 5, "sort": "-price", "filter": {"in_stock": "true"}}'
curl -i "https://api.sondahub.com/grpc/sondahub.store.v1.StoreService/GetProduct?encoding=json&message=%7B%22id%22%3A999999%7D"
grpcurl -proto store.proto -d '{"id": 1}' api.sondahub.com:443 sondahub.store.v1.StoreService/GetProduct
# the hub answers grpc-status 12 (UNIMPLEMENTED), the reason in grpc-message
From a browser or Node
Generate a client from the .proto with buf and protobuf-es, then point Connect's gRPC-Web transport at the hub:
import { createClient } from '@connectrpc/connect'
import { createGrpcWebTransport } from '@connectrpc/connect-web'
import { StoreService } from './gen/store_pb' // generated from store.proto by buf
const client = createClient(StoreService, createGrpcWebTransport({ baseUrl: 'https://api.sondahub.com/grpc' }))
const product = await client.getProduct({ id: 1n })
const page = await client.listProducts({ limit: 5, sort: '-price', filter: { in_stock: 'true' } })
for await (const e of client.watch({ topics: ['orders'], maxEvents: 5 })) console.log(e.topic, e.data)
int64 ids are bigint in protobuf-es. Writes keep to the session: send X-Sondahub-Session as call metadata, and read the new one from the response headers. CORS is open, so this runs from any page.
Questions
Why gRPC-Web and not native gRPC?
Native gRPC needs HTTP/2 trailers end to end, and the platform the hub runs on cannot send them. gRPC-Web and Connect were made for exactly that: the same calls and the same protobuf messages over plain HTTP, with the trailers in the body. A native gRPC call is answered UNIMPLEMENTED with a message saying so, rather than hanging.
Does server streaming work?
Yes: every service has Watch, the API's live activity as a server stream — each topic at its own pace (the Store's orders every 4 s, inventory every 6 s) until max_events (10 by default, up to 120). Over gRPC-Web and over Connect streaming (application/connect+json or +proto). Client and bidirectional streams need what native gRPC needs, so there are none.
Is server reflection available?
No — reflection is a native gRPC service. The .proto files are served instead, one per API, generated from the same descriptors that encode the wire, so they cannot drift.
Can I call it with curl?
Connect makes that easy: POST JSON with Content-Type: application/json to the method's path, or GET it with ?encoding=json&message=…. Errors come back as Connect errors, {"code": "not_found", "message": …}, with the matching HTTP status.