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.

Remote generation

In the tutorial, we have been using generated code 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-Web client.

See our documentation on remote generation for details. Please be aware that remote generation is alpha, and is likely to undergo breaking changes in the future. That said, we are convinced that this workflow is extremely useful, and will continue to support it with Connect-Web.

Local generation

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

  • protoc-gen-connect-web — generates services from your Protocol Buffer schema
  • protoc-gen-es — generates base types, like request and response messages

The code we will generate has two runtime dependencies:

  • @bufbuild/connect-web — provides clients for the Connect and gRPC-web protocol
  • @bufbuild/protobuf — provides serialization and more for the base types

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

$ npm install --save-dev @bufbuild/protoc-gen-connect-web @bufbuild/protoc-gen-es
$ npm install @bufbuild/connect-web @bufbuild/protobuf

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://docs.buf.build/configuration/v1/buf-gen-yaml
version: v1
plugins:
- name: es
path: node_modules/.bin/protoc-gen-es
out: gen
# With target=ts, we generate TypeScript files.
# Use target=js+dts to generate JavaScript and TypeScript declaration files
# like remote generation does.
opt: target=ts
- name: connect-web
path: node_modules/.bin/protoc-gen-connect-web
out: gen
# With target=ts, we generate TypeScript files.
opt: target=ts

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

$ buf generate buf.build/bufbuild/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:

  • gen/buf/connect/demo/eliza/v1/eliza_connectweb.ts
  • gen/buf/connect/demo/eliza/v1/eliza_pb.ts

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

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

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

The full file includes some comments and additional RPCs, but the const above really is all Connect-Web 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.

Using the local files

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

- import { ElizaService } from "@buf/bufbuild_connect-web_bufbuild_eliza/buf/connect/demo/eliza/v1/eliza_connectweb";
+ import { ElizaService } from "../gen/buf/connect/demo/eliza/v1/eliza_connectweb";