trpc icon indicating copy to clipboard operation
trpc copied to clipboard

Enable SSR per page (NextJS)

Open carlosbaraza opened this issue 2 years ago • 10 comments

Instead of having a global setup to enable SSR with withTRPC wrapping _app, we need to use it in a per page basis because our app is really big, and some parts need to be static and we don't host in Vercel but in a K8s environment.

I spent the few hours trying to understand why withTRPC would not work wrapping a page. It seems like somehow the query is not being sent after the first prepass.

carlosbaraza avatar Jul 18 '22 16:07 carlosbaraza

Could be added, but nextjs is deprecating getInitialProps, harder so with the new router they're planning. Happy to take PRs that enables this on the next branch though.

KATT avatar Jul 18 '22 22:07 KATT

If you're planning on adding the feature, please make an RFC in discussions and/or message in the contributors channel on discord to get help! 🙏

KATT avatar Jul 18 '22 23:07 KATT

Thanks for your idea. I agree this is good but not something I personally will spend time on, will actually leave it open for a bit if someone wants to take it on.

KATT avatar Jul 18 '22 23:07 KATT

@KATT Hello Alex, just spent some time implementing my own version of withTRPC that supports SSR per page. Given that there are multiple upcoming changes in both NextJS and TRPC v10, I didn't implement it with a PR into the main library.

However, please look at this CodeSandbox or the repo carlosbaraza/trpc-ssr-config-per-page which contains the simple TRPC example with my custom implementation to support SSR configured per page.

Let me know if you think the pattern of manually adding a TRPCProvider in the _app is a good one. We also have Apollo configured for SSR in our application, so we need to manually add the Provider in the right part of the tree, because there are multiple providers and wrappers in the _app, so wrapping everything with a HOC would not cut it.

carlosbaraza avatar Jul 22 '22 09:07 carlosbaraza

That's super impressive! I'll def get back to you on this :)

KATT avatar Jul 22 '22 13:07 KATT

That's really cool, it's easier to understand what's going on that way. And it's very normal to have BlahProviders in _app.tsx. Our current next.js app has six of them so far. It might unlock some other possibilities like more easily passing data to the TRPCProvider from other react hooks, etc.

Given getInitialProps is "legacy" and may not be available at all in Layouts, I wonder if it could go a step further and put the responsibility in the user's hands, while providing useful helpers. Maybe something like:

const Page = () => {
  const query = trpc.useQuery(['foo'])
  return <>{query.data?.greeting || 'no data'}</>
}

export default Page

export const getServerSideProps = trpc.serverSidePropsPrepass(Page)

or, if people prefer to stick with getInitialProps while they can:

const Page = () => {
  const query = trpc.useQuery(['foo'])
  return <>{query.data?.greeting || 'no data'}</>
}

export default Page

Object.assign(Page, { getInitialProps: trpc.initialPropsPrepass(Page) })

(haven't dug into the implementation, so not sure if serverSidePropsPrepass and intialPropsPrepass would even need to be two separate functions)

mmkal avatar Jul 22 '22 18:07 mmkal

@mmkal that serverSidePropsPrepass is an interesting approach. I found it quite frustrating that the recommended way in Apollo now is to prefetch the queries in the getServerSideProps function. We like fetching data at the component level, because it keeps units of logic independent from each other, and it makes it really hard to know what would be the queries in a particular page. To SSR, this requires traversing the whole page tree to find the particular queries that would be made. It has performance implications, but the DX is much better, so it is a tradeoff we are willing to tolerate.

Do you have any idea if implementing serverSidePropsPrepass would be possible?

carlosbaraza avatar Jul 23 '22 12:07 carlosbaraza

Was about to open a similar issue, getInitialProps in _app goes against Next.js best practices and defaults, this should really explicitly be decided on a page-by-page basis using the standard getServerSideProps / getStaticPaths / getStaticProps fns. Looking into the preliminary _withTRPC implementation, I wonder if it makes more sense to put the double rendering/prefetching logic into _document like Apollo does with getDataFromTree (usage example). Especially with v10 approaching here's a virtual thumbs up from me to anyone who manages to integrate this on time. 👍 (I'm definitely having my eyes on it, but can't commit to anything right now)

3x071c avatar Jul 23 '22 13:07 3x071c

I don't think Next.js is going to / can deprecate per-page getInitialProps. Even if they don't include that exact method in the upcoming layouts proposal, they MUST include a way to prefetch data and then do SSR on the server only.

Maybe something like this: https://github.com/vercel/next.js/discussions/29117

Other frameworks like Sveltekit are making ways to do this, and it's absolutely necessary to have the best user experience (server rendering + client route transitions that can optionally prefetch data before the route changes).

At my company, we are going to use getInitialProps per page as it's the best option for now.

mgreenw avatar Aug 18 '22 14:08 mgreenw

If someone wants to make withTRPC ssr (or similar) work on a per-page basis, I'd happily merge it.

I'd want it to have and example project running through e2e testing using Playwright with javascript disabled to show that the data fetching works on the server.

KATT avatar Aug 18 '22 15:08 KATT

App layouts is too close, we won't spend time on it but if someone feels inclined to have a stab at it we'll happily merge it

KATT avatar Nov 27 '22 16:11 KATT