Giter Site home page Giter Site logo

trpc / trpc Goto Github PK

View Code? Open in Web Editor NEW
32.6K 94.0 1.2K 35.82 MB

🧙‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.

Home Page: https://tRPC.io

License: MIT License

TypeScript 79.36% JavaScript 1.97% CSS 1.46% MDX 17.21%
typescript api nextjs prisma next react

trpc's Introduction

tRPC

tRPC

Move fast and break nothing.
End-to-end typesafe APIs made easy.

codecov weekly downloads MIT License Discord
Twitter

Demo

The client above is not importing any code from the server, only its type declarations.


Note

You are looking at the next-branch of tRPC which is the current work in progress representing version 11.

  • The functionality is stable and can be used in production, but we may do small breaking API-changes between patches until we reach 11.0.0
  • The packages are published with the next-tag on npm
  • For the list of changes made, see https://trpc.io/docs/v11/migrate-from-v10-to-v11

Intro

tRPC allows you to easily build & consume fully typesafe APIs without schemas or code generation.

Features

  • ✅  Well-tested and production ready.
  • 🧙‍♂️  Full static typesafety & autocompletion on the client, for inputs, outputs, and errors.
  • 🐎  Snappy DX - No code generation, run-time bloat, or build pipeline.
  • 🍃  Light - tRPC has zero deps and a tiny client-side footprint.
  • 🐻  Easy to add to your existing brownfield project.
  • 🔋  Batteries included - React.js/Next.js/Express.js/Fastify adapters. (But tRPC is not tied to React, and there are many community adapters for other libraries)
  • 🥃  Subscriptions support.
  • ⚡️  Request batching - requests made at the same time can be automatically combined into one
  • 👀  Quite a few examples in the ./examples-folder

Quickstart

There are a few examples that you can use for playing out with tRPC or bootstrapping your new project. For example, if you want a Next.js app, you can use the full-stack Next.js example:

Quick start with a full-stack Next.js example:

# yarn
yarn create next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# npm
npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# pnpm
pnpm create next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

# bun
bunx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-prisma-starter trpc-prisma-starter

👉 See full documentation on tRPC.io. 👈

Star History

Star History Chart

Core Team

Do you want to contribute? First, read the Contributing Guidelines before opening an issue or PR so you understand the branching strategy and local development environment. If you need any more guidance or want to ask more questions, feel free to write to us on Discord!


Alex / KATT
👋 Hi, I'm Alex and I am the creator of tRPC, don't hesitate to contact me on Twitter or email if you are curious about tRPC in any way.

Project leads

The people who lead the API-design decisions and have the most active role in the development


Julius Marminge

Alex / KATT

Active contributors

People who actively help out improving the codebase by making PRs and reviewing code


Nick Lucas

Flo

Sachin Raja

Special shout-outs


Chris Bautista

Theo Browne
Ahmed%20Elsakaan
Ahmed Elsakaan

James Berry

Kamil Ogórek

Sponsors

If you enjoy working with tRPC and want to support us, consider giving a token appreciation by GitHub Sponsors!

🥇 Gold Sponsors

Tola
Tola

🥈 Silver Sponsors

Cal.com,%20Inc.
Cal.com, Inc.

🥉 Bronze Sponsors

Echobind
Echobind
Dr.%20B
Dr. B
Flylance
Flylance

😻 Smaller Backers

Ahoy%20Labs
Ahoy Labs
Dyaa
Dyaa
Brooke
Brooke
Max%20Greenwald
Max Greenwald
Tom%20Ballinger
Tom Ballinger
Faraz%20Patankar
Faraz Patankar
Adam%20Slaker
Adam Slaker
Dmitry%20Maykov
Dmitry Maykov
Chris%20Bradley
Chris Bradley
Ahmed%20Elsakaan
Ahmed Elsakaan
Hampus%20Kraft
Hampus Kraft
Illarion%20Koperski
Illarion Koperski
SchlagerKhan
SchlagerKhan
Jared%20Wyce
Jared Wyce
fanvue
fanvue
Andrew%20Brown
Andrew Brown
Ascent%20Factory
Ascent Factory
Unkey
Unkey
Jonas%20Strassel
Jonas Strassel
Jordy
Jordy
Daniel%20Burger
Daniel Burger
Scale%20Leap
Scale Leap
Drew%20Powers
Drew Powers
Drizzle%20Team
Drizzle Team
Liran%20Goldman
Liran Goldman
Spencer%20McKenney
Spencer McKenney
Proxidize
Proxidize
Oskar%20Hertzman
Oskar Hertzman
Imamuzzaki%20Abu%20Salam
Imamuzzaki Abu Salam

All contributors ✨

A table of avatars from the project's contributors


Powered by Vercel

trpc's People

Contributors

allcontributors[bot] avatar amirhhashemi avatar anthonyshew avatar bautistaaa avatar bennettdams avatar c-ehrlich avatar colelawrence avatar colinhacks avatar dependabot[bot] avatar iduuck avatar ixahmedxi avatar jgoux avatar jlalmes avatar jonparton avatar joshuakgoldberg avatar juliusmarminge avatar jussisaurio avatar kamilogorek avatar katt avatar mmkal avatar mozzius avatar neo773 avatar nick-lucas avatar nilskj avatar nsttt avatar quiibz avatar renovate[bot] avatar rhenriquez28 avatar sachinraja avatar sheraff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trpc's Issues

Pretty console logs outgoing requests

Would be nice if you could optionally log outgoing requests in the client, colour coded by type, with input, & highlight responses.

Especially nice for queries as the input params are urlencoded json, they're quite hard to read by looking at the URL.

Copy endpoint resolver format from GraphQL?

How it is right now:

const rootRouter = trpc.router().endpoint(
  'hello',
  trpc.endpoint((ctx: Context) => (arg1: string, arg2: string) => {
    return {
      hello: `${data}`,
      path: ctx.req.path,
    };
  }),
);

Suggestion:

  • 1 single input argument being a named options object
const rootRouter = trpc.router().endpoint(
  'hello',
  trpc.resolver((input: { name: string }, ctx) => {
    return {
      hello: input.name,
    };
  }),
);

Add `trpc.useContext().setQueryData()` callback

When updating values in the query cache which rely upon the old value, an updater callback comes handy, e.g.:

setQueryData(
  ["Page.findAllBySiteId", data.siteId],
  (prev) => [...prev, data],
);

Instead of:

const prevPagesBySiteId =
  getQueryData(["Page.findBySiteId", data.siteId]) ?? [];
setQueryData(
  ["Page.findAllBySiteId", data.siteId],
  [...prevPagesBySiteId, data],
);

This overload should be made available through trpc.useContext().setQueryData.

API recommendations

Some recommendations for improvements to the API:

  • I don't really see any reason to split up @trpc/server and @trpc/client. I think they should be merged into a single trpc module. It makes the out-of-the-box experience so much cleaner.
  • Then the @trpc scope can be used for specific plugins/adapters etc. You could publish the Express adapter as @trpc/express for instance. That would also let you properly specify all the requisite peerDependencies (which I don't believe are specified using the current @trpc/server/adapters/express approach since it doesn't have its own package.json.
  • I think import * as imports are a bad user experience. I submitted a PR with a solution.
  • Simplify the function names: createTRPCClient => createClient, createNextApiHandler => createHandler, CreateNextContextOptions => ContextOptions. It should be standard across adapters. It's obvious that a function called createHandler imported from "@trpc/server/adapters/next" is creating a Next.js API handler. It doesn't need to be in the name.
  • The adapters should export a createContext function that can automatically type opts with the necessary types.
  • I think all requests (queries and mutations) should get served from a single URL. The path can be added as an additional query parameter for queries and in the body for mutations. This would also simplify the Next/Express adapters (no need for a catchall API route).

Less important:

I think there should be a way of manually typing the input of a resolver without passing a validator into input. This is easy to do if the resolver functions had two parameters instead of one. I have this working on a local branch (type inference still works the same).

I propose this syntax/convention:

const router = trpc.router<Context>().query('test', {
    resolve(input: number, req) {
      return input;
    },
  });

The req variable above if of type { ctx: TContext, type: ProcedureType }. This leaves the door open to future API expansion, as req can act like a catchall for any additional properties we want to pass into resolvers.

TSDX?

Should we use https://github.com/formium/tsdx?

If we create a bunch of different libs it might get messy as you can only have one output and I am not sure if you can do specific browser/node build

Make `createContext` optional

Default to ()=>({})

Simplifies use cases where it isn't required. e.g if the user is passing a token in the input.

[RFC] SSR Magic - `getDataFromTree()`

Idea

Similar to Apollo's way of prefetching.

Should be possible to prefetch all data on the server automatically without using any getStaticProp/etc. Could be done in _app.tsx

Since the queries have the paths and the input one could just invoke the router server side when calling to useQuery() and write it to the react-query cache.

Dummy code

import { createTRPCClient } from '@trpc/client';
import type { AppContext, AppProps /*, AppContext */ } from 'next/app';
import { QueryClientProvider } from 'react-query';
import { Hydrate } from 'react-query/hydration';
import { trpc } from '../utils/trpc';
import type { AppRouter } from './api/trpc/[trpc]';
import { getDataFromTree, ssrLink } from '@trpc/react/ssr';
import App from 'next/app'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <QueryClientProvider client={trpc.queryClient}>
      <Hydrate state={trpc.useDehydratedState(pageProps.dehydratedState)}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  );
}
if (!process.browser) {
  MyApp.getInitialProps = async (appContext: AppContext) => {
    // calls page's `getInitialProps` and fills `appProps.pageProps`
    const appProps = await App.getInitialProps(appContext);

    await getDataFromTree(trpc)

    return { 
      ...appProps, 
      dehydratedState: // ... todo, 
    }
  };

}
export default MyApp;

References

Next steps after

And as a step two we could do something really cool, which is to provide a wrapper around <Link> (Blink?) and optimistically preload queries from other routes on e.g. mouse over.

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

[RFC] OpenAPI aka swagger support

Would be possible for each query/mut to have a schema:-prop next to the resolver where one could define an api schema of which the structure could be statically enforced as well.

Maybe the input arg of the schema could also be used as an actual input validator?


Another way could possibly be to statically infer the swagger schema with codegen, but then one couldn't actually document properties.

Improve react-query support

  • useQuery
  • useMutation
  • useInfiniteQuery - #82
  • Hydration / rehydration
  • prefetchQuery on server for SSR/SSG
  • prefetchQuery typed on client - #81

Add `trpc.useContext().getQueryData()`

In order to execute updates optimistically with React Query, data has to be read from the queryClient, as seen in the linked example:

// Snapshot the previous value
const previousTodos = queryClient.getQueryData('todos')

Unfortunately, the situation isn’t so easy with tRPC, as it doesn’t provide a getQueryData method, but only setQueryData. After digging into the source code, I figured out an escape hatch to perform query data reads with – but it relies upon the CACHE_KEY_QUERY internal constant. The issue is showcased below without the additional clutter of handling optimistic updates via onMutate and onError:

export function useCreatePage() {
  const { setQueryData, queryClient } = trpc.useContext();

  return trpc.useMutation("Page.create", {
    onSuccess: (data) => {
      setQueryData(["Page.find", data.id], data);

      const prevPagesBySiteId =
        queryClient.getQueryData<Page[]>([
          "Page.findBySiteId",
          data.siteId,
          "TRPC_QUERY", // CACHE_KEY_QUERY
        ]) ?? [];
      setQueryData(
        ["Page.findAllBySiteId", data.siteId],
        [...prevPagesBySiteId, data],
      );
    },
  });
}

The code above should be replaceable with:

export function useCreatePage() {
  const { getQueryData, setQueryData } = trpc.useContext();

  return trpc.useMutation("Page.create", {
    onSuccess: (data) => {
      setQueryData(["Page.find", data.id], data);

      const prevPagesBySiteId =
        getQueryData(["Page.findBySiteId", data.siteId]) ?? [];
      setQueryData(
        ["Page.findAllBySiteId", data.siteId],
        [...prevPagesBySiteId, data],
      );
    },
  });
}

@trpc/server - router (query, mutation, ...) input + zod = "Excessive stack depth comparing types" TS ~4.3.2

@trpc/server: 7.0.0

Given the following usage of mutation or query with input:

import {router} from "@trpc/server"
import {z} from "zod"

router()
  .mutation("xyz", {
    input: z.object({x: z.string()}),
    resolve({input}) {}
  })

The input typing causes error Excessive stack depth comparing types. I'v downgraded typescript from 4.3.2 to 4.2.4 and the error is gone. I don't know if its a TS bug, or BC-break. I did not test all versions >4.2.4 up to 4.3.2

Use different transformers for client<->server

First of all I want to say thank you very much for making this library! I was already looking for a library like this for quite some time.

There is one thing I would like to ask/suggest:

Superjson is great for preserving type information, but has some performance drops for example when using with lots of nested dates. There is another library, devalue, which has a much better performance, but it is not usably for client->server for security reasons.

Thats why I currently use superjson for client->server and devalue for server->client in some of my apps.

Is it currently possible to use different transformers for each direction? If not, I would like to suggest adding this.

examples/next-prisma-todomvc next build failed

Hi, @KATT.

I hit some wired problem when build my trpc+prisma+next project which forked from examples/next-prisma-todomvc.

yarn build
> Build error occurred
{ [Error: ENOENT: no such file or directory, mkdir '/Users/z/Projects/Node/bar-alarm/.next/export/_next/AhH1ZtavD0fj_EwrOE7Zc']
  errno: -2,
  code: 'ENOENT',
  syscall: 'mkdir',
  path:
   '/Users/z/Projects/Node/bar-alarm/.next/export/_next/AhH1ZtavD0fj_EwrOE7Zc' }
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Then I tried in the original example project, it occured again:

> Build error occurred
{ [Error: ENOENT: no such file or directory, mkdir '/Users/z/Downloads/next-prisma-todomvc/.next/export/_next/5_elkb7RsbNxWfqfh-96P']
  errno: -2,
  code: 'ENOENT',
  syscall: 'mkdir',
  path:
   '/Users/z/Downloads/next-prisma-todomvc/.next/export/_next/5_elkb7RsbNxWfqfh-96P' }
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Any clues why?

Implement exchanges/middlewares to make client more pluggable

Background

I'd like to make the calls through tRPC-client more customizable so you can intercept procedure calls and decide what to do with them.

References

Visualisation from urql docs

What this would (hopefully) enable

Each of these could be implemented already, but it'd likely make things quite convoluted

  • Procedure call batching - #273
  • WebSocket transport - #268
  • More robust subscription support
  • Our own query cache, as a plugin
  • Logging & devtools
  • Supplying "retry" links
  • Moving transformers to a plugin
  • Would make creating libs for Svelte/Vue/etc easier
  • Potentially, move away from relying on react-query in @trpc/react and make trpc lighter (unlikely to happen though)

Batching - safeguard for max url length

👋 Do you want to pick this up? See #506 (comment)


Challenge described:

  • Max characters for a url is ~2k characters (I think)
  • Currently the batching lumps everything in a request as /api/trpc/posts.byId,posts.byId.query,posts.byId?batch=1&input=${encodeURIComponent(JSON.stringify(input))}

This could potentially lead to situations where the client constructs a url that is too long.

A few things we could do to prevent this

  • Change the way we build the query
    • Given query, this could be built like like /api/trpc/3*posts.byId?batch=x
  • Make sure the data loader splits up the queries into separate requests when exceeding limit of x
  • What I don't want to do is to use anything but GET for queries

Refs in the code


I think the best interface way of handling this is to allow doing httpBatchLink({ maxURILength: 2048 }).

[RFC] Procedure call batching

Would be cool if one could automatically batch all parallel queries into the same request on the client. https://github.com/graphql/dataloader for inspo.

Shouldn't be that hard to do. Could enable some cool stuff like making a db transaction across multiple parallel mutations.


considerations:

  • should query/mutation/subscriptions be able to batched together or should we batch per type?
  • query-specific: how to avoid caching of a mix of "public"/"private" procedures if one uses caching
  • subscriptions: tricky
  • opt-in or opt-out?

Allow passing no `input` on mutations with optional/no input

When no input is specified for a mutation on the back-end, the corresponding useMutation hook shouldn’t require any variables to be given. Unfortunately, this isn’t the case over here:

image

With code similar to:

const { mutate: softCancelSubscription } = trpc.useMutation(["WithoutInput"]);

mutate(); // Type checking fails when no parameter is given

Shape up chat example

  • Make useSubscription()-support in react client
  • Make UI pretty
  • Add auth(?)

Potential features:

  • Who's online
  • Typing indicators

next major

  • cleanup @deprecated
  • change loggerLink to accept a custom logger with proper opts
  • maybe change ending link to return Error downstream if (!result.ok)
  • ~maybe force you start trpc client with links`
  • stricter context

Log internal server errors during development by default

Currently, I’m using the following code snippet for Next.js at the api/trpc/[trpc] route:

import * as trpcNext from "@trpc/server/adapters/next";

const __DEV__ = process.env.NODE_ENV !== "production";

export default trpcNext.createNextApiHandler({
  router: appRouter,
  createContext,
  onError: __DEV__
    ? ({ error, type, path, input }) => {
        if (error.code === "INTERNAL_SERVER_ERROR") {
          // eslint-disable-next-line no-console
          console.error("Error:", {
            type,
            path,
            input,
            cause: error.originalError,
          });
        }
      }
    : undefined,
});

I think it would be useful to always log unhandled errors (where error.code === "INTERNAL_SERVER_ERROR") to the server’s console like this during development, even when no onError is specified.

React lib/hooks

React hooks for ease of use. Things that would be nice to see

  • Normalized global cache
  • Fetch policies
  • Hooks for ease of use on both queries and mutations
  • Composition of multiple calls

Adding lockfiles to examples

  • examples/* doesn't have lockfiles right now as they're part of the monorepo
  • this might make the vercel deployment randomly fail if someone publishes a bad build of the 10^100 sub-deps of next (as shown yesterday in #91 (comment))
  • would be nice if they had lockfiles but not sure how to make it happen in the yarn workspace

Ideas / things to do

General

  • I want args to be changed to be 1 single input argument (similar to GraphQL best practices)
  • Traceability. Listen to errors so they're loggable - express/next middlewares & api client
  • custom error printing - printing validation output would prob be nice
  • Any way of automatically testing/asserting type inference in jest or whatnot?
  • nice readme with some gifs showing the type autocompletion
  • Tests

docs

  • relationship with graphql / why not graphql?
  • gif / video with autocompletion
  • link examples in readme
  • alternatives? blitz, hyper-ts
  • mutations / queries / subscriptions docced
  • input validators doc

Server

Resolvers with input schema

  • make input optional
  • Possible to bulk-add input schema (like .queries()
  • make queries(), etc private - disallow creating endpoints without input schema

niceities

  • better inferer-helpers & clean them up - e.g. inferQueryData<TRouter, TPath extends string>
  • subscription pull factory in core? see chat example for how it works (no need for redis or smth for queue)
  • Create connect middleware / standalone server
  • refactor: remove invokeX-fns, make nicer impl
  • get rid of @typescript-eslint/no-explicit-any across codebase
  • get rid of @typescript-eslint/ban-types
  • update chat example to use type inference rather than importing prisma types

Client

  • Can we use react query select?
  • is http subscriptions a terrible idea that shouldn't be part of this?
  • allow useQuery(string) rather than forcing useQuery([string, ...args]) on queries w/o inputs
  • move o onSuccess to react specific implementation as the client doesn't have the transformed data
  • query keys needs to be prefixed with sub/mut/quer so they don't collide
  • prefetch query, rename ssr prefetch

old/completed

  • Breaking: enforced input data validation - using zod/yup
  • Turn into monorepo with client/react/server as separate libs
  • 🐛 Fix type inference on react lib after monorepo migration
  • Subscriptions
    • Add TTL
    • Cancel requests in client on unsub
    • Cancel sub on cancel req
  • Next.js support
  • Next.js reference project
  • Refactor createExpress / createNext things
  • Remove logs, etc (add debug()?)
  • publish
  • Order of get initial data after trigger on
  • Zodendpointt that calls endpoint (async)?
    • See if works with other validation
      • Validate unknown >> input
      • Yupesque
  • Turn endpoints private
  • Integrations in lib
  • See if works with tsdx
  • Subscriptions using react query
  • Refactor fetch methods to be cancellable
  • Subscription Refactor on/on to be async, remove initial data ting
  • Move adapters to adapters folder
  • data transformers are a bit hacked in and it's unclear where they belong - maybe move to implementation code smh?

tests

[RFC] Enforce dot-notation in sub-routers

Initially when writing trpc>1 I was thinking I would use / as a separator between sub-routers, but it's more readable to have the whole path written out.

I've forgotten to add the . at the end a few times when creating sub-routers and it's a bit annoying. Don't see any harm in enforcing it.

This would be a major version change, but easy refactor - could be done application-wide with a simple regex.

Change proposal

Before

const appRouter = createRouter()
  .merge('users.', users) // prefix user procedures with "users."
  .merge('posts.', posts) // prefix poosts procedures with "posts."
  ;

After

const appRouter = createRouter()
  .merge('users', users) // prefix user procedures with "users."
  .merge('posts', posts) // prefix poosts procedures with "posts."
  ;

Implement a response envelope

Would make it more extensible

Discriminated union suggestion:

type ResponseEnvelopeSuccess<TData> = {
  ok: true;
  statusCode: number;
  data: TData;
};

type ResponseEvenelopeErrorObject = {
  message: string;
  stack?: string;
};

type ResponseEnvelopeError<TError extends ResponseEvenelopeErrorObject> = {
  ok: false;
  statusCode: number;
  error: TError;
};

type ResponseEnvelope<TData, TError extends ResponseEvenelopeErrorObject> =
  | ResponseEnvelopeSuccess<TData>
  | ResponseEnvelopeError<TError>;

@trpc/server - Inferrence quirk with ProcedureInputParserCustomValidatorEsque<TInput>

@trpc/server: 7.0.0
Typescript tested: 4.2.4 & 4.3.2

There is a small, but annoying quirk with inference on the ProcedureInputParserCustomValidatorEsque<TInput>:

router()
  .mutation("write", {
    // 1. "a" parameter is inferred as unknown (=correct), but the return type is not inferred up the type tree
    input: (a) => "x",
    // 2. you need to explicitly type (a: unknown), for the inference of return type go up the tree:
    // input: (a: unknown) => "x"
    resolve({input}) {
      // input is resolved as unknown in case 1. and correctly as "string" in 2. case
    }
  })

WebSocket transport

Would be nice with a WebSocket transport. Subscriptions through long-polling are very 2010.

Error thrown in Resolver always results in a 500 Status code

I have this resolver setup

export const authRouter = createRouter().mutation("login", {
  input: LoginInput,
  async resolve({ input, ctx }) {
    throw trpc.httpError.unauthorized("Optional message");
  },
});

Which should result in a 401 status code, however, the error that is actually sent by the endpoint is (with a 500 code):

TRPCError: Optional message
    at internalServerError (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:264:10)
    at getErrorFromUnknown (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:289:10)
    at Object.requestHandler (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:558:19)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:94:5)
    at async C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:184:5
    at async apiResolver (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\api-utils.js:8:1)
    at async DevServer.handleApiRequest (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:67:462)
    at async Object.fn (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:59:492)
    at async Router.execute (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\router.js:25:67)
    at async DevServer.run (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:69:1042)
    at async DevServer.handleRequest (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:34:504) {
  originalError: HTTPError: Optional message
      at Object.unauthorized (C:\Users\malte\Github\streamparty\monorepo\node_modules\@trpc\server\dist\http-c0b986e9.cjs.dev.js:98:28)
      at ProcedureWithInput.resolve [as resolver] (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:1381:66)
      at ProcedureWithInput.call (C:\Users\malte\Github\streamparty\monorepo\node_modules\@trpc\server\dist\router-91b6d32c.cjs.dev.js:61:31)
      at Router.invoke (C:\Users\malte\Github\streamparty\monorepo\node_modules\@trpc\server\dist\router-91b6d32c.cjs.dev.js:266:22)
      at Object.mutation (C:\Users\malte\Github\streamparty\monorepo\node_modules\@trpc\server\dist\router-91b6d32c.cjs.dev.js:285:21)
      at Object.requestHandler (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:460:29)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (node:internal/process/task_queues:94:5)
      at async C:\Users\malte\Github\streamparty\monorepo\packages\website-next\.next\server\pages\api\v2\trpc\[trpc].js:184:5
      at async apiResolver (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\api-utils.js:8:1)
      at async DevServer.handleApiRequest (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:67:462)
      at async Object.fn (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:59:492)
      at async Router.execute (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\router.js:25:67)
      at async DevServer.run (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:69:1042)
      at async DevServer.handleRequest (C:\Users\malte\Github\streamparty\monorepo\packages\website-next\node_modules\next\dist\next-server\server\next-server.js:34:504) {
    originalError: undefined,
    code: 'UNAUTHENTICATED',
    statusCode: 401
  },
  code: 'INTERNAL_SERVER_ERROR'
}

Should this be the case? Let me know if you need any extra information

@trpc/react query cache is shared between Next.js pages

Hey there I noticed potentially a big problem with the instructions on how to use library with security implications.

I noticed this issue Sendouc/sendou.ink#354 and started investigating it. Essentially the query cache is shared between pages. In my case it just added useless bloat to the getStaticProps JSONs since it had queries from various pages but of course if you use say getServerSideProps with sensitive information then cache being shared like this between requests is dangerous.

Here is how I fixed it: Sendouc/sendou.ink@e42bc3c

I noticed a discrepancy between how trpc docs instruct to do it: https://trpc.io/docs/nextjs#2-create-a-trpc-client and how React Query docs do: https://react-query.tanstack.com/guides/ssr#using-hydration (see the bolded part especially).

`trpc.error` syntax

I see the appeal of the trpc.httpError.forbidden syntax for common HTTP codes, but I think there should be a fallback function for easily throwing HTTPErrors with any numerical code. Similar to the v0.x syntax:

trpc.error(code: number, opts; { status?: string; message?: string; originalError?: Error })

throw trpc.error(418, { message: "I'm a teapot" })

I'm used to thinking about HTTP codes as numbers. It looks like you're trying to require a string representation of the status code (TCode) which is understandable. You could use something like this to convert the numerical code into a string code: https://github.com/prettymuchbryce/http-status-codes/blob/master/src/status-codes.ts

Alternatively, just expand trpc.httpError to include all possible status codes. It's odd that only certain codes are available.

[RFC] Simplify refactoring by allowing aliasing

When refactoring it might be useful to rename a procedure but still maintain the old one functional.

Let's say you have something like this

export const appRouter = createRouter()
  .mutation('createPost', {
    input: z.object({
      text: z.string(),
    }),
    resolve({ input }) {
      // do something
    },
  })

You later wanna split up all your "post"-related stuff into a separate router, then it'd be nice to be able to do this:

option 1

export const mainRouter = createRouter()
  .merge('posts.', postsRouter()
  .aliasMutation('createPost', 'posts.create') // <--


const postsRouter = createRouter()
  .mutation('create', {
    input: z.object({
      text: z.string(),
    }),
    resolve({ input }) {
      // do something
    },
  })

option 2

export const mainRouter = createRouter()
  .merge('posts.', postsRouter()
  .mutation('createPost', 'posts.create') // <---


const postsRouter = createRouter()
  .mutation('create', trpc)

Loosen `trpcNext.CreateNextContextOptions` type for better SSR support

I just started using trpcNext.CreateNextContextOptions and noticed that it’s typed as follows:

type CreateNextContextOptions = {
  req: NextApiRequest,
  res: NextApiResponse,
};

While it works great for API requests, the context that gets passed to getServerSideProps only contains a portion of this information – the basis of NextApiRequest and NextApiResponse objects:

import type { IncomingMessage, ServerResponse } from "http";

type GetServerSidePropsContextOptions = {
  req: IncomingMessage & { cookies: NextApiRequestCookies },
  res: ServerResponse,
};

In order to support SSR better and keep the API context versatile, I propose a context in which only the req object is mandatory during SSR, as responses shouldn’t be altered in that case. API functionality is kept intact:

type CreateNextContextOptions =
  | {
      req: NextApiRequest;
      res: NextApiResponse;
    }
  | {
      req: IncomingMessage & { cookies: NextApiRequestCookies };
      // Altering responses during SSR isn’t a good idea:
      res?: never; // Previous thought: `res?: ServerResponse;`
    };

Move away from `pathAndArgs` in @trpc/react

Really not a fan of the pathAndArgs syntax. Is there a reason for it? Instead of trpc.useQuery(path, input, opts)? Is this a relic of when you were supporting multiple inputs?

Add `createSSGHelpers().setQueryData()`

Thank you for bringing this project to life – it has been a joy to use from the very start!

While experimenting with SSG helpers, I realized that the only React Query methods available are fetchQuery and prefetchQuery. The latter could be used with a custom initializer method, but unfortunately, tRPC doesn’t support that approach at this time.

When prefetching more queries during SSR/SSG, executing DB queries as a single transaction to obtain all the data required could outperform parallel queries ran via Promise.all([ssg.fetchData(…), ssg.fetchData(…), …]).

For instance, Prisma’s $transaction API makes it possible to execute the related queries at once, without having to wait for hops between the DB and the app server multiple times. However, the results have to be primed manually into the QueryClient returned by createSSGHelpers. Exposing setQueryData could bring great performance improvements for end-users.

Context type of queries and mutations isn’t inferred when using React Query

When doing updates optimistically, having a context value to roll back failed mutations with is crucial. Unfortunately, the TContext type used within React Query isn’t inferred in tRPC’s React Query client, and so the value of context will always be unknown, e.g. in onError handlers.

Providing a fourth generic type parameter, TContext, to the underlying useQuery and useMutation hooks might help – please see the related code snippet for details:

function _useQuery<
TPath extends keyof TQueries & string,
TProcedure extends TQueries[TPath],
TOutput extends inferProcedureOutput<TProcedure>
>(
pathAndArgs: [path: TPath, ...args: inferHandlerInput<TProcedure>],
opts: UseTRPCQueryOptions<
inferProcedureInput<TQueries[TPath]>,
TError,
TOutput
> = {},
): UseQueryResult<TOutput, TError> {
const cacheKey = getCacheKey(pathAndArgs, CACHE_KEY_QUERY);
const { client, isPrepass } = useContext();
const query = useQuery(
cacheKey,
() => client.query(...pathAndArgs) as any,
{
...opts,
suspense: isPrepass && opts.ssr !== false ? true : opts.suspense,
},
);
return query;
}
function _useMutation<
TPath extends keyof TMutations & string,
TInput extends inferProcedureInput<TMutations[TPath]>,
TOutput extends inferProcedureOutput<TMutations[TPath]>
>(path: TPath, opts?: UseMutationOptions<TOutput, TError, TInput>) {
const client = useContext().client;
const hook = useMutation<TOutput, TError, TInput>(
(input) => (client.mutation as any)(path, input),
opts,
);

Remove authorization

As discussed yesterday - if data validation shouldn't be enforced, auth shouldn't be either

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.