Skip to content

Getting Started ​

Install ​

bash
npm install -g proto-codegen
# or
pnpm add -g proto-codegen

Create a config file ​

Add proto-codegen.config.json to your project root:

json
{
  "input": {
    "protoDir": "./proto",
    "include": ["*.proto"],
    "exclude": ["*_test.proto"]
  },
  "output": {
    "dir": "./generated",
    "clientsDir": "clients",
    "typesDir": "types"
  },
  "generation": {
    "generateTypes": true,
    "generateClients": true
  }
}

Add your proto files ​

Put .proto files in ./proto. The tool reads google.api.http annotations to build the HTTP client:

protobuf
syntax = "proto3";

package example;

message GetUserRequest {
  string user_id = 1;
}

message GetUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse) {
    option (google.api.http) = {
      get: "/api/v1/users/{user_id}"
    };
  }
}

Generate ​

bash
proto-codegen generate

Output structure:

generated/
├── index.ts
├── clients/
│   ├── index.ts
│   ├── http-client.ts
│   └── user-client.ts
└── types/
    ├── index.ts
    └── example.ts

Use it from your frontend ​

The generator also outputs an HttpClient interface (in generated/clients/http-client.ts) — implement it once with the HTTP library you already use (fetch, axios, etc.) and every generated client is ready to go:

typescript
import { HttpClient } from './generated/clients/http-client';

// Implement the generated interface with your HTTP library — just once
class MyHttpClient implements HttpClient {
  async get<T>(url: string, params?: any): Promise<T> {
    const res = await fetch(url + '?' + new URLSearchParams(params));
    return res.json();
  }
  // post, put, delete, patch...
}

Now create a client and call it — params and return type are inferred from the proto:

typescript
import { UserServiceClient } from './generated/clients/user-client';

const userService = new UserServiceClient(new MyHttpClient());
const user = await userService.getUser({ userId: '123' }); // typed as GetUserResponse
console.log(user.id, user.name, user.email);

Need to unwrap { code, data, message } response wrappers, or add a path prefix? See Using generated code.

What you need installed ​

Type generation shells out to protoc with the ts-proto plugin, so both need to be on your PATH:

  • protoc (Protocol Buffers compiler)
  • ts-proto is pulled in as a dependency, but its protoc-gen-ts_proto binary must be resolvable

The client layer has no such requirement. It runs anywhere Node 18+ does.

Next steps ​