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
.proto
files. This isn’t a Deno specific issue, but it is something we will fix nonetheless. - It uses
require
statements, which are not supported by Deno. We will fix this by usingimport
statements instead. - It has no file extensions in the import statements, which is required by Deno. We will fix this by adding
.ts
to the import statements.
The problems are fixed by adding the following flags to the protoc
command:
--ts_proto_out=out
specifies the output directory for the generated code. For this to work, create aout
directory in the same directory as the.proto
files.--ts_proto_opt=esModuleInterop=true
stops old stylerequire
statements from being generated and instead usesimport
statements which are supported by Deno.--ts_proto_opt=importSuffix=.ts
will ensure the generated code includes file extensions with the.ts
suffix.
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"
}
}