TypeScript Plugin Support for Cosmo Connect

Milinda Dias
Cosmo Connect now lets you extend the router entirely in TypeScript. If you already work in a TypeScript-heavy codebase, you no longer need to introduce Go or maintain a separate microservice just to add custom logic to your graph. The plugin runs inside the router, is published through Cosmo Cloud, and participates in schema checks like any other subgraph.
Weβve released TypeScript plugin support for Cosmo Connect. You can now write plugins in TypeScript and load them directly into the router instead of running a separate service. This makes it easier to expose small pieces of logic, legacy API calls, or internal service integrations behind your GraphQL contract.
Until now, Cosmo Connect router plugins were only available in Go. Thatβs fine if your team already writes Go, but it can be a blocker if you donβt. Most teams already have people writing TypeScript every day, so adding TS support makes the plugin workflow accessible to a lot more users.
A common pattern we see is teams wanting to expose a small piece of logic or wrap a legacy REST API without spinning up another service. With a TypeScript plugin, you can model a GraphQL type, call the internal API, transform the response, and expose it through the router with minimal overhead. It behaves like a subgraph but without the operational cost of deploying one.
TypeScript plugins donβt introduce new capabilities on top of Go plugins. They make the same plugin workflow available to more teams. From the platformβs point of view, this feature is mainly about adoption and accessibility: if your engineers are already comfortable in TypeScript, they can start using plugins immediately without needing to learn Go first.
In a typical federated setup, each subgraph is its own service with its own deployment. With router plugins, you can move some of that logic into the router itself. This is especially useful if you want to create a new "subgraph" but don't want to deploy an entire service just to do it, or if you have a legacy system that does not speak GraphQL and you want to use router plugins to bridge that gap.
When initializing a plugin, you can now pass in ts as a language option, for example wgc router plugin init planets --language ts.
When you publish the plugin and update the subgraph, it goes through the same composition and schema checks as everything else in Cosmo. There are no extra checks specific to TypeScript plugins.
Like with Go plugins, TypeScript plugins are supported in the Cosmo Cloud Plugin Registry, which means you can push plugins directly to Cosmo Cloud, and it will be pulled by all of your running router instances.
Cosmo Connect's plugin story started in Go. The router itself is written in Go, and we use HashiCorp's go-plugin library under the hood to load and manage plugins. That setup is very "batteries included" for Go: most of the low-level mechanics are already handled for us.
TypeScript support took longer because thereβs no equivalent, fully packaged runtime for TS plugins. We did more of the plumbing ourselves to make TypeScript plugins feel as integrated as Go plugins, which is why Go shipped first.
If you're curious, our CEO Jens wrote about the choice to use HashiCorp's go-plugin in REST in Peace-Connectors Were Never the Right Supergraph Abstraction .
Here is a quick example of what it might look like to implement a TypeScript plugin. For a more in-depth walkthrough, see the full plugin tutorial in our docs .
You start by initializing a Cosmo Connect TypeScript plugin using our CLI. This sets up the basic scaffolding which you can build upon.
Cosmo Connect works by generating protobuf definitions from your GraphQL schema. Upon scaffolding the plugin, you still need to generate the protobuf definitions. You can run the following command to generate the protobuf definitions from the GraphQL schema as well as generate the protobuf code.
After this, you have a minimal plugin project with a basic TypeScript plugin server. You can now start editing the GraphQL schema inside the plugin directory.
After running the above two commands, you should have the following project structure:
As mentioned earlier, the hashicorp-go library provides helpers for initializing a plugin server. We implemented similar helpers ourselves in plugin-server.ts, which includes:
- Initializing gRPC health checks
- Initializing the handshake between the router and plugin
The plugin.ts file serves as the entrypoint for your plugin. This is also where you will implement the RPC handlers like queryHello and the new queryPlanet method.
Next, update the schema inside the plugin directory:
Any time you change these types, you need to regenerate the protobuf definitions so the plugin code has access to the new fields and operations.
After updating the schema, we need to regenerate the protobuf types:
This uses our internal tool protographic to compile the schema into protobuf definitions the router understands.
You can see this by opening service.proto in the generated proto directory before and after running the command.
Before running generate, the proto file only contains the original World and hello RPC definitions. After running it, the new Planet message and RPC (QueryPlanet) appear in service.proto.
You implement both of these RPC handlers inside src/plugin-server.ts, alongside the existing QueryHello handler.
The existing hello RPC:
Then add the new planet RPC for planet: Planet!:
This is all the business logic. The router will convert the protobuf response back into GraphQL automatically.
In a real application, this is where you'd call an internal HTTP service, legacy API, or database.
Build the plugin:
A successful build produces a plugin artifact, which you can verify by looking in the bin folder of the plugin. It should contain a binary, whose name will depend on your OS and architecture. For example, if you ran the build command on an M1 Mac, the binary name would be darwin-arm64.
The next step is to publish the plugin to Cosmo Cloud so that your router can pull and use it.
Publish it to Cosmo Cloud:
This uploads the plugin to Cosmo Cloud for the specified namespace.
Then update the subgraph metadata:
Updating the subgraph metadata triggers routers in the namespace to reload and pull the new plugin, so the new planet field becomes part of the schema they serve.
Once the router reloads and pulls the updated plugin after the subgraph update, the planet field appears in the playground schema.
It returns:
When you publish the plugin and update the subgraph:
- Normal composition rules apply
- No extra checks are required for TypeScript plugins
For observability:
- You can test your plugin logic directly in the Playground
- TypeScript plugins currently donβt have built-in tracing
A good first experiment is to create a TypeScript plugin that wraps a single REST endpoint. Define the GraphQL type, map it in your handler, publish the plugin through Cosmo Cloud, update the subgraph metadata, and query it in the Playground. This gives you end-to-end familiarity with the workflow in a real environment.
Thatβs the full flow: define the schema, generate the protobufs, implement the handlers in TypeScript, build, publish, and the router reloads automatically once the update completes. This makes it much easier to fold small pieces of logic or legacy services into your graph without standing up another subgraph.
For a more in-depth look at building plugins, check out the full plugin tutorial in our docs.
Frequently Asked Questions (FAQ)
It allows you to write router plugins in TypeScript and load them directly into the router instead of running a separate service.
No. They provide the same plugin workflow but make it accessible to teams that prefer or primarily use TypeScript.
The router and plugin system started in Go and use the go-plugin library, which handles most mechanics for Go. TypeScript required more plumbing because there is no equivalent packaged runtime.
You need to regenerate the protobuf definitions so the router can consume the updated fields and operations.
Yes. They can be published to Cosmo Cloud and pulled by all running router instances.
