-
-
Notifications
You must be signed in to change notification settings - Fork 421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: expose TRPC transformer from api package #1189
base: main
Are you sure you want to change the base?
Conversation
… than `@acme/api/transformer` Example of imports ```ts import type { AppRouter } from "@acme/api"; // <- correct: imports type import type { hello as _1 } from "@acme/api/hello"; // <- correct: imports type import { createCaller as _2 } from "@acme/api"; // <- incorrec: imports value import { transformer } from "@acme/api/transformer"; // <- correct: imports value from allowed module import { world as _3 } from "@acme/api/world"; // <- incorrect: imports value from restricted module ``` Results of running `pnpm lint`: ```sh [...]/create-t3-turbo/apps/expo/src/utils/api.tsx 8:1 error '@acme/api' import is restricted from being used. Only type imports from '@acme/api' are allowed @typescript-eslint/no-restricted-imports 10:1 error '@acme/api/world' import is restricted from being used by a pattern. Only certain modules from '@acme/api' can be imported @typescript-eslint/no-restricted-imports ✖ 2 problems (2 errors, 0 warnings) ```
@@ -15,9 +15,10 @@ | |||
"typecheck": "tsc --noEmit" | |||
}, | |||
"dependencies": { | |||
"@acme/api": "workspace:*", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have Expo shipped tree shaking yet? If not I think including this as a prod dep will leak backend code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SDK 52 it looks like https://x.com/anis_RNCore/status/1841790356930887859
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, didn't though of that. I'm not sure how to check if it's happening (I'm quite new with Expo and RN). Also, not sure if updating SDK is easy or not and should be done in this PR or in a different one.
Another option would be to move the transformer logic into a separate package so we are 100% sure it doesn't bring any BE code. For example @acme/transformer
or @acme/api-transformer
(though IMHO it feels much better to have it as a module in @acme/api
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am personally scared of adding an api package as a dependency, regardles of wether or not Expo does tree-shaking correctly or not. Might not be an issue, but it's something to be mindful of.
I personally have a @acme/shared
package that exports common utils/stuff that is safe to be shared everywhere. Adding another package just for the tranformer export might be a bit verbose for this repo though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, yeah it makes sense. I'll prepare that and revert the api package to just dev dependency in Expo.
packages/api/src/transformer.ts
Outdated
/** | ||
* tRPC transformer, used to serialize/deserialize data between client and server. | ||
* This export is used internally in `@acme/api` and in both `apps/nextjs` and `apps/expo`. | ||
* | ||
* ! IMPORTANT: changing this is a BREAKING CHANGE ! | ||
* ? Even though this helps swapping transformers, bear in mind that distributed Expo apps | ||
* ? will have the previous transformer bundled in their device, which will cause a mismatch | ||
* ? in how the data is sent and received (basically, all API requests will fail). | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good callout. add a @see
comment to trpc docs here too as a reference. not sure many people have used the "full input/output syntax" before
Ok, so swapping the transform is for cases where the current superJSON might not be enough? When will that be the case? |
Yes. Though bear in mind that this refactor ends up with having the transformer in a single place, which would allow you to easily/consistently enhance superjson (for example to perform serialisation for custom entities, see recipes section in superjson documentation).
In this case yes. For this specific issue, having the transformer logic in a single place would allow to to do a local fix on top of superjson's deserialise function, at least as a temporary fix while the fix is done at their end (this was the first approach I did in my project before swapping to I think having transformer in a single place is a good thing also to let developers choose the transformer they prefer, not due to any bug (for example, they may like more devalue due to performance/footprint) |
Context
In #1110, a problem with
superjson
caused the necessity of swapping transformer todevalue
. Code forsuperjson
is spread in 4 different places:packages/api/src/trpc.ts
apps/nextjs/src/trpc/query-client.ts
apps/nextjs/src/trpc/react.tsx
apss/expo/src/utils/api.tsx
Currently all of those places directly import
superjson
, which for this specific transformer is not super problematic (as its interface matches 1 to 1 to the expected transformer in tRPC), but it makes it inconvenient to swap to another or do any customization to its behavior, as it needs to be changed in all places.Proposal
Define the transformer in the api package and export it so it can be used in all places. Besides the benefit of applying DRY, it moves the entire responsability of the transformer where it belongs (close to the API) and the apps are just consumers.
Clarifications
Swapping transformers
Now swapping transformers will be super easy and convenient and fully transparent to developers in their local machines, though underneath it's a big breaking change as it drastically changes how the data is being sent through the API. This is why I found it necessary to add a disclaimer in that file just in case.
For example, if there are already distributed apps with the
superjson
transformer and there's a swap todevalue
, all of those app's API calls will break when the API usingdevalue
gets released.Of course, this would happen before as well and it's out of the scope of this repo, but IMO as now it's easier and "seems prepared to do it" adding the disclaimer made sense.
Expo app
Including the module
To include the
@acme/api/module
I had to add it in thetsconfig.json
'scompilerOptions.paths
section, pointing directly to the file.I'm not really sure if this is correct, but it's how I managed to make it work.
Restricting usage from
@acme/api
packageSince the
@acme/api
package is now a direct dependency in the expo app, I've added some eslint rules to prevent importing values directly from it, or from modules other than/transformer
.Example of imports
Requirements