How to use ts-proto or Protocol Buffers with Deno
Published September 21, 2023
One of the most popular tools for using Protocol Buffers with TypeScript is ts-proto, which is a code generator that takes a .proto file as input and outputs TypeScript code. A problem for Deno users is that the generated code makes use of npm packages, meaning it can require some work before it can be used with Deno. In this post, I will show you how to configure ts-proto and Deno so that they work together.
Table of contents
Demo setup
For the sake of this post, suppose we have the following .proto files:
money.proto:
syntax = "proto3";
package demo;
message Money {
string currency = 1;
int64 amount = 2;
}
demo.proto:
syntax = "proto3";
package demo;
import "money.proto";
import "google/protobuf/timestamp.proto";
message Transaction {
google.protobuf.Timestamp time = 1;
Money value = 2;
}
In the same directory, we are going to install ts-proto with npm by running npm install ts-proto. We can then generate the code with the following command assuming protoc is installed:
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. ./demo.proto
Problems and solutions for ts-proto
There are 3 problems with the generated code that can be fixed by adding flags to the protoc command:
- It gets put in the same directory as the
.protofiles. This isn’t a Deno specific issue, but it is something we will fix nonetheless. - It uses
requirestatements, which are not supported by Deno. We will fix this by usingimportstatements instead. - It has no file extensions in the import statements, which is required by Deno. We will fix this by adding
.tsto the import statements.
The problems are fixed by adding the following flags to the protoc command:
--ts_proto_out=outspecifies the output directory for the generated code. For this to work, create aoutdirectory in the same directory as the.protofiles.--ts_proto_opt=esModuleInterop=truestops old stylerequirestatements from being generated and instead usesimportstatements which are supported by Deno.--ts_proto_opt=importSuffix=.tswill ensure the generated code includes file extensions with the.tssuffix.
Now, the full command looks as follows:
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=out --ts_proto_opt=esModuleInterop=true --ts_proto_opt=importSuffix=.ts ./demo.proto
Problems and solutions for Deno
The generated code uses npm packages. Luckily, Deno can run these packages. To make the code run without any changes, we can use import maps. Add the following to your deno.json
{
"imports": {
"long": "npm:long",
"protobufjs/minimal.ts": "npm:protobufjs/minimal.js"
}
}
The last entry of the import map is necessary because protobufjs/minimal.ts doesn’t actually exist as the protobufjs has JavaScript files, not TypeScript files. This is why we need to replace the .ts extension with .js in the generated code.
Summary
You can find a demo GitHub repository with everything I just mentioned here.
Run the following with ts-proto installed to generate the code:
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=out --ts_proto_opt=esModuleInterop=true --ts_proto_opt=importSuffix=.ts ./demo.proto
Add the following deno.json:
{
"imports": {
"long": "npm:long",
"protobufjs/minimal.ts": "npm:protobufjs/minimal.js"
}
}