This entire post is written by @pi0neerpat - all credit goes to him. I’m reposting in a separate thread so that it is more easily searchable. Original post here: Using GraphQL Envelop+Helix in Redwood v0.35+ - #28 by dthyresson
As of v0.37, Redwood made the switch to using The Guild’s Envelop. As a result setting up plugins has become substantially easier! This is now the defacto way of setting up Sentry, and likely the easiest one too!
Docs for using Sentry.io with redwood
Install the required libraries:
yarn add @envelop/sentry @sentry/node @sentry/tracing
Create a reusable Sentry file api/src/lib/sentry
import * as Sentry from '@sentry/node'
import * as Tracing from '@sentry/tracing'
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 0.5,
})
export default Sentry
Then import it into your graphql function api/src/functions/graphql
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
import { useSentry } from '@envelop/sentry'
import Sentry from 'src/lib/sentry'
import { getCurrentUser } from 'src/lib/auth'
import { logger } from 'src/lib/logger'
import directives from 'src/directives/**/*.{js,ts}'
import sdls from 'src/graphql/**/*.sdl.{js,ts}'
import services from 'src/services/**/*.{js,ts}'
import { db } from 'src/lib/db'
const extraPlugins = [
useSentry({
includeRawResult: false, // set to `true` in order to include the execution result in the metadata collected
includeResolverArgs: false, // set to `true` in order to include the args passed to resolvers
includeExecuteVariables: false, // set to `true` in order to include the operation variables values
// appendTags: (args) => { return { ... }} // if you wish to add custom "tags" to the Sentry transaction created per operation
}),
]
export const handler = createGraphQLHandler({
getCurrentUser,
loggerConfig: {
logger,
options: { tracing: true, operationName: true },
},
directives,
sdls,
services,
extraPlugins,
onException: () => {
db.$disconnect()
},
})
For a custom function, such as api/src/functions/checkout
import Sentry from 'src/lib/sentry'
import { processOrder } from 'src/lib/wyre/order'
export const handler = async (event) => {
// Default response
let statusCode = 200
let message = ''
try {
let body = event.body
if (typeof body === 'string') body = JSON.parse(body)
await processOrder({ orderId: body.orderId, referenceId: body.referenceId })
return {
statusCode,
body: {
message: 'Success!',
},
}
} catch (e) {
console.log(event)
Sentry.captureException(e)
return {
statusCode, // Always return 200
body: {
message: 'Internal server error',
},
}
}
}
Tada! Please share your experience if you use Sentry or other plugins
PS extra goodness in getCurrentUser
//api/src/lib/auth.js
import Sentry from 'src/lib/sentry'
export const getCurrentUser = async (session) => {
const member = await db.member.findUnique({ where: { id: session.id } })
if (!member) return null
// WARNING: Returned values here are exposed to the FE
Sentry.setUser({ username: member.username, id: member.id })