Skip to main content

Generating code

We mentioned earlier that the ELIZA service defines a Protocol Buffer schema. So what is that schema? It is really just a simple file that describes the service, its methods, and their argument and return types:

syntax = "proto3";

service ElizaService {
rpc Say(SayRequest) returns (SayResponse) {}
}

message SayRequest {
string sentence = 1;
}

message SayResponse {
string sentence = 1;
}

You can see the full version including comments and some additional RPCs on the Buf Schema Registry (BSR). The rpc keyword stands for Remote Procedure Call — a method you can invoke remotely. The schema is the contract between server and client, and it precisely defines how data is exchanged down to the very details of serialization.

The schema comes to life by generating code. For the server, an interface is generated, and the developer can focus on filling the methods with business logic. For the client, there really isn't anything to do — the developer can just call the client methods, rely on the generated types for compile-time type-safety and serialization, and focus on the application logic.

Generated SDKs

In the tutorial, we have been using generated SDKs with an npm install command. When the package was requested on the BSR NPM registry, it ran the schema through a code generator, and served the generated files as a package with all required dependencies.

If you want to use a Connect or gRPC service whose schema is published on the BSR, you can simply use npm to install the package, and hit the service with a Connect client.

See our documentation on generated SDKs for details.

Local generation

We're going to generate our code using Buf, a modern replacement for Google's protobuf compiler, and two compiler plugins:

The code we will generate has three runtime dependencies:

First, let's install buf, the plugins and runtime dependencies:

$ npm install --save-dev @bufbuild/buf @connectrpc/protoc-gen-connect-es@"^1.0.0" @bufbuild/protoc-gen-es@"^1.0.0"
$ npm install @connectrpc/connect@"^1.0.0" @connectrpc/connect-web@"^1.0.0" @bufbuild/protobuf@"^1.0.0"

Next, tell Buf to use the two plugins with a new configuration file:

buf.gen.yaml
# buf.gen.yaml defines a local generation template.
# For details, see https://buf.build/docs/configuration/v2/buf-gen-yaml
version: v2
plugins:
# This will invoke protoc-gen-es and write output to src/gen
- local: protoc-gen-es
out: src/gen
opt: target=ts
# This will invoke protoc-gen-connect-es
- local: protoc-gen-connect-es
out: src/gen
# Add more plugin options here
opt: target=ts

If desired, you can also skip local plugin installation and use remote plugins. See the connect-es example for a buf.gen.yaml which uses remote plugins.

Finally, tell Buf to generate code for the ELIZA schema:

$ npx buf generate buf.build/connectrpc/eliza

If you prefer, you can use protoc instead of Buf — the plugins behave like any other plugin.

Output

Let's take a peek at what was generated. There are two new files:

  • src/gen/connectrpc/eliza/v1/eliza_connect.ts
  • src/gen/connectrpc/eliza/v1/eliza_pb.ts

The first file was generated by protoc-gen-connect-es and contains the service:

import { SayRequest, SayResponse } from "./eliza_pb.js";
import { MethodKind } from "@bufbuild/protobuf";

export const ElizaService = {
typeName: "connectrpc.eliza.v1.ElizaService",
methods: {
say: {
name: "Say",
I: SayRequest,
O: SayResponse,
kind: MethodKind.Unary,
},
}
} as const;

The full file includes comments and additional RPCs, but the const above really is all Connect needs to provide clients.

The second file was generated by protoc-gen-es, and contains the request and response classes. You can see them being imported for the service definition. To learn more about protoc-gen-es, head over to the documentation for the Protobuf-ES project.

If your bundler does not handle the .js extension in the import from "./eliza_pb.js" correctly, you can configure it following our examples here, or you can add the plugin option import_extension=none to remove the extension.

You can find the documentation for all available plugin options on npmjs.com.

Using the local files

To use the locally generated files in the tutorial, update the import path:

- import { ElizaService } from "@buf/connectrpc_eliza.connectrpc_es/connectrpc/eliza/v1/eliza_connect";
+ import { ElizaService } from "./gen/connectrpc/eliza/v1/eliza_connect";