Re:Full-Stack TypeScript with tRPC and React
by rcfox on Tuesday March 14, 2023 @04:36PM
(#35158092)
I'm currently working on a project with Typescript and gRPC. It's mostly great, but I'm somewhat disappointed with the generated code.
The frontend and backend get code generated differently. The backend is all interfaces (which, through some Nestjs magic get translated into protobufs) and the frontend is all objects. So on the frontend, you're expected to write:
const message = new MyRequest().setName('Bob').setFoo('bar');
On the backend, you'd just write: const message = { name: 'Bob', foo: 'bar' }
It's not a big deal, but it's weird. The backend ends up being more/overly strict about all values being set when you might just be fine with the default zero/empty values.Also, using a `oneof` field is correctly typed on the frontend as
myfield: TypeA | TypeB | TypeC`
But the backend gives you myfield: {a: TypeA | undefined, b: TypeB | undefined, c: TypeC | undefined}
For a future project, I'd look into buf or Connect instead of the default gRPC code generators, but for now, I just need to get stuff done.
Re:Full-Stack TypeScript with tRPC and React
by tczMUFlmoNk on Tuesday March 14, 2023 @06:19PM
(#35159854)
> Also, using a `oneof` field is correctly typed on the frontend as
myfield: TypeA | TypeB | TypeC
But what about when the `oneof` has multiple variants with the same base type? Like: message User {
oneof contact_info {
string email = 1;
string phone_number = 2;
}
}
Does this get compiled down to `contact_info: string | string` on the frontend? How do you tell which variant was used without encoding the discriminant?
Re:Full-Stack TypeScript with tRPC and React
by thinkingkong on Tuesday March 14, 2023 @04:20PM
(#35157829)
The big item is having no build step. With gRPC even no matter how fast your codegen is, it still needs to execute. With tRPC the return types from the routers are all automatically derived from whatever the mutations or queries return.
How does a TypeScript based codebase not have a build step?
Re:Full-Stack TypeScript with tRPC and React
by thinkingkong on Tuesday March 14, 2023 @05:21PM
(#35158931)
It depends how you're running the application but at _development_ in your IDE no build is required between the time you define your controller return and then switch into your react code.
Re:Full-Stack TypeScript with tRPC and React
by azangru on Tuesday March 14, 2023 @04:47PM
(#35158287)
> What does this get me that gRPC + Protobuf to generate a Typescript client wouldn't?
Have you tried running gRPC in the browser? How was the experience?
I have. It wasn't great, but it worked, and that was years ago. Is that the reason, though? tRPC generates a nicer client when targeting the browser?
Re:Full-Stack TypeScript with tRPC and React
by azangru on Wednesday March 15, 2023 @03:56AM
(#35165196)
Yes, this is my understanding. If the browser client and the server are both written in typescript, tRPC has a very attractive story of bridging the two. No protobufs though; so whatever performance wins gRPC brings, are absent from the tRPC story.
Re:Full-Stack TypeScript with tRPC and React
by mattwad on Tuesday March 14, 2023 @04:50PM
(#35158331)
I tried gRPC with Typescript once, and we needed transformers to transform gRPC to something usable - there's no optional fields in the v3 version for example. So we ended up with an extra layer on reading and writing to it, which was hard to test thoroughly.
My learnings were that gRPC is for compressing data, not type-safety. The type-safety is kind of just a necessity to get the compression to work, but it is not very expressive.
What does this get me that gRPC + Protobuf to generate a Typescript client wouldn't?
Edit: Ah. I see, you can use Typescript to define the IDL vs. learning Protobuf, Smithy, ...
Not sure I would take the trade-off. I like having the option of using something other than Typescript for my back-end. The dev experience isn't compelling enough for me. Contract changes are not an area I iterate on so often that a small delay in re-generating the client from an IDL causes me to grab for this dependency.