Temporal SDK with typescript - json protobuf

Hi!
I’m using the typescript sdk in which I have a workflow that I want to pass a proto object as arg.
This is the error I’m getting:
ValueError: Unknown encoding: json/protobuf
According to the docs, I guess I need to use PayloadConverter to convert my payload from proto to json. I didn’t manage to do so.
I create the proto files using grpc_tools_node_protoc command, and not pbjs. is it possible to achieve what I need with this?

Hey,

Please take a look at our docs on the subject:

You can also use our omes load generator code as reference:

Thanks for the answer Roey!
Regarding the first option, think it’s depracated to some extent, as both pbjs commands aren’t working due to old syntax, and when adjusting them they create a wrong json-module (also if I deploy the project to docker image and run the commands at startup I won’t be able to manually change the file as suggested)

Regarding the second option, it looks interesting, but think it’s only supported in Golang, is there any doc/usage example for this?

Also, about my original problem, I tried running the workflow via a workflow client, having a generated proto as args and it worked. the only case in which it isn’t working for me is when I try activating the workflow an untyped (running it as a child workflow from Java in my case).

I’m currently creating the generated proto files like this:
mkdir -p src/generated && grpc_tools_node_protoc --js_out=import_style=commonjs,binary:src/generated --grpc_out=src/generated --proto_path=../common/protobuf ../common/protobuf/*.proto

What do you mean by deprecated?

Regarding the second option

What second option? I gave omes as a reference where we use protos with the TypeScript SDK.

having a generated proto as args and it worked

This may work if the inputs are serialized to JSON and the object only consists of basic types, otherwise, you’d need types that can be serialized to the standard protobuf JSON representation.

To consume protos in the TypeScript SDK, I recommend following the document and using protobufjs for generation.
It’s the only library that is supported out of the box by the SDK.

Thanks!
I think I’m starting to get it.
Now I’m getting thie error:

Cannot read properties of undefined (reading 'constructor') at _patchProtobufRoot

My code:

Proto file:

syntax = "proto3";

package io.company.proto;

message MessageRequest {
  string account_id = 1;
}

message MessageResponse {
  string s3_path = 1;
}

payload-converter.ts:

import { DefaultPayloadConverterWithProtobufs } from '@temporalio/common/lib/protobufs';
import root from './root';

export const payloadConverter = new DefaultPayloadConverterWithProtobufs({
  protobufRoot: root,
});

root.ts:

import { patchProtobufRoot } from '@temporalio/common/lib/protobufs';
import unpatchedRoot from './json-module';

export default patchProtobufRoot(unpatchedRoot);

I generated both root.d.ts and json-module using the suggested commands

Also, when trying to use the proto message as arg in my workflow I get an error that the type doesn’t exist in my root:
Module '"../../protobuf/root"' has no exported member 'io'. Did you mean to use 'import io from "../../protobuf/root"' instead?


import { ActivityOptions, proxyActivities } from '@temporalio/workflow';
import { io } from '../../protobuf/root'; // doesn't exist
import type * as activities from './activities';

const commonOptions: ActivityOptions = {
  retry: {
    maximumAttempts: 2,
  },
  scheduleToCloseTimeout: '10m',
};
const { getPDF } = proxyActivities<typeof activities>(commonOptions);

export async function ExportWorkflow(message: io.company.proto. MessageRequest): Promise<string> {
  return `The answer is ${answer}`;
}

Don’t create a root.ts file,

Here we use a root.js file and use the d.ts file generated by pbts:

Thanks! would have never though that’s the problem!
Now the message is coming in the workflow as expected, but when passed to the activity I get an error:

 Failed to parse activity args for activity getActivity: Unable to deserialize protobuf message without `root` being provide

workflow.ts:

import { ActivityOptions, proxyActivities } from '@temporalio/workflow';
import { io } from '../../protobuf/root';
import type * as activities from './activities';

const commonOptions: ActivityOptions = {
  retry: {
    maximumAttempts: 2,
  },
  scheduleToCloseTimeout: '10m',
};
const { getActivity } =
  proxyActivities<typeof activities>(commonOptions);

export async function ExportWorkflow(
  message: io.company.protocol.MessageRequest,
): Promise<io.company.protocol.MessageResponse> {
  const s3Path = await getActivity(message); // failing here
  return io.company.company.MessageResponse.create({
    s3Path,
  });
}

getActivity.ts:

import { io } from '../../../protobuf/root';

export async function getActivity(
  message: io.company.protocol.MessageRequest,
): Promise<string> {
return "hello"
}

This seems to be happening only when running the workflow via untyped invoke, when running the workflow directly from a client in the same project it’s working just fine

I guess I can do .toJson() between activities but not sure if it’s the best solution

This is happening because root wasn’t properly provided to the payload converter for the activity for some reason:

I would avoid using toJSON it’s not proto JSON compliant and you should be able to get the proto converter to work.

any reason for it to pass to the workflow but not the activity?
should I share some more code?

Workflow code is bundled internally by the SDK and runs in a separate v8 context.
Not sure exactly why you’re having issues with activities though.

You could try to console.log and figure out why root is undefined.
Feel free to also provide a repro and I’ll take a look at it.

1 Like