Skip to content
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: v11 goals & suggestion box #3496

Open
12 of 22 tasks
KATT opened this issue Dec 27, 2022 · 49 comments
Open
12 of 22 tasks

feat: v11 goals & suggestion box #3496

KATT opened this issue Dec 27, 2022 · 49 comments
Assignees
Labels
😌 QoL Quality of life for contributors RFC Request for comments - please comment!

Comments

@KATT
Copy link
Member

KATT commented Dec 27, 2022

🚀 v11 is available to use and ready for production: https://trpc.io/docs/migrate-from-v10-to-v11

Summary

  • The main goal of v11 is to improve our own quality-of-life by removing v9-support as it makes the codebase very messy.
  • Work will be done in the next-branch
  • Ideas welcome from the community of API-decisions you think we should revisit.
  • We plan not to delete anything which hasn't previously been marked as @deprecated (if we rename, we'll do an alias)
  • Discord chat: https://discord.com/channels/867764511159091230/1057652120473575505

Plan

Before official alpha

Beta

  • Rename main to 10.x & next -> main

Maybes of things to do

Subtle breaking changes

See https://github.com/trpc/trpc/blob/next/.wip/changes.md

Hey you! Yes, you!

Anything you think we should add that should be fixed?

Funding

  • You can sponsor this specific effort via a Polar.sh pledge below
  • We receive the pledge once the issue is completed & verified
Fund with Polar
@KATT KATT added the 😌 QoL Quality of life for contributors label Dec 27, 2022
@KATT KATT changed the title feat: v11 feat: v11 goals Dec 27, 2022
@Nick-Lucas
Copy link
Contributor

Nick-Lucas commented Dec 27, 2022

This might be a bigger discussion and way out of scope here, but I think the React Query API could be cleaner long term if changes are on the table. There are a few painpoints with it

Take this rough example:

import { trpc } from '../utils/trpc';

function MyComponent() {
  const utils = trpc.useContext();
  
  const postsQuery = trpc.post.all.useQuery({ pageSize: 100 })
  const mutation = trpc.post.useMutation({
    onSuccess: () => {
      utils.post.invalidate(/* id etc */)
    }
  })
}

There are a couple painpoints:

  • trpc.post.useSomeHook is still not lintable by eslint because it doesn't follow the expected patterns of hooks. It may be easier to change the API rather than change the 'rules' which have been pushed since 2019
  • utils comes from useContext, which is a slight naming inconsistency in TRPC's patterns, and collides with the react API. At minimum useContext might be renamed to useUtils
  • We have to navigate utils and trpc separately in order to access different methods on the same procedure. I suppose this is because a procedure called "invalidate" might exist and collide if they were collocated. My experience is it does feel a little unergonomic though.

Something a bit tidier might look like:

import { useUtils, useMutation, useQuery } from '../utils/trpc';

function MyComponent() {
  const utils = useUtils();

  const postsQuery = useQuery(trpc => trpc.post.all({ pageSize: 100 })) // .all would likely return a config object, so query keys can be calculated, rather than being the trpc call itself
  const mutation = useMutation(trpc => trpc.post.update, {
    onSuccess: () => {
      utils.post.invalidate(/* id etc */)
    }
  })
}

Doesn't cover everything, but getting linting working seems like the important piece, and bringing the utils naming in line seems like low-hanging-fruit

@lilingxi01

This comment was marked as off-topic.

@oljimenez

This comment was marked as off-topic.

@TkDodo
Copy link
Collaborator

TkDodo commented Dec 28, 2022

Let's try to sync this with TanStack Query v5. Our roadmap is here:

@kran6a

This comment was marked as off-topic.

@KATT
Copy link
Member Author

KATT commented Dec 28, 2022

Thanks, everyone! In order to keep the conversation here tidy, we will hide discussions & things that wouldn't result in breaking changes - feel free to open separate issues on those. If you wanna debate or discuss specific topics here, join Discord. I just set up a channel specifically for v11.

  • @kran6a, your suggestion shouldn't require breaking changes and is somewhat covered by the SOA-example
  • @TkDodo, we would love to try to sync this with v5 of react-query
  • @Nick-Lucas, I love your suggestion and this is something we debated heavily and we considered exactly this design, we might revisit in the future but it's not something we'll do in the next 3-6 months
    • if we do we'll do codemods for people to migrate easily
    • @oljimenez, as a response to your comment: there's nothing with this API proposal that would need to work differently than how tRPC works today.
  • @lilingxi01, your suggestion would not require a breaking change but open an issue separated with a clearer description on what problem it's trying to solve

@KATT KATT changed the title feat: v11 goals feat: v11 goals & suggestion box Dec 28, 2022
@KATT KATT added the RFC Request for comments - please comment! label Dec 28, 2022
@SamueleBarbiera
Copy link

Content type support

Like this lib, it would be super cool to support like file type
https://www.npmjs.com/package/zod-form-data#

And give it as input and receive as the output a different type like a string or an URL type

@gcavanunez
Copy link

Improved error handling

I really love the flexibility offered by the errorFormatter, however also love the inference achieved when returning 200 status responses. Wished we could throw an Error, and be able to access the data in a similar style to how 200 responses do so, particularly for validation style of errors.

I know there have been discussions around the topic here

@iway1

This comment was marked as off-topic.

@jamesopstad

This comment was marked as off-topic.

@ollyde

This comment was marked as off-topic.

@SrBrahma

This comment was marked as off-topic.

@KATT
Copy link
Member Author

KATT commented Jan 3, 2023

Hidden a few comments that I think could all be non-breaking changes. Feel free to open separate issues or PRs.

@StringKe
Copy link

StringKe commented Jan 9, 2023

Add support for monorepo to be able to separate the definition of routes , and separately in the backend framework (nest or express or ...) implement the tsrpc api

https://discord.com/channels/867764511159091230/1061875891774443570/1061875891774443570

mermaid-diagram-2023-01-09-131346

@KATT KATT pinned this issue Jan 11, 2023
@AlexRMU
Copy link

AlexRMU commented Jan 12, 2023

#1939 and #1937

@ajs11174
Copy link

ajs11174 commented May 2, 2023

I'd like to propose a use case of providing a superset of the trpc object to lower level components. Today it is difficult to define multiple routers with shared components.

Here's an example using a monorepo with the following structure

├─ apps
      ├─ dashboard-client     //   combine multilple routers and create the `trpc` implementation
      ├─ dashboard-admin   //   combine multilple routers and create the `trpc` implementation
├─ libs
     ├─ router
          ├─ router-auth   // define routers
     ├─ feature
          ├─ feature-auth   // consume an abstract trpc client
     ├─ util
          ├─ trpc   // creates a shared t object and utilities like adminProcedure

What is possible today

  • Create a single shared router and provide all methods to all apps. The downside here is that you are shipping the entire API across multiple apps when each app only needs a subset of the procedures.
  • Implement the trpc object in each app and pass down the methods/props to components. The downside here is that you need to implement the passing down of methos/props in each app instead of just dropping in something like <MyComponent trpc={trpc} />
  • Create separate app specific feature libraries that import an app specific TRPC router/client. Again the downside here is that you need to implement this multiple times.

What would be a great enhancement

Provide a way to pass down a trpc client which is a superset of what the component needs.

Starting from the ground up here is how I would envision building this.

  1. The libs/util/t library should define the t object and Context as well as shared procedure definitions like authenticatedProcedure and publicProcedure.
import type { Context } from './create-context';

export const t = initTRPC.context<Context>().create({
  transformer: superjson,
});

const isAuthed = t.middleware(({ next, ctx }) => {
  if (!ctx.auth.userId) {
    throw new TRPCError({ code: 'UNAUTHORIZED', message: 'You must be signed in to view this content ' });
  }
  return next({
    ctx: {
      auth: ctx.auth,
    },
  });
});

export const authenticatedProcedure = t.procedure.use(isAuthed);

export const publicProcedure = t.procedure;
  1. The libs/router/router-auth library should import the shared t object and define its methods.
import { authenticatedProcedure, publicProcedure, t } from '@monorepo/util/trpc';
import { z } from 'zod';

export const authRouter = t.router({
  register: publicProcedure
    .input(z.object({ email: z.string(), password: z.string() }))
    .output(UserSchema)
    .mutation(async ({ input }) => {
      // psuedo-implementation
      return createUser(input);
    }),
  getUser: authenticatedProcedure.query(({ ctx }) => {
    return ctx.auth;
  }),
});

export type AuthRouter = typeof authRouter;
  1. A feature component from @monorepo/libs/feature/feature-auth
import { TRPCNextClientLike } from '@trpc/next';
import type { AuthRouter } from '@monorepo/router/router-auth';

export type MyComponentProps = {
  trpc: TRPCNextClientLike<{ auth: AuthRouter }>;
};

export function MyComponent({ trpc }: MyComponentProps) {
  const { data } = trpc.auth.getUser.useQuery();
  return <pre>JSON.stringify(data, null, 2)</pre>;
}

This is the main part of the proposal. This component shouldn't care about whatever else is on the trpc object as long as it satisfies {auth: AuthRouter}.

  1. Router created in apps/dashboard-client app
import { authRouter } from '@monorepo/router/router-auth';
import { t } from '@monorepo/util/trpc';

export const dashboardClientAppRouter = t.router({
  auth: authRouter,
  dashboardClientSpecificProcedure: t.query(() => {
    return 'something from dashboard-client';
  }),
});

export type DashboardClientAppRouter = typeof dashboardClientAppRouter;
  1. Client created in apps/dashboard-client app
import type { DashboardClientAppRouter } from './server';
export const trpc = createTRPCNext<DashboardClientAppRouter>({
  // ... implementation
});
  1. From here a page in dashbard-client app should be able to pass down any trpcNext client that satisfies {auth: AuthRouter}.
import { MyComponent } from '@monorepo/feature/feature-auth';
import { trpc } from '../utils/trpc/client'; // defined locally in the app

export function IndexPage() {
  return <MyComponent trpc={trpc} />;
}

Steps 4, 5, and 6 should be repeated for the apps/dashboard-admin app and yield the same result.


Ideal functionality

When using the client utility TRPCNextClientLike you should be able to do the following:

  • Have access to all of the routes that you have defined on the subtype. In this example we would have access to everything under trpc.auth but not dashboardClientSpecificProcedure
  • useContext() should also be available for trpc.auth but not dashboardClientSpecificProcedure when working within the shared component

Limitations I'd be willing to live with

  • All routers should reuse the same t object. This will ensure that the transformer, context, and errorFormatter are the same
  • If you want to use the TRPCNextClientLike type you should be mounting the router on the same path. In this example we mounted the authRouter to the auth path in the app

I think what I've laid out above would help make tRPC a bit more reusable within the context of a monorepo while still sticking to the core tennant of type safety. Additionally, I think that the only change needed here is an additional type for each client (TRPCReact, TRPCNext). The above code is valid from a functionality standpoint and will run.

This would help address #3798

@nahtnam
Copy link

nahtnam commented May 12, 2023

I think a couple of people have mentioned this but I think having a schema export tool would be very useful for projects that can't be monorepos. It would be something like a TRPC route called _schema and a CLI tool would make a request to the server and pull that schema into a format that another repo can initialize a TRPC client

This could be in the form of OpenAPI. The is already a library that can emit the basic structure of the entire TRPC app. Is it potentially possible to have that exported, imported on the frontend and instantiate a client that's fully typed? (I know we can just use OpenAPI libraries but it would be nice to have this all contained within the TRPC ecosystem)

@KATT KATT added the linear label May 25, 2023
@KATT KATT changed the title feat: v11 goals & suggestion box [TRP-40] feat: v11 goals & suggestion box May 25, 2023
@KATT KATT removed the linear label May 25, 2023
@KATT KATT changed the title [TRP-40] feat: v11 goals & suggestion box feat: v11 goals & suggestion box May 25, 2023
@ViktorQvarfordt

This comment was marked as resolved.

@ollyde

This comment was marked as resolved.

@ViktorQvarfordt

This comment was marked as resolved.

@KATT
Copy link
Member Author

KATT commented May 30, 2023

We won't be removing the distinction between queries and mutations in the short-term unless someone from the community can agree to:

  • build a configuration option in tRPC where you can configure "no distinction"-mode when you call initTRPC({})
  • updates all adapters that we have in the lib
  • writes all the documentation and does it well - mixing docs not having the distinction between queries/mutations with docs that do will be very confusing for a lot of users
  • agrees to maintain it for a long time

If someone is keen, we'll send you some money & might invite you to the core team. :)

I will now hide all discussions in this thread as it's noisy - feel free to open a separate discussion about it

@olehmisar
Copy link

olehmisar commented Sep 2, 2023

Fix name inconsistency. You have initTRPC, createTRPCProxyClient vs httpLink, httpBatchLink. Why keep TRPC uppercase but not HTTP or vice versa? I would suggest initTrpc and createTrpcProxyClient as it is easier to read and using camelCase for abbreviations is pretty much a standard in node.js ecosystem.

Also, utility types (e.g., inferReactQueryProcedureOptions) should start with a capital letter to be consistent with typescript conventions.

@xsjcTony

This comment was marked as abuse.

@tonyxiao
Copy link

v5 of react-query comes with suspense / experimental server component support. Are we able to leverage that in trpc?

@juliusmarminge
Copy link
Member

juliusmarminge commented Oct 30, 2023

Let's try to sync this with TanStack Query v5. Our roadmap is here:

I know this is a duplicate post, but just want to mention the stable version of react-query v5 just released today Link

Please adapt the v5, cheers. Also feel free to resolve this post :D

You can do this today using next tag: #4218 (comment)

@juliusmarminge
Copy link
Member

v5 of react-query comes with suspense / experimental server component support. Are we able to leverage that in trpc?

Should work ootb, we kinda made that feature in trpc first before upstreaming the work to tanstack query.

I use it in t3-turbo: https://github.com/t3-oss/create-t3-turbo/blob/41d84a2aed1dbd585469f68c5f464ab84b2f43b4/apps/nextjs/src/app/providers.tsx#L59

@michaelhays
Copy link

v11 beta has been released! https://trpc.io/docs/migrate-from-v10-to-v11

@bchilcott
Copy link

Define the root cause of this issue more clearly:

Property 'createClient' does not exist on type '"The property 'useContext' in your router collides with a built-in method, rename this router or procedure on your backend." | "The property 'useUtils' in your router collides with a built-in method, rename this router or procedure on your backend." | ... 4 more ... | "The property 'useDehydratedState' in your route...'.

I honestly have no idea what causes it but it seems to crop up any time there's a typescript issue between the frontend/backend projects.

@caidesen

This comment was marked as off-topic.

@schulzf

This comment was marked as off-topic.

@KATT
Copy link
Member Author

KATT commented Feb 1, 2024

cc @bchilcott @caidesen @schulzf

Please raise actual issues for any of these problems if you can reproduce them. I've used v11 in production for several months.

@akashdevcc
Copy link
Contributor

akashdevcc commented Apr 8, 2024

Here is the repository to reproduce the issue @bchilcott has mentioned above!

Property createClient does not exist on type
 in your router collides with a built-in method, rename this router or procedure on your backend." | "The property useUtils' in your router collides with a built-in method, rename this router or procedure on your backend." | ... 4 more ... | "The property useDehydratedState' in your route...'.

The reproduction steps has been described in the README file!
https://github.com/akashdevcc/zero-stack/blob/main/README.md

I have also created a new issue #5614 for the same.

@akashdevcc
Copy link
Contributor

akashdevcc commented Apr 11, 2024

@bchilcott In my reproduction repo (https://github.com/akashdevcc/zero-stack), I have resolved my issue. The root cause was probably using "commonjs" modules for "trpc" section of my project.

Because each package needs to be built first and export everything which was required by another package, somehow "trpc's unstable-core-do-not-import" types were not getting resolved. To resolve the issue, I explicitly re-exported those unresolved types from my packages/trpc package and it worked!!

However, as mentioned in tRPC code comments here, importing from this file should be avoided. So, I may file a new issue as instructed in this file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
😌 QoL Quality of life for contributors RFC Request for comments - please comment!
Projects
Status: Ready to work on - Focus
Development

No branches or pull requests