Readit News logoReadit News
haney · 2 years ago
I'm currently in the process of removing tRPC from our codebase. It's been a nightmare of tight coupling. I've also found that it encourages more junior developers to not think about interfaces / data access patterns (there's a mapping from Prisma straight through to the component). It's fantastic for rapid prototyping but really paints you into a corner if you ever want to decouple the codebase.
miraantabrez · 2 years ago
This is the overlooked advantage of a schema (e.g. in GraphQL): it forces you to think about the data types and contract, and serves as a good way to align people working on different parts of the code. It also scales to other languages besides TypeScript which helps if you ever want to migrate your backend to something else or have clients in other languages (e.g. native mobile apps in Swift, Kotlin, etc).
spankalee · 2 years ago
TypeScript is actually a great schema language and fixes a number of problems in GraphQL's SDL, especially the lack of generics.

I think if you're defining a JSON API, that TypeScript is a natural fit since its types line up with with JSON - ie, number is the same in both and if you want something special like an integer with more precision, then you have to model it the same way in your JSON as your TypeScript interfaces (ie, a string or array). This makes TS good even for backends and frontends in other languages. You can also convert TS interfaces to JSON Schema for more interoperability.

thinkingkong · 2 years ago
We implemented tRPC at work and use all the other things that would have been 'tightly coupled' within our code base had we not planned a tiny bit ahead. tRPC is incredible but it's still just the transport layer between your back-end and your front-end. Allowing the internals of tRPC to be used deep within your business logic is just as bad as not having a clear 'controller' or 'router' layer where you can cleanly define inputs, schemas, and keep things separated. In this sense, if we ever decided to move from tRPC it would be relatively straightforward. Lifting an entire sub-system and running it over a queue for example would be trivial.
Swizec · 2 years ago
Your problem isn’t tRPC, your problem is that you have engineers who type things for typing’s sake. They’ll have the same problem in any tool.

There’s a learning curve to these things. It always starts with type FunctionIWroteTodayArgs = …, which is useless and tells you nothing.

After a few iterations (this takes years) people gradually realize that the goal is to describe your domain and create types/interfaces/apis that are reusable and informative, not just a duplication of your code. You then get more useful types and things start really flying.

I guess what I’m saying is work on that with your team, not on ripping out tRPC.

killthebuddha · 2 years ago
+1. Almost every time I actually write out a type it's because I want to communicate some domain knowledge. For everything else I use inferred types. IMO this is The Way.
dclowd9901 · 2 years ago
Eh? Useless? Maybe you’ve not written generic Javascript before but “type FunctionIWroteTodayArgs” has eliminated an entire class of problems we used to face with JS code.

If you’re talking about decoupled services, that’s about business domain composition more than type description. And those types benefit from a higher level description/reusability/transportability.

epolanski · 2 years ago
Could you expand on the nightmare of coupling?

I don't see how declaring an http client server side and consuming it client-side can be a worse thing.

We use the same pattern of creating services that then every consumer can use (a web interface, a cli, etc) and the fact that those things never get to break is a massive improvement over anything I've seen in the past.

haney · 2 years ago
If you only ever use Typescript and are sure you’ll never need to interact with the code in any other language or service in a different repo it’s fine. But as soon as you need to reuse that backend for anything else you’re stuck building something new.
welder · 2 years ago
Having the opposite experience with it as a small team, and I can see how it would work great in my past large teams. I bet you're gonna have the same complaints about any API you use not just tRPC (junior developers not thinking about interfaces).
haney · 2 years ago
I’m willing to admit that poor usage can make any tool a problem. But, tRPC is set up to make it easy to directly expose your backend for use in a component. For new projects that’s fantastic, for larger projects and teams having the ‘friction’ of defining a gRPC, GraphQL or REST endpoint is leading to more thoughtful API design and ability to keep isolation between layers.
macrael · 2 years ago
It's a dangerous anti-pattern to pass your db types through to your api handlers. Those are always different models and it's important to have an intermediary representation for the rest of your domain.
calebio · 2 years ago
Agreed. Although, sometimes it feels like this is a losing battle. I've worked with too many people who see that level of separation as "unneeded duplication", with constant complaints about having to update a bunch of different layers "just to add a new field.

IMO, at a minimum, you have your API layer model, your internal model, and your database model with a mapping layer at each boundary.

I rarely have problems when I structure it that way, but when working on applications that pass the same model from their API down to their database, or vice-versa, it's always full of the same types of problems that comes with tight coupling.

yieldcrv · 2 years ago
our problem with tRPC is that we don't have an easy to way to test the endpoint in say, curl or postman.

there is a “REST Wrapper” project out there, but learning that was even needed was … fun

I don't mind it, we found other ways to test

Out of curiosity, how do you add a memcache to tRPC if you dont want to write directly to the prisma database

JCharante · 2 years ago
I use trpc playground. It takes a few lines to setup.
vendiddy · 2 years ago
There are libs like ts-rest which I found to be less magical and easier to test.
alexdotjs · 2 years ago
We a tool bring which lets you track schema changes it over time and also ability to have an approval workflow for schema changes.

Would that help your team?

Happy to give you a demo if you reach out on Twitter dms or email (alex@trpc.io)

replygirl · 2 years ago
is it git?

Deleted Comment

random_kris · 2 years ago
Care to elaborate further? I've been building on top of trpc and even for inter service communication we use it
jitl · 2 years ago
We’ve use an API style similar to tRPC at Notion, although our API predated tRPC by 4 years or so.

You can build this kind of thing yourself easily using Typescript’s mapped types, by building an object type where the keys are your API names, and the values are { request, response } types. Structure your “server” bits to define each API handler as a function taking APIs[“addUser”][“request”] and returning Promise<APIs[“addUser”][“response”]>. Then build a client that exposes each API as an async function with those same args and return type.

We use this strategy for our HTTPS internal API (transport over POST w/ JSON bodies), real-time APIs (transport over Websockets), Electron<>Webview APIs (transport over electron IPC), and Native (iOS, Android)<>Webview APIs (transport over OS webview ipc).

For native APIs, the “server” side of things is Swift or Kotlin, and in those cases we rewrite the request and response types from Typescript by hand. I’m sure at some point we’ll switch to a binary based format with its own IDL, but for a single cross-language API that grows slowly the developer experience overhead of Protobuf or similar hasn’t seemed worth it.

purkka · 2 years ago
In our current project with a TS frontend and Python backend, we use an OpenAPI schema as the source of truth and openapi-typescript-codegen [0] to interface with it on the client side. While not perfect, it provides a very nice interface to our API with request/response typings.

I also wrote a 10-line mock API wrapper that you can call as mockApi<SomeService["operationName"]>((request) => response), and it will type-check that your mock function implements the API correctly and return a function that looks exactly like the real API function.

[0]: https://github.com/ferdikoomen/openapi-typescript-codegen

iansinnott · 2 years ago
Can second this approach. At a past job we did the same, except to connect the frontend to a Go backend.

I really like that the openAPI approach is language agnostic, and makes it relatively simple to support SDKs for many other languages if needed. For any company where the API itself is a product, OpenAPI is great.

Cannabat · 2 years ago
Another great library to generate TS types from OpenAPI is https://github.com/drwpow/openapi-typescript . It provides the types as single objects you access via indexing, which is pretty nice. There's a partner library to generate a typed fetch client.
scottmas · 2 years ago
This is the way. tRPC adds unnecessary complexity over simply inferring types. My theory is that no well maintained and promoted library adopting this approach has emerged, and that’s why you don’t see it discussed very often.
scriptsmith · 2 years ago
Is there some trick to doing validation of request data using this process? That's a valuable part of using something like tRPC, JSON Schema + type generation, zod, etc.
jitl · 2 years ago
We use an internal validator library that we infer request types from. It’s similar to Zod (but also predates it by a year).

I’ve also spent some time on a Typescript type to X compiler. My first prototype is open source and targets Thrift, Proto3, Python, and JSON schema: https://github.com/justjake/ts-simple-type/tree/main/src/com...

I’m not happy with the design decision in that codebase to try to “simplify” Typescript types before compiling, and probably won’t continue that implementation, but we have a few internal code generators that consume TS types and output test data builders and model clases we use in production.

I want to open source some of those bits but haven’t found the time.

chaos_emergent · 2 years ago
Deepkit is a fantastic solution for this. It uses a compilation step to inject metadata about types into plain JS.

https://deepkit.io/

dclowd9901 · 2 years ago
Yeah, I’m reading their sample code and wondering if this is just type-imbuing wrappers on top of XHR calls. It even asks you to provide the generic argument in the invocation (this sucks for trying to keep your dependency tree in order).
c-hendricks · 2 years ago
yencabulator · 2 years ago
That's seems to be completely missing validation. Typescript types are at their worst when they are lies, and the actual shape of the data is something completely different.
ushakov · 2 years ago
For cross-language, I can recommend Fern, which works with OpenAPI

http://buildwithfern.com

mdaniel · 2 years ago
You can recommend it in what context, from openapi (as they claim https://github.com/fern-api/fern#starting-from-openapi ) or from their ... special ... definition schema?

For those wanting less talk, moar code: https://github.com/fern-api/fern-java/blob/0.4.2-rc3/example... -> https://github.com/fern-api/fern-java/blob/0.4.2-rc3/example...

Regrettably, I wasn't able to readily find a matching openapi example, likely cause they really seem to believe their kid-gloves format is the future (or lock in, depending on how bitter one is feeling)

---

confusingly, their repo has a "YCombinator 2023" badge on it that just links to the badge itself. Some Algolia for Launch HN didn't cough up anything, but there was a Show HN I found: https://news.ycombinator.com/item?id=34346428

nip · 2 years ago
Similarly, we built a typed-client for inter-service communication using lambdas at Robocorp.

The requests and responses are inferred from the interface (published following semver) defined in Zod (including the errors that are following HTTP-like conventions: 401, 409…).

It also includes “hooks” for pre and post processing on the server side (effectively middlewares)

danvk · 2 years ago
I built something that sounds very similar at my last company called crosswalk. Open sourced here: https://github.com/danvk/crosswalk

One thing that worked surprisingly well: codegen TypeScript types from your database and use those in your API schema.

jensneuse · 2 years ago
I'm a big fan of tRPC. It's amazing how it pushed TypeScript only stacks to the limit in terms of DX. Additionally, it made the GraphQL community aware of the limitations and tradeoffs of the Query language. At the same time, I think tRPC went through a really fast hype cycle and it doesn't look like we're seeing a massive move away from REST and GraphQL to RPC. That said, we see a lot of interest in RPC these days as we've adopted some ideas from tRPC and the old NextJS. In our BFF framework (https://wundergraph.com/) we've combined file based routing with RPC. In addition to tRPC, we're automatically generating a JSON Schema for each operation and an OpenAPI spec for the whole set of operations. People quite like this approach because you can easily share a set of RPC endpoints as an OpenAPI spec or postman collection. In addition, there are no discussions around HTTP verbs and such, there's only really queries, mutations and subscriptions. I'm curious what other people's experiences are with GraphQL, REST and RPC style APIs? What are you using these days and how many people/teams are involved/using your apis?
ushakov · 2 years ago
Garph is like tRPC but for GraphQL: https://garph.dev

For REST APIs there's ts-rest (https://ts-rest.com), zodios (https://www.zodios.org) and Hono (https://hono.dev)

If your team uses multiple languages, there's Fern: https://www.buildwithfern.com

cassepipe · 2 years ago
+1 for ts-rest

The people on the Discord are very helplful

https://ts-rest.com/docs/comparisons/rpc-comparison

c-hendricks · 2 years ago
> it made the GraphQL community aware of the limitations and tradeoffs of the Query language

Could you expand on that? Our graphql types are generated from our federated schema whenever it changes, and response types for queries / mutations are generated whenever you save a file.

jensneuse · 2 years ago
With tRPC and similar frameworks, you infer client types from server definitions without including actual server code into the client. As much as I like GraphQL, one thing that IDEs really suck at is recognizing when generated code changes. E.g. with Jetbrains IDEs it sometimes takes forever until a change to the generated code is actually picked up by intellisense. VSCode is a little bit better regarding this. When you infer types in tRPC style, this whole problem is gone. You can even jump between the client usage and the server implementation. That said, this is not without a cost. Large Typescript codebases can slow down the typescript autocompletion and this approach only works best when both client and server are written in typescript and ideally in the same codebase.
cjblomqvist · 2 years ago
The thing that always strikes me is that verbs and paths are pretty tiny details (and also easy to abstract away using endpoint consumer generation libs) - then what else do you really get compared to your normal average web api? You still need to perform everything in each call to the backend anyway.
johnmorrison · 2 years ago
I love tRPC, it's by far the best fullstack DX I've ever seen and has such a brilliant API especially when combined with Zod.

Zod and tRPC are some of the most important projects in the future of TS imho, I think we're gonna see a beautiful bloom of tRPC inspired DX across the TS space in coming years.

Two projects already have clearly have tRPC DNA attacking different use cases are Ping's UploadThing (https://github.com/pingdotgg/uploadthing) and our Lusat (https://github.com/lusatai/lusat).

graftak · 2 years ago
I don’t really like Zod at all, it has very finicky and verbose types/generic params and it’s object generic won’t allow you to pass a data type, but instead you must pass zods own schema types as properties which are very messy and unintuitive.

Another annoyance is that the type of validation errors varies depending on what kind of schema you’re checking against. This makes it unpredictable to handle errors and there’s always too many edge cases.

Lots of room for improvement.

johnmorrison · 2 years ago
I don't disagree (not that I feel the same level of annoyance, on balance I love Zod for the time it saves me) but what would you recommend as an alternative?

The new valibot.dev looks cool but I haven't tried it yet.

matthewfcarlson · 2 years ago
I also recent did a personal project that uses tRPC and Zod and I agree that it was a fantastic experience. It also makes writing unit tests way easier.
MrJohz · 2 years ago
Do you know of anything in this region for MongoDB? The two big problems there seem to be type-safe data CRUD, but also migration of data. I see people referencing Mongoose a lot, but that seems like it's a big downgrade from Zod/TS in terms of type safety.
killthebuddha · 2 years ago
I've never used it for Mongo, but Prisma is a pretty great type-safe ORM that supports Mongo: https://www.prisma.io/docs/getting-started.
killthebuddha · 2 years ago
Zod is...so freaking great. I'm feeling kind of hyperbolic at the moment, so I'll even go so far as: for a certain style of programming, adding Zod to a codebase is as big of a win as adding TypeScript.
johnmorrison · 2 years ago
Absolutely. Zod is to the runtime what TypeScript is to the IDE.
epolanski · 2 years ago
I would also recomment effect/core + effect/schema, the pattern of creating typed services starting from schemas is there perfect for people that are more functional-programming leaning.
johnmorrison · 2 years ago
I saw that a while back, but it seems like a large learning curve and I don't immediately feel much drawing me towards it. Will be keeping an eye on it anyhow.
ShadowBanThis01 · 2 years ago
I just checked out the Zod intro pages but I still don’t know what problem it’s trying to solve. What is it adding to TypeScript (which I am not conversant in either)?

Thanks for any insight!

johnmorrison · 2 years ago
runtime
skybrian · 2 years ago
I'm wondering how they handle version skew and migration.

Fields have a lifecycle. When they're introduced, no clients or servers know about them. Clients and servers aren't restarted all at once. They won't have the same version of the types. Data will be written using one version of a type and read using a different version.

If you can guarantee all binaries, running programs, and data gets upgraded (no persistent data exists) then it might not be a problem. As soon as you have multiple organizations involved, guaranteeing all apps and servers sync to the latest version of a library, rebuild, and redeploy will be difficult.

Static type checking assumes no version skew. All the code in the binary got built with the same version of the library defining the types. It's a closed world.

yencabulator · 2 years ago
Pretty much "you do it". Zod can accept unrecognized fields, and then you move to optional fields, and finally once the dust settles you can make the field required.

Of course, the demographic that most desires what tRPC does is the least likely to think about that sort of stuff.

pennaMan · 2 years ago
That's why it's a tool for web apps
simonbw · 2 years ago
Web apps can still have the problem of the server and client getting out of sync.

1. Plenty of people deploy their frontend and their backend separately. 2. Even if frontend and backend are deployed at the same time, there's still the case where the user has the old version of the client loaded in their browser as a new backend gets deployed. I've seen some web apps tackle this by periodically checking if a new version is available and either refreshing the page or prompting the user to refresh the page.

tantalor · 2 years ago
Even with webapps, duration a binary rollout you can have clients reaching different versions of the app on each request (that's basic version skew).
dbbk · 2 years ago
SPA web-apps still have long-lived sessions
wb14123 · 2 years ago
Of course you don’t need schemas or code generation if you are only targeting one language.
rwieruch · 2 years ago
I have used tRPC for two ~50k loc web applications and love it. The DX is incredible. But I feel like tRPC had its hype time some time ago, so RSC’s hype quickly caught up. These days everyone speaks about whether to use or not to use RSC while there is such a great stable solution like tRPC out there. I am not against RSC, but there is way too much discussion about it. tRPC is a super pragmatic way to create applications with these days!

Edit: RSC = React Server Components - which come with their own read/write data philosophy.

ianlevesque · 2 years ago
For what it’s worth I have never heard of RSC and it’s not googleable. Have a link?
rwieruch · 2 years ago
React Server Components. Sorry, I edited the comment above.
candiddevmike · 2 years ago
What does the Royal Shakespeare Company have to do with RPC?

(or please define/link acronyms folks may not know!)

thowrasdf23 · 2 years ago
I'm guessing React Server Components which is apple to oranges with tRPC.
alexdotjs · 2 years ago
Heya! Creator of tRPC here, just wanted to drop by and say thanks for all the love ♥
tough · 2 years ago
Hey Alex, I had the luck to learn about tRPC directly from you when working at an open source company that used it for a while.

I quite remember tRPC being the only unknown part of the stack when I started there, and I was a bit scared, but tbh it was pretty easy to pick up and it's an awesome abstraction to make the server of fullstack codebases in typescript.

I always liked GraphQL but if you're working solo it doesn't make that much sense to have the GraphQL api as contract. With external devs a little bit more.

+1 to OpenAPI and being able to generate code, SDK's, docs, automagically.

Trufa · 2 years ago
Hey dude! We met in the prisma conf in Berlin a couple of years ago and had some beers after with an Aussie dude, since then I’ve used nothing but tRPC in all of my projects and couldn’t be happier, thanks for the hard work!
dfee · 2 years ago
This is the first time I’ve even seen an emoji in an HN comment. What?!

Unicode Character “♥” (U+2665)

ayewo · 2 years ago
How about emoji in a HN submission?

https://news.ycombinator.com/item?id=36159443

johnmorrison · 2 years ago
Thank YOU Alex!

This project has done incredible things for TypeScript

kanarian · 2 years ago
you are awesome!