Integrating Sentry with the t3 stack (v7 edition) #8500
Replies: 6 comments 37 replies
-
@AbhiPrasad I followed your instructions, but unfortunately my errors still aren't getting automatically intercepted form TRPC. Do you have any recommendations? NOTE: I replaced sensitive values. Also, errors from my frontend are properly getting intercepted // sentry.server.config.ts
// This file configures the initialization of Sentry on the server.
// The config you add here will be used whenever the server handles a request.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
import { prisma } from "~/server/db";
Sentry.init({
dsn: "fake dsn",
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1,
integrations: [
new Sentry.Integrations.Prisma({
client: prisma,
}),
],
// Setting this option to true will print useful information to the console while you're setting up Sentry.
debug: false,
}); // auth.ts
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { Community, Role } from "@prisma/client";
import { type GetServerSidePropsContext } from "next";
import {
getServerSession,
type DefaultSession,
type NextAuthOptions,
DefaultUser,
} from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { env } from "~/env.mjs";
import { prisma } from "~/server/db";
import * as Sentry from "@sentry/nextjs";
/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
* object and keep type safety.
*
* @see https://next-auth.js.org/getting-started/typescript#module-augmentation
*/
declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
role: Role;
isManager: boolean;
isHost: boolean;
} & DefaultSession["user"];
}
interface User extends DefaultUser {
role: Role;
isManager: boolean;
isHost: boolean;
}
}
/**
* Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
*
* @see https://next-auth.js.org/configuration/options
*/
export const authOptions: NextAuthOptions = {
callbacks: {
session: async ({ session, user }) => {
let userDetails = await prisma.user.findUnique({
where: {
id: user.id,
},
include: {
managedCommunities: true,
hostedEvents: true,
Event: true,
},
});
return {
...session,
user: {
...session.user,
id: user.id,
role: user.role,
isManager: !!userDetails?.managedCommunities.length,
isHost:
!!userDetails?.hostedEvents.length || !!userDetails?.Event.length,
},
};
},
},
events: {
signIn({ user }) {
Sentry.setUser({ id: user.id });
},
},
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
/**
* ...add more providers here.
*
* Most other providers require a bit more work than the Discord provider. For example, the
* GitHub provider requires you to add the `refresh_token_expires_in` field to the Account
* model. Refer to the NextAuth.js docs for the provider you want to use. Example:
*
* @see https://next-auth.js.org/providers/github
*/
],
};
/**
* Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
*
* @see https://next-auth.js.org/configuration/nextjs
*/
export const getServerAuthSession = (ctx: {
req: GetServerSidePropsContext["req"];
res: GetServerSidePropsContext["res"];
}) => {
return getServerSession(ctx.req, ctx.res, authOptions);
}; // trpc.ts
/**
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
* 1. You want to modify request context (see Part 1).
* 2. You want to create a new middleware or type of procedure (see Part 3).
*
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
* need to use are documented accordingly near the end.
*/
import * as Sentry from "@sentry/nextjs";
import { initTRPC, TRPCError } from "@trpc/server";
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
import { type Session } from "next-auth";
import superjson from "superjson";
import { ZodError } from "zod";
import { getServerAuthSession } from "~/server/auth";
import { prisma } from "~/server/db";
import { s3 } from "../aws/s3";
/**
* 1. CONTEXT
*
* This section defines the "contexts" that are available in the backend API.
*
* These allow you to access things when processing a request, like the database, the session, etc.
*/
/**
* This helper generates the "internals" for a tRPC context. If you need to use it, you can export
* it from here.
*
* Examples of things you may need it for:
* - testing, so we don't have to mock Next.js' req/res
* - tRPC's `createSSGHelpers`, where we don't have req/res
*
* @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
*/
const createInnerTRPCContext = (opts: CreateContextOptions) => {
return {
session: opts.session,
prisma,
s3,
};
};
/**
* This is the actual context you will use in your router. It will be used to process every request
* that goes through your tRPC endpoint.
*
* @see https://trpc.io/docs/context
*/
export const createTRPCContext = async (opts: CreateNextContextOptions) => {
const { req, res } = opts;
// Get the session from the server using the getServerSession wrapper function
const session = await getServerAuthSession({ req, res });
return createInnerTRPCContext({
session,
});
};
/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/
const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
/**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
*
* These are the pieces you use to build your tRPC API. You should import these a lot in the
* "/src/server/api/routers" directory.
*/
/**
* This is how you create new routers and sub-routers in your tRPC API.
*
* @see https://trpc.io/docs/router
*/
export const createTRPCRouter = t.router;
/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;
/** Reusable middleware that enforces users are logged in before running the procedure. */
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
session: { ...ctx.session, user: ctx.session.user },
},
});
});
type CreateContextOptions = {
session: Session | null;
};
const sentryMiddleware = t.middleware(
Sentry.Handlers.trpcMiddleware({
attachRpcInput: true,
})
);
const finalMiddleware = sentryMiddleware.unstable_pipe(enforceUserIsAuthed);
/**
* Protected (authenticated) procedure
*
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
* the session is valid and guarantees `ctx.session.user` is not null.
*
* @see https://trpc.io/docs/procedures
*/
export const protectedProcedure = t.procedure.use(finalMiddleware); |
Beta Was this translation helpful? Give feedback.
-
Hey @AbhiPrasad thank you for this nice guide, and (i assume) for the docs section on trpc :) I've stumbled upon a problem while trying this on my T3 project. In your guide, you've used a Sentry import from
On the other hand, the aforementioned docs import it from Do you have any advice for how to handle this please? :) |
Beta Was this translation helpful? Give feedback.
-
Hey @AbhiPrasad , excellent guide! I was trying to integrate Sentry with next TRPc, which is v11(it's a default in create t3 app now) and I think some types got incompatible in the meantime.
do you have an idea of a quick solution to this? obv something that will not require the changes in SDK. thanks a lot for the great effort writing this & answering! |
Beta Was this translation helpful? Give feedback.
-
Hey folks - with the release of |
Beta Was this translation helpful? Give feedback.
-
@AbhiPrasad Maybe Update the prisma setup guide to match the latest documentation, https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/ |
Beta Was this translation helpful? Give feedback.
-
Hi, due to popular demand we have released an updated version of this guide for version 8 of the Next.js SDK: #13103 |
Beta Was this translation helpful? Give feedback.
-
This guide has an updated version
Important
Due to popular demand we have released an updated guide for version 8 of the Sentry Next.js SDK.
You can find the updated guide here.
This is a small guide on how to integrate Sentry with https://create.t3.gg/. A code repo of the guide can be found here: https://github.com/AbhiPrasad/sentry-t3-app
Was initially requested here.
Initial Setup
First, create your test app with
npm create t3-app@latest
.Next, follow our Next.js SDK setup guide by running
npx @sentry/wizard@latest -i nextjs
. This will configure and install our Next.js SDK with the appropriate Sentry project.You can test this out by running
npm run dev
and checking out http://localhost:3000/sentry-example-pageNow depending on what you've added, use our optional integrations/setup.
Optional setup
Prisma
If you've configured prisma, import the prisma client from
~/server/db
and add the Sentry Prisma integration to yoursentry.server.config.ts
file.nextAuth
If you've set up
nextAuth
, you can configure it's events to attach information to Sentry. For example below I've updatedsrc/server/auth.ts
to set the user on sign in. You can also create Sentry breadcrumbs on the different events emitted.trpc
If you're using tRPC, you can set up Sentry's tRPC integration by updating
src/server/api/trpc.ts
to extend the trpc middleware.Beta Was this translation helpful? Give feedback.
All reactions