← Back to blog

Astro + tRPC v10

Elliot Hesp

CEO

27th November, 2022

Need a custom tool? Invertase can help

Tired of investing in off-the-shelf software that doesn't quite fit your business? Invertase can build solutions tailored to your exact needs. Tell us what you're looking for here and we'll be in touch.

With the recent release of tRPC v10, the simplified API provides a few out of the box adapters for Express, Lambdas and the Fetch API.

In previous versions of tRPC, you may have had to use 3rd party libraries such as astro-trpc which wraps internal tRPC APIs to handle an Astro API Endpoint. Fortunately for us, Astro uses standard web-fundamental Fetch APIs such as Request & Response which tRPC v10 provides an adapter for!

Let’s get started.

Firstly, install the server and client tRPC packages:

npm install @trpc/client @trpc/server --save

Next you’ll need to create an API Router in Astro (make sure you’re using an SSR adapter!). Although not required, the usual convention is to create a file at /pages/api/trpc/[trpc].ts, which will be handling all tRPC requests:

// /pages/api/trpc/[trpc].ts

import { initTRPC, TRPCError } from '@trpc/server';
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';

const t = initTRPC.create();

const router = t.router({ 
  // Add your procedures 
});

// The Astro API route, handling all incoming HTTP requests.
export const all: APIRoute = ({ request }) => {
  return fetchRequestHandler({
    req: request,
    endpoint: '/api/trpc',
    router,
    },
  });
};

export type AppRouter = typeof router;

And that’s it! Using the tRPC fetch adapter, we simply return the handler with the Request instance from Astro.

On the client, create a proxy client to point to your endpoint:

// src/trpc.ts

import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';

export default createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3000/api/trpc',
    }),
  ],
});

You can now call your procedures by importing the proxy client:

import trpc from '../trpc';

const result = await trpc.your.routes.query();

Adding context

You’ll probably want to add context to your procedures, which fortunately is really simple. Let’s say we have a cookie containing a session token, first we update the client proxy to pass it along to our API endpoint as a header (or whatever you want):

// src/trpc.ts

import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import cookies from 'js-cookie';
import type { AppRouter } from 'src/pages/api/trpc/[trpc]';

export default createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:3000/api/trpc',
      headers() {
        return {
          'X-Session': cookies.get('session'),
        };
      },
    }),
  ],
});

Now back on the API Route, grab the header and add it as context:

type Context = { session?: string; };

const t = initTRPC.context<Context>().create();
const router = t.router({ });

export const all: APIRoute = ({ request }) => {
  return fetchRequestHandler({
    req: request,
    endpoint: '/api/trpc',
    router,
    createContext: async ({ req }) => {
      return { session: req.headers.get('X-Session') ?? undefined };
    },
  });
};

You can now access your session directly in the procedures!

Server calls

In some scenarios, you might want to call your procedures from the server rather than the client. This is also really simple; we can simply create a “caller” which we can trigger in our Astro “front matter”.

Back in the API Endpoint, add a new export like so:

// /pages/api/trpc/[trpc].ts

const t = initTRPC.context<Context>().create();
const router = t.router({ });

export function caller(request: Request) {
  const { session } = cookie.parse(request.headers.get('cookie') || '');
  return router.createCaller({ session });
}

This ‘caller’ function can be imported into our Astro files and accepts a Request instance directly, for example:

---
import { caller } from 'src/pages/api/trpc/[trpc]';

const user = await caller(Astro.request).user.query();
---

<div>Hello {user.name}!</div>

Stay tuned for more updates and exciting news that we will share in the future. Follow us on Invertase TwitterLinkedin, and Youtube, and subscribe to our monthly newsletter to stay up-to-date. You may also join our Discord to have an instant conversation with us.

Elliot Hesp

CEO

Categories

Web

Tags