1

I'm creating a simple twitter clone with Next and TRPC, I already got tRPC working, but I would like to move all my code into the src directory. I noticed that just by simply moving everything, TRPC stops working with the following error:

TypeError: Cannot read properties of null (reading 'client')

First, here's my project's file structure, everything is located at the project's root.

.
├── components
├── containers
├── model
├── next.config.js
├── next-env.d.ts
├── package.json
├── pages
├── postcss.config.js
├── prettier.config.js
├── prisma
├── README.md
├── routers
├── tailwind.config.js
├── tsconfig.json
├── utils
└── yarn.lock

7 directories, 9 files

In here, I followed tRPC's set up guide for Next.js which instructed me to perform the following changes.

Create a tRPC router

I created a router at routers/TweetRouter.ts.

export const tweetRouter = createRouter().query("all", {
  resolve() {
    const tweets: Tweet[] = [
      {
        id: 1,
        user: "Je12emy",
        text: "Hello world",
        likes: 2,
      },
      {
        id: 2,
        user: "Je12emy",
        text: "TRPC + Nextjs + Tailwind = T3 App",
        likes: 0,
      },
    ];
    return tweets;
  },
});

Which is then merged in pages/api/trpc/[trpc].ts

import * as trpc from "@trpc/server";
import * as trpcNext from "@trpc/server/adapters/next";
import { tweetRouter } from "../../../routers/tweetRouter";

export function createRouter() {
  return trpc.router();
}
const router = createRouter().merge('tweet.', tweetRouter);

export const appRouter = router;
export type AppRouter = typeof router;

export default trpcNext.createNextApiHandler({
  router,
  onError({ error }) {
    if (error.code === "INTERNAL_SERVER_ERROR") {
      // send to bug reporting
      console.error("Something went wrong", error);
    }
  },
});

Create tRPC hooks

This should be done at utils/trpc.ts

import { createReactQueryHooks } from '@trpc/react';
import type { AppRouter } from '../pages/api/trpc/[trpc]';

export const trpc = createReactQueryHooks<AppRouter>();

Configure _app.tsx

I just copied over the code in the documentation, I would like to remove the getURL function down the line.

function MyApp({ Component, pageProps }: AppProps) {
  const queryClient = new QueryClient();
  return (
    <QueryClientProvider client={queryClient}>
      <Component {...pageProps} />{" "}
    </QueryClientProvider>
  );
}

function getBaseUrl() {
  if (typeof window !== 'undefined') {
    return '';
  }
  // reference for vercel.com
  if (process.env.VERCEL_URL) {
    return `https://${process.env.VERCEL_URL}`;
  }

  // reference for render.com
  if (process.env.RENDER_INTERNAL_HOSTNAME) {
    return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
  }

  // assume localhost
  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export default withTRPC<AppRouter>({
  config({ ctx }) {
    /**
     * If you want to use SSR, you need to use the server's full URL
     * @link https://trpc.io/docs/ssr
     */
    return {
      url: `${getBaseUrl()}/api/trpc`,
      /**
       * @link https://react-query-v3.tanstack.com/reference/QueryClient
       */
      // queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
    };
  },
  /**
   * @link https://trpc.io/docs/ssr
   */
  ssr: true,
})(MyApp);

Make API Requests

Finally, I can import the client and send API requests, this works as expected

import type { NextPage } from "next";
import TweetCard from "../components/TweetCard";
import PageContainer from "../containers/Page";
import { trpc } from "../utils/trpc";

const Home: NextPage = () => {
  const { data, isLoading } = trpc.useQuery(["tweet.all"]);

  if (isLoading) {
    return (
      <PageContainer className="flex items-center justify-center">
        <p> loading... </p>
      </PageContainer>
    );
  }

  if (!data) {
    return (
      <PageContainer className="flex items-center justify-center">
        <p> No Tweets </p>
      </PageContainer>
    );
  }

  return (
    <PageContainer className="flex flex-col space-y-3">
      {data.map((tweet) => (
        <TweetCard key={tweet.id} {...tweet} />
      ))}
    </PageContainer>
  );
};

export default Home;

And so, I decided to commit this code and to move it into a src directory just to clean up my project's root. Here were the problem materializes at src/pages/index.ts at line 7, which is where the query is being done. I updated my file structure a bit, and fixed all the imports, so here's my current file structure.

src
├── backend
│   ├── model
│   │   ├── tweet.ts
│   │   └── user.ts
│   └── router
│       ├── index.ts
│       └── tweet.ts
├── components
│   ├── LikesCounter.tsx
│   └── TweetCard.tsx
├── containers
│   └── Page.tsx
├── pages
│   ├── api
│   │   └── trpc
│   │       └── [trpc].ts
│   ├── _app.tsx
│   └── index.tsx
├── styles
│   └── globals.css
└── utils
    └── trpc.ts

10 directories, 14 files

You can check the complete code in my repository: https://github.com/Je12emy/twitter-nextjs/tree/trpc_port

Jeremy
  • 1,447
  • 20
  • 40

1 Answers1

0

Had a similar problem but it turned out we were missing the apihandler 'page' in the new src folder. It doesn't look like that's the case for your repo though.

jcho
  • 21
  • *It doesn't look like that's the case for your repo though* -- So, why post it as an answer? – Gert Arnold Dec 09 '22 at 19:35
  • 3
    So, if someone has the same issue as me and they find this thread they can check if they made the same mistake as me… – jcho Dec 10 '22 at 20:10