Next.js + TRPC + Prisma

Client Setup

  1. Make sure you've installed the library first!

  2. Create a file to configure your data models - put this in src/datamodels.tsx:

import { useCallback, useMemo } from 'react';
import { useRouter } from 'next/router';

import {
  DataModels,
  AdminContextProvider,
  queryParameterStateCache,
  RemoteDataModels,
} from '@/admin';
import {
  useGenerateRemoteDataModelsTRPCClient,
} from "@/admin/remote-data-model-adapters/protocols/trpc";

import { api } from "@/utils/api";

export default function AllDataModels({ children }: { children: React.ReactNode}) {
  // 1. Set up a "remote data model client" that communicates with the server over trpc.
  const { fetchRemoteDataModels } = useGenerateRemoteDataModelsTRPCClient(
    api,

    // NOTE: for older versions of trpc, this should be `api.useContext()`
    api.useUtils(),

    // NOTE: This is a path to the place the corresponding trpc router is mounted.
    // This default value matches with what will be configured later in this guide.
    // `api.foo.bar.baz.(mounted router)` would be represented as `['foo', 'bar', 'baz']`
    ['admin'],
  );

  return (
    <div style={{ padding: 8 }}>
      <AdminContextProvider stateCache={queryParameterStateCache} nextRouter={useRouter()}>
        {/* 2. Give the data models provider the way to get remote data models */}
        <DataModels fetchRemoteDataModels={fetchRemoteDataModels}>
          {/* 3. Include all remote data models in the admin interface */}
          <RemoteDataModels />

          {children}
        </DataModels>
      </AdminContextProvider>
    </div>
  );
}
  1. Create a new directory src/pages/admin and put a file named [...path].tsx inside containing:

import { useRouter } from 'next/router';
import { ListDetailRenderer } from '@/admin';
import AllDataModels from '@/datamodels';

// If no other route matches, this component will render a default / "fallback" list or detail page
// To customize an individual list or detail page, define a custom implementation for it:
// - https://bread-1.gitbook.io/react-admin-contraption/pages/list
// - https://bread-1.gitbook.io/react-admin-contraption/pages/detail
export default function Page() {
  const router = useRouter();
  const path = router.query.path ? router.query.path as Array<string> : null;
  if (!path) {
    return null;
  }

  return (
    <AllDataModels>
      <div style={{ padding: 8 }}>
        <ListDetailRenderer
          basePath="/admin"
          name={path[0]}
          view={path.length > 1 ? 'detail' : 'list'}
          itemKey={path[1] === 'new' ? undefined : path[1]}
        />
      </div>
    </AllDataModels>
  );
}
  1. Create a new file index.tsx inside src/pages/admin containing:

import { Launcher } from "@/admin";
import AllDataModels from "@/datamodels";

export default function Page() {
  return (
    <AllDataModels>
      <Launcher />
    </AllDataModels>
  );
}
  1. Wherever you add global css files, make sure to include @/admin/styles.css:

import "@admin/styles.css";
// import "../../node_modules/react-admin-contraption-unstable/styles.css";

Server Setup

  1. Add the admin router into server/api/root.ts:

import prisma from "@prisma/client";

import { generateRemoteDataModelsTRPCRouter } from "@/admin/remote-data-model-adapters/protocols/trpc";
import PrismaRemoteDataModelProvider from "@/admin/remote-data-model-adapters/datastores/prisma";

import { createTRPCRouter, publicProcedure } from "@/server/api/trpc";

export const appRouter = createTRPCRouter({
  /* NOTE: preexisting trpc routers go here! */

  // This custom router backs the admin interface and allows CRUD interactions
  // to be performed on all database tables.
  admin: createTRPCRouter(
    generateRemoteDataModelsTRPCRouter(
      // NOTE: this default value is not handling auth currently, which means any user can make
      // arbitrary changes to your database! THIS IS INSECURE! DO NOT DEPLOY THIS INTO PRODUCTION!
      publicProcedure,
      // To handle auth, do something like this:
      // publicProcedure.use(({ next, ctx }) => {
      //   // Check `ctx` here, and throw a `TRPCError` if the user shouldn't be given access.
      // }),

      // Connect over this interface to prisma!
      // Note that if you were using a different ORM tool, this is where you'd plug that in!
      PrismaRemoteDataModelProvider(prisma),
    ),
  ),
});

// export type definition of API
export type AppRouter = typeof appRouter;

Try it out!

Start your app, and go to /admin. You should see the admin interface.

Clicking on a model should go to the list view for it, showing a column per model field.

Clicking on a row should go to the detail page, where the model can be viewed, updated, or deleted.

Last updated