@valian/node-sentry
v0.0.1
Published
A comprehensive Sentry integration library for Firebase Functions (v1 and v2) that provides automatic error tracking, context enrichment, and performance monitoring.
Readme
@valian/node-sentry
A comprehensive Sentry integration library for Firebase Functions (v1 and v2) that provides automatic error tracking, context enrichment, and performance monitoring.
Features
- 🔧 Easy Sentry initialization with Firebase-specific integrations
- 📦 Wrapper functions for all Firebase Function types (v1 and v2)
- 🎯 Automatic error capture with unhandled exception tracking
- 📊 Performance monitoring with distributed tracing
- 🏷️ Automatic context enrichment (function name, version, event data)
- 🔗 Support for error cause chains with custom Sentry context
- ⚡ Helper utilities for promise handling
Installation
npm install @valian/node-sentry @sentry/node @sentry/coreor
pnpm add @valian/node-sentry @sentry/node @sentry/coreQuick Start
1. Initialize Sentry
Create an initialization file (e.g., sentry.ts):
import { initSentry } from '@valian/node-sentry/init'
initSentry({
dsn: process.env.SENTRY_DSN,
environment: process.env.ENVIRONMENT || 'development',
tracesSampleRate: 1.0,
})Import this file at the top of your functions entry point before defining any functions.
2. Wrap Your Firebase Functions
Firebase Functions v2
Firestore Trigger
import { onDocumentWritten } from 'firebase-functions/v2/firestore'
import { sentryWrapOnDocumentChange } from '@valian/node-sentry'
export const onUserUpdate = onDocumentWritten(
'users/{userId}',
sentryWrapOnDocumentChange({ name: 'onUserUpdate' }, async (event) => {
// Your function logic here
const before = event.data?.before.data()
const after = event.data?.after.data()
// ...
})
)PubSub Trigger
import { onMessagePublished } from 'firebase-functions/v2/pubsub'
import { sentryWrapOnMessagePublished } from '@valian/node-sentry'
export const onTaskCreated = onMessagePublished(
'task-created',
sentryWrapOnMessagePublished({ name: 'onTaskCreated' }, async (event) => {
const messageData = event.data.message.json
// Your function logic here
})
)Scheduled Function
import { onSchedule } from 'firebase-functions/v2/scheduler'
import { sentryWrapOnSchedule } from '@valian/node-sentry'
export const dailyCleanup = onSchedule(
'every 24 hours',
sentryWrapOnSchedule({ name: 'dailyCleanup' }, async (event) => {
// Your cleanup logic here
})
)Firebase Functions v1
Firestore Trigger
import * as functions from 'firebase-functions/v1'
import { sentryOnWriteV1Wrapper } from '@valian/node-sentry'
export const onUserUpdateV1 = functions.firestore.document('users/{userId}').onWrite(
sentryOnWriteV1Wrapper({ name: 'onUserUpdateV1' }, async (change, context) => {
// Your function logic here
})
)PubSub Trigger
import * as functions from 'firebase-functions/v1'
import { sentryOnPublishV1Wrapper } from '@valian/node-sentry'
export const onTaskCreatedV1 = functions.pubsub.topic('task-created').onPublish(
sentryOnPublishV1Wrapper({ name: 'onTaskCreatedV1' }, async (message, context) => {
// Your function logic here
})
)Auth Trigger
import * as functions from 'firebase-functions/v1'
import { sentryOnUserChangeV1Wrapper } from '@valian/node-sentry'
export const onUserCreate = functions.auth.user().onCreate(
sentryOnUserChangeV1Wrapper({ name: 'onUserCreate' }, async (user, context) => {
// Your function logic here
})
)Scheduled Function
import * as functions from 'firebase-functions/v1'
import { sentryOnScheduleRunV1Wrapper } from '@valian/node-sentry'
export const dailyCleanupV1 = functions.pubsub.schedule('every 24 hours').onRun(
sentryOnScheduleRunV1Wrapper({ name: 'dailyCleanupV1' }, async (context) => {
// Your cleanup logic here
})
)Advanced Usage
Custom Error with Sentry Context
Use ErrorWithSentryCaptureContext to throw errors with additional Sentry context that will be automatically captured:
import { ErrorWithSentryCaptureContext } from '@valian/node-sentry'
throw new ErrorWithSentryCaptureContext(
'Failed to process payment',
{
extra: {
paymentId: 'payment_123',
amount: 100,
},
tags: {
payment_type: 'subscription',
},
fingerprint: ['payment-processing-error'],
},
{ cause: originalError }
)Handle Unawaited Promises
For fire-and-forget promises that you don't want to await, use handleNotAwaitedPromise to ensure errors are captured:
import { handleNotAwaitedPromise } from '@valian/node-sentry'
// Instead of:
// someAsyncTask().catch(console.error)
// Use:
handleNotAwaitedPromise(someAsyncTask(), (scope) => {
scope.setTag('task_type', 'background')
})Custom Configuration Wrapper
For custom function types or more control, use sentryConfigurationWrapper:
import { sentryConfigurationWrapper } from '@valian/node-sentry'
export const myCustomFunction = async (request, response) => {
return sentryConfigurationWrapper(
{ name: 'myCustomFunction', op: 'http.server' },
(scope) => {
scope.setTag('function.type', 'custom')
scope.setContext('request', {
method: request.method,
url: request.url,
})
},
async () => {
// Your function logic here
return { success: true }
}
)
}Mark Events as Unhandled
To explicitly mark caught exceptions as unhandled in Sentry:
import { captureException } from '@sentry/node'
import { markEventUnhandled } from '@valian/node-sentry'
try {
await riskyOperation()
} catch (error) {
captureException(error, (scope) => {
markEventUnhandled(scope)
scope.setTag('operation', 'risky')
return scope
})
// Re-throw or handle as needed
}What Gets Captured Automatically
The wrapper functions automatically capture and send to Sentry:
- Unhandled exceptions - All errors thrown from your function handlers
- Function metadata - Function name, version (v1 or v2)
- Event context - Event ID, type, timestamp, source
- Firestore data - Document paths, IDs, before/after snapshots
- PubSub messages - Message data
- User information - User ID for auth triggers
- Performance traces - Distributed tracing for all operations
Environment Variables
SENTRY_DSN=your-sentry-dsn
ENVIRONMENT=production
NODE_ENV=productionAPI Reference
initSentry(options)
Initialize Sentry with Firebase-specific integrations.
- Parameters:
options(Omit<NodeOptions, 'beforeSend'>): Sentry Node.js options
- Returns: void
Wrapper Functions
All wrapper functions follow the same pattern:
wrapperFunction({ name: 'functionName' }, async (event, context?) => {
// Your handler
})V2 Wrappers
sentryWrapOnDocumentChange- Firestore document changessentryWrapOnMessagePublished- PubSub messagessentryWrapOnSchedule- Scheduled functions
V1 Wrappers
sentryOnWriteV1Wrapper- Firestore writessentryOnPublishV1Wrapper- PubSub publishessentryOnUserChangeV1Wrapper- Auth user changessentryOnScheduleRunV1Wrapper- Scheduled runs
ErrorWithSentryCaptureContext
Custom error class for attaching Sentry capture context.
new ErrorWithSentryCaptureContext(
message: string,
captureContext: ErrorCaptureContext,
options?: ErrorOptions
)handleNotAwaitedPromise(promise, hint?)
Handle promises that aren't awaited and capture any errors.
- Parameters:
promise(Promise<T> | undefined): The promise to handlehint(optional): Sentry capture hint for additional context
sentryConfigurationWrapper(context, configure, work)
Generic wrapper for custom Sentry configuration.
- Parameters:
context(StartSpanOptions): Span configurationconfigure((scope: Scope) => void): Scope configuration functionwork(() => Promise<R>): The work to execute
Best Practices
- Initialize early - Import and call
initSentry()before defining any functions - Name your functions - Always provide a descriptive
namein wrapper options - Use appropriate wrappers - Use v2 wrappers for v2 functions, v1 for v1
- Handle background tasks - Use
handleNotAwaitedPromisefor fire-and-forget operations - Add custom context - Use
ErrorWithSentryCaptureContextfor domain-specific error information - Set environment - Configure different sample rates and environments for dev/prod
License
MIT
Development
Run unit tests:
nx test sentryBuild the library:
nx build sentry