Search...
ctrl/
Light
Dark
System
Sign in

Integrating Gel with tRPC

This guide explains how to integrate Gel with tRPC for a modern, type-safe API. We'll cover setting up database interactions, API routing, and implementing authentication, all while ensuring type safety across the client and server.

You can reference the following repositories for more context:

Gel will serve as the database layer for your application.

To initialize Gel, run the following command using your preferred package manager:

Copy
$ 
pnpm dlx gel project init # or `npx gel project init`

This will create an Gel project and set up a schema to start with.

The previous command generated a schema file in the dbschema directory.

Here's an example schema that defines a User model:

dbschema/default.gel
Copy
module default {
  type User {
    required name: str;
    required email: str;
  }
}

Once schema changes are made, apply migrations with:

Copy
$ 
pnpm dlx gel migration create # or npx gel migration create
Copy
$ 
pnpm dlx gel migration apply # or npx gel migration apply

To interact with Gel from your application, you need to configure the client.

First, install the Gel client using your package manager:

Copy
$ 
pnpm add gel
Copy
$ 
# or yarn add gel
Copy
$ 
# or npm install gel
Copy
$ 
# or bun add gel

Then, create a client instance in a gel.ts file:

src/gel.ts
Copy
import { createClient } from 'gel';

const gelClient = createClient();
export default gelClient;

This client will be used to interact with the database and execute queries.

tRPC enables type-safe communication between the frontend and backend.

Install the required tRPC dependencies:

Copy
$ 
pnpm add @trpc/server @trpc/client
Copy
$ 
# or yarn add @trpc/server @trpc/client
Copy
$ 
# or npm install @trpc/server @trpc/client
Copy
$ 
# or bun add @trpc/server @trpc/client

If you're using React and would like to use React Query with tRPC, also install a wrapper around the @tanstack/react-query.

Copy
$ 
pnpm add @trpc/react-query
Copy
$ 
# or yarn add @trpc/react-query
Copy
$ 
# or npm install @trpc/react-query
Copy
$ 
# or bun add @trpc/react-query

Here's how to define a simple tRPC query that interacts with Gel:

server/routers/_app.ts
Copy
import { initTRPC } from '@trpc/server';
import gelClient from './gel';

const t = initTRPC.create();

export const appRouter = t.router({
  getUsers: t.procedure.query(async () => {
    const users = await gelClient.query('SELECT User { name, email }');
    return users;
  }),
});

export type AppRouter = typeof appRouter;

This example defines a query that fetches user data from Gel, ensuring type safety in both the query and response.

Now that the server is set up, you can use the tRPC client to interact with the API from the frontend. We will demonstrate how to integrate tRPC with Next.js and Express.

If you're working with Next.js, here's how to integrate tRPC:

Inside api/trpc/[trpc].ts, create the following handler to connect tRPC with Next.js:

pages/api/trpc/[trpc].ts
Copy
import { createNextApiHandler } from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers/_app';

export default createNextApiHandler({
  router: appRouter,
});

Next, create a tRPC client to interact with the API:

utils/trpc.ts
Copy
import { createTRPCReact } from "@trpc/react-query";
import { AppRouter } from './routers/_app';

export const api = createTRPCReact<AppRouter>();

You can then use tRPC hooks to query the API from the client:

components/UsersComponent.tsx
Copy
import { trpc } from '../utils/trpc';

const UsersComponent = () => {
  const { data, isLoading } = trpc.getUsers.useQuery();

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      {data?.map(user => (
        <p key={user.email}>{user.name}</p>
      ))}
    </div>
  );
};

export default UsersComponent;

If you're not using Next.js, here's how you can integrate tRPC with Express.

Here's how you can create an Express server and integrate tRPC:

Copy
import express from 'express';
import { appRouter } from './routers/_app';
import * as trpcExpress from '@trpc/server/adapters/express';

const app = express();

app.use(
  '/trpc',
  trpcExpress.createExpressMiddleware({
    router: appRouter,
  })
);

app.listen(4000, () => {
  console.log('Server is running on port 4000');
});

In non-Next.js apps, use the tRPC client to interact with the server:

Copy
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import { AppRouter } from './routers/_app';

const trpc = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:4000/trpc',
    }),
  ],
});

async function fetchUsers() {
  const users = await trpc.getUsers.query();
  console.log(users);
}

In this section, we will cover how to integrate Gel Auth with tRPC and context in both Next.js and Express environments. This will ensure that user authentication is handled securely and that both server-side and client-side tRPC calls can access the user's session.

In Next.js, integrating Gel Auth with tRPC involves creating a context that provides the user session and Gel client to the tRPC API.

  1. Initialize Gel Client and Auth

    First, initialize the Gel client and Gel Auth:

    Copy
    import { createClient } from "gel";
    import createAuth from "@gel/auth-nextjs/app";
    
    // Initialize Gel client
    export const gelClient = createClient();
    
    // Initialize Gel Auth
    export const auth = createAuth(gelClient, {
      baseUrl: process.env.VERCEL_ENV === "production"
        ? "https://production.yourapp.com"
        : "http://localhost:3000",
    });
  2. Create tRPC Context

    The tRPC context provides the Gel Auth session to the tRPC procedures:

    src/trpc.ts
    Copy
    import { initTRPC } from '@trpc/server';
    import { headers } from "next/headers";
    import { auth } from "src/gel.ts";
    
    // Create tRPC context with session and Gel client
    export const createTRPCContext = async () => {
      const session = await auth.getSession(); // Retrieve session from Gel Auth
    
      return {
        session, // Pass the session to the context
      };
    };
    
    // Initialize tRPC with context
    const t = initTRPC.context<typeof createTRPCContext>().create({});
  3. Use tRPC Context in API Handler

    In Next.js, set up an API handler to connect your tRPC router with the context:

    pages/api/trpc/[trpc].ts
    Copy
    import { createNextApiHandler } from '@trpc/server/adapters/next';
    import { createTRPCContext } from 'src/trpc.ts';
    import { appRouter } from 'src/routers/_app';
    
    export default createNextApiHandler({
      router: appRouter, // Your tRPC router
      createContext: createTRPCContext,
    });
  4. Example tRPC Procedure

    You can now write procedures in your tRPC router, making use of the Gel Auth session and the Gel client:

    Copy
    export const appRouter = t.router({
      getUserData: t.procedure.query(async ({ ctx }) => {
        if (!(await ctx.session.isSignedIn())) {
          throw new Error("Not authenticated");
        }
        // Fetch data from Gel using the authenticated client
        const userData = await ctx.session.client.query(`
          select User { name, email }
        `);
    
        return userData;
      }),
    });

In Express, the process involves setting up middleware to manage the authentication and context for tRPC procedures.

  1. Initialize Gel Client and Auth for Express

    Just like in Next.js, you first initialize the Gel client and Gel Auth:

    Copy
    import { createClient } from "gel";
    import createExpressAuth from "@gel/auth-express";
    
    // Initialize Gel client
    const gelClient = createClient();
    
    // Initialize Gel Auth for Express
    export const auth = createExpressAuth(gelClient, {
      baseUrl: `http://localhost:${process.env.PORT || 3000}`,
    });
  2. Create tRPC Context Middleware for Express

    In Express, create middleware to pass the authenticated session and Gel client to the tRPC context:

    Copy
    import { type AuthRequest, type Response, type NextFunction } from "express";
    
    // Middleware to set up tRPC context in Express
    export const createTRPCContextMiddleware = async (
      req: AuthRequest,
      res: Response,
      next: NextFunction
    ) => {
      const session = req.auth?.session(); // Get authenticated session
      req.context = {
        session, // Add session to context
        gelClient, // Add Gel client to context
      };
      next();
    };
  3. Set up tRPC Router in Express

    Use the tRPC router in Express by including the context middleware and Gel Auth middleware:

    Copy
    import express from "express";
    import { appRouter } from "./path-to-router";
    import { auth } from "./path-to-auth";
    import { createTRPCContextMiddleware } from "./path-to-context";
    import { createExpressMiddleware } from "@trpc/server/adapters/express";
    
    const app = express();
    
    // Gel Auth middleware to handle sessions
    app.use(auth.middleware);
    
    // Custom middleware to pass tRPC context
    app.use(createTRPCContextMiddleware);
    
    // tRPC route setup
    app.use(
      "/trpc",
      createExpressMiddleware({
        router: appRouter,
        createContext: (req) => req.context, // Use context from middleware
      })
    );
    
    app.listen(4000, () => {
      console.log('Server running on port 4000');
    });
  4. Example tRPC Procedure in Express

    Once the context is set, you can define tRPC procedures that use both the session and Gel client:

    Copy
    export const appRouter = t.router({
      getUserData: t.procedure.query(async ({ ctx }) => {
        if (!(await ctx.session.isSignedIn())) {
          throw new Error("Not authenticated");
        }
        // Fetch data from Gel using the authenticated client
        const userData = await ctx.session.client.query(`
          select User { name, email }
        `);
    
        return userData;
      }),
    });

By integrating Gel Auth into the tRPC context, you ensure that authenticated sessions are securely passed to API procedures, enabling user authentication and protecting routes.

You can also reference these projects for further examples: