Connect-ES gives TypeScript RPCs that you can curl by hand
The TypeScript implementation of Connect: Protobuf RPC that works.
At a glance
- What is it?
- Connect-ES is the Apache-2.0 TypeScript implementation of Connect, serving and calling typed Protobuf RPCs over plain HTTP while still speaking gRPC and gRPC-Web, with separate npm packages per runtime. It is particularly useful when browsers are first-class clients.
- Who is it for?
- Adopt Connect-ES when your TypeScript code runs in both Node and the browser and you want one schema to give you typed clients, typed servers and a protocol you can inspect with curl, then add `@connectrpc/validate` before you expose anything, since the README says validation through Protovalidate is almost always recommended and shows `createValidateInterceptor()` in the server setup.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One schema, three wire protocols, and a curl you can paste
The argument for Connect is easiest to see in a single request. Define the service in Protobuf:
service ElizaService {
rpc Say(SayRequest) returns (SayResponse) {}
}Then call it with nothing more than HTTP:
curl \
--header 'Content-Type: application/json' \
--data '{"sentence": "I feel happy."}' \
https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/SayThat request is legible in a network inspector, reproducible from a terminal, and testable without a client library. Compare that with a gRPC-Web call carrying a length-prefixed binary frame, which is neither.
Underneath the same schema, Connect implements three RPC protocols: gRPC, gRPC-web, and Connect's own protocol, which the README describes as optimised for the web. The interoperability payoff is that a Connect client can call a gRPC server and a gRPC client can call a Connect server, so you are not locked into one language or one transport.
The encoding layer is Protobuf-ES, which the README identifies as the only fully-compliant Protobuf JavaScript library, linking to the Buf conformance post. That matters more than it sounds: a library that fails conformance will produce subtle mismatches with Go, Java or Rust services.
The framing is that you get end to end type safety without giving up ordinary HTTP semantics. Streaming, deadlines and interceptors are part of the library, but the transport underneath stays legible.
The server example shows the interceptor you should not remove
The README's Node example is worth reading line by line rather than copying, because two lines are doing real work.
const handler = connectNodeAdapter({
// Validation via Protovalidate is almost always recommended
interceptors: [createValidateInterceptor()],
routes: (router: ConnectRouter) => {
router.service(ElizaService, {
say(req: SayRequest) {
return {
sentence: `You said "${req.sentence}"`,
};
},
});
},
});
http.createServer(handler).listen(8080)The adapter turns RPC routes into a Node request handler, which is then handed to `http.createServer`. That is the entire server. The README immediately undercuts it as a production shape: it says plainly that a plain HTTP server is not fit for production use and points to server plugins for Fastify, Next.js, Express and more.
The interceptors array is the more important line. `createValidateInterceptor()` comes from `@connectrpc/validate`, a Protovalidate interceptor, and the comment says validation is almost always recommended. With it, a request that violates the constraints in your `.proto` file is rejected before your handler runs. Without it, `say()` receives whatever the client sent and your `req.sentence` is whatever came over the wire.
The generated types come from a `./gen/` directory that you produce with code generation, and the imports point at `eliza_pb.js`, not at the source `.proto`. That is the whole workflow: author the schema, generate, implement against the generated service descriptor.
On the client side, `createClient(ElizaService, createConnectTransport({...}))` binds the generated service to a transport. The example sets `httpVersion: "1.1"` and `baseUrl: "http://localhost:8080"`. Note the HTTP version: Connect's own protocol needs HTTP/2 for some features, and pinning to 1.1 is the conservative choice when you are behind a proxy that will not negotiate h2.
Six published packages, because a browser and a server need different adapters
Connect-ES is a monorepo of separately published npm packages, and the split tells you where the platform boundaries are.
`@connectrpc/connect` holds the RPC clients and servers for your schema, and it is the package you depend on in every environment. `@connectrpc/connect-web` provides adapters for web browsers and any other platform with the fetch API. `@connectrpc/connect-node` serves RPCs on vanilla Node.js servers and lets you call RPCs with any protocol. Then there are three server plugins: `@connectrpc/connect-fastify` for Fastify, `@connectrpc/connect-next` for Next.js API routes, and `@connectrpc/connect-express` for Express.
The root `package.json` lists the workspaces, and it reveals more packages than the README advertises: `connect-web-bench`, `example`, `connect-migrate`, `connect-conformance`, `connect-cloudflare` and a wildcard for `packages/typescript-compat/*`. So there is an internal package for Cloudflare, a conformance test package and a TypeScript compatibility matrix, even though the README section on other platforms says the project would like to hear about your use cases on Bun, Deno, the Vercel Edge Runtime and Cloudflare Workers through the Buf Slack or a GitHub issue.
The release stream is mature and boring in the way you want. v2.1.1 shipped on 2025-11-17, v2.1.2 on 2026-06-12 and v2.2.0 on 2026-09-07. A minor bump is where new protocol features and adapters land.
Licensing is the least surprising thing in the repository: Apache-2.0 throughout, with the license header tool configured to stamp a 2021-2026 range and "The Connect Authors" as the holder.
Interoperability and conformance are checked, not asserted
Two repository details back up the interoperability claim.
First, the `connect-conformance` workspace package, and the ecosystem list pointing to the `connectrpc/conformance` repository described as gRPC-Web and Connect interoperability tests. Conformance testing is what separates a Protobuf library that merely compiles from one that produces wire-compatible output against other languages, and the presence of a dedicated conformance workspace is the mechanism.
Second, `@arethetypeswrong/cli` is a root devDependency, and the aggregated `all` script runs it as part of the gate: `turbo run --ui tui build format test conformance bundle-size lint attw license-header`. That single command covers build, format, tests, conformance, bundle size, lint, type packaging correctness and license headers. Bundle size in particular is a maintained concern for a library that ships to browsers.
The formatting and lint story is Biome rather than ESLint and Prettier, configured through `biome.base.json` and `biome.json`, with `biome lint --error-on-warnings` in CI. TypeScript is at `^6.0.3`, Turbo at `^2.11.5`, and the repository declares `type: module` with `engineStrict: true` and `packageManager: [email protected]`, so the workspace pins the toolchain and refuses to run on a mismatched Node.
There is also a `MIGRATING.md` at the root for v1 to v2 moves, and `MAINTAINERS.md` and `SECURITY.md` alongside a `.nvmrc`. Current activity is high: the last push was on 2026-10-16, days before today's date.
Where Connect-ES is the wrong tool
The clearest wrong case is a team that cannot adopt code generation. Connect's type safety comes from generated files, so a workflow where Protobuf cannot be run in the build will fight the tool rather than benefit from it. The trade is real: you get a schema as the single source of truth, and you pay for it with a generation step and generated files in your repository.
The second case is an environment where binary framing is genuinely required. If your only clients are Go or Java services and you have no browser or curl in the loop, plain HTTP JSON buys you nothing, and a mature gRPC stack with mature tooling may serve you better. Connect's pitch is strongest when a browser, a human in a terminal, or a mixed-language fleet all have to reach the same service.
The third case is streaming at volume in production, where the maturity of the ecosystem around your protocol matters more than its elegance. Connect-ES is a young ecosystem relative to gRPC itself, and the README is candid that reach outside the listed platforms is something the project wants to learn about from users rather than a documented support matrix.
One practical trap worth naming: the quickstart server binds `http.createServer` on port 8080 with no TLS, no timeouts and no logging. Anything you build from that example needs those added before it faces real traffic, which is why the plugins exist.
Editorial conclusion
Adopt Connect-ES when your TypeScript code runs in both Node and the browser and you want one schema to give you typed clients, typed servers and a protocol you can inspect with curl, then add `@connectrpc/validate` before you expose anything, since the README says validation through Protovalidate is almost always recommended and shows `createValidateInterceptor()` in the server setup. Do not adopt it if you cannot run code generation from Protobuf, and consult MIGRATING.md if a v1 to v2 migration is already on your plate. Before you deploy, swap the demo `http.createServer(handler).listen(8080)` for a real server plugin, choosing `@connectrpc/connect-fastify`, `@connectrpc/connect-next` or `@connectrpc/connect-express` according to your runtime, and check your Node version against the maintained releases the compatibility section lists.
Frequently asked questions
How do I install and set up Connect-ES for Node?
Install the runtime packages, generate code from your Protobuf schema, then serve your services with connectNodeAdapter from @connectrpc/connect-node and mount the handler on http.createServer. The README's ten-minute Node tutorial walks through spinning up a service and calling it from the web and from a gRPC client.
Which RPC protocols does Connect-ES support?
Three: gRPC, gRPC-web and Connect's own protocol, which is optimised for the web. Because the same schema backs all three, a Connect client can call a gRPC server and vice versa.
Do I need @connectrpc/validate?
The README says validation via Protovalidate is almost always recommended, and the server example includes createValidateInterceptor() from @connectrpc/validate in its interceptors array. Without it, handlers receive whatever the client sent without constraint checks from your schema.
Which frameworks can serve Connect RPCs?
Vanilla Node.js through connectNodeAdapter, plus server plugins for Fastify (@connectrpc/connect-fastify), Next.js API routes (@connectrpc/connect-next) and Express (@connectrpc/connect-express). The README notes that its own plain HTTP server example is not fit for production.
How do I migrate a Connect-ES project from v1 to v2?
The repository includes a MIGRATING.md at the root for that transition. The current line is v2.x, with v2.1.1 from 2025-11-17, v2.1.2 from 2026-06-12 and v2.2.0 from 2026-09-07.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/connectrpc-connect-es)Was this useful?