Skip to content

Latest commit

 

History

History
293 lines (230 loc) · 5.68 KB

File metadata and controls

293 lines (230 loc) · 5.68 KB

JavaScript/TypeScript Integration

Complete setup for Node.js, React, Next.js, and browser applications.

Package Installation

# All packages
npm install @honeybadger-io/js @sentry/node @devcycle/nodejs-server-sdk appwrite

# React/Browser
npm install @sentry/react @devcycle/react-client-sdk

# Next.js
npm install @sentry/nextjs

Node.js Setup

services.js

import Honeybadger from '@honeybadger-io/js';
import * as Sentry from '@sentry/node';
import { initializeDevCycle } from '@devcycle/nodejs-server-sdk';
import { Client, Account, Databases } from 'node-appwrite';

// Initialize all services
export async function initServices() {
  // Sentry
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment: process.env.NODE_ENV,
    tracesSampleRate: 0.1,
  });

  // Honeybadger
  Honeybadger.configure({
    apiKey: process.env.HONEYBADGER_API_KEY,
    environment: process.env.NODE_ENV,
  });

  // DevCycle
  const devcycle = await initializeDevCycle(
    process.env.DEVCYCLE_SERVER_SDK_KEY
  );

  // Appwrite
  const appwrite = new Client()
    .setEndpoint(process.env.APPWRITE_ENDPOINT)
    .setProject(process.env.APPWRITE_PROJECT_ID)
    .setKey(process.env.APPWRITE_API_KEY);

  return {
    sentry: Sentry,
    honeybadger: Honeybadger,
    devcycle,
    appwrite: {
      client: appwrite,
      account: new Account(appwrite),
      databases: new Databases(appwrite),
    },
  };
}

Express.js Integration

import express from 'express';
import * as Sentry from '@sentry/node';

const app = express();

// Sentry request handler (before routes)
app.use(Sentry.Handlers.requestHandler());

// Your routes
app.get('/', (req, res) => {
  res.send('Hello World');
});

// Sentry error handler (after routes)
app.use(Sentry.Handlers.errorHandler());

app.listen(3000);

React Setup

App.jsx

import { DevCycleProvider, useVariableValue } from '@devcycle/react-client-sdk';
import * as Sentry from '@sentry/react';

// Initialize Sentry
Sentry.init({
  dsn: process.env.REACT_APP_SENTRY_DSN,
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration(),
  ],
  tracesSampleRate: 0.1,
  replaysSessionSampleRate: 0.1,
});

function App() {
  return (
    <Sentry.ErrorBoundary fallback={<ErrorFallback />}>
      <DevCycleProvider
        sdkKey={process.env.REACT_APP_DEVCYCLE_CLIENT_SDK_KEY}
        user={{ user_id: getUserId() }}
      >
        <AppContent />
      </DevCycleProvider>
    </Sentry.ErrorBoundary>
  );
}

function FeatureComponent() {
  const showNewFeature = useVariableValue('new-feature', false);

  return showNewFeature ? <NewFeature /> : <LegacyFeature />;
}

Next.js Setup

sentry.client.config.js

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});

sentry.server.config.js

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: 0.1,
});

next.config.js

const { withSentryConfig } = require('@sentry/nextjs');

const nextConfig = {
  // Your Next.js config
};

module.exports = withSentryConfig(nextConfig, {
  org: process.env.SENTRY_ORG,
  project: process.env.SENTRY_PROJECT,
  silent: true,
});

TypeScript Types

// types/services.d.ts
import type { DevCycleClient } from '@devcycle/nodejs-server-sdk';
import type { Client } from 'node-appwrite';

export interface Services {
  devcycle: DevCycleClient;
  appwrite: {
    client: Client;
    account: Account;
    databases: Databases;
  };
}

export interface User {
  user_id: string;
  email?: string;
  customData?: Record<string, unknown>;
}

Environment Variables

# .env.local

# Sentry
SENTRY_DSN=https://key@sentry.io/project
NEXT_PUBLIC_SENTRY_DSN=https://key@sentry.io/project
SENTRY_AUTH_TOKEN=
SENTRY_ORG=
SENTRY_PROJECT=

# Honeybadger
HONEYBADGER_API_KEY=

# DevCycle
DEVCYCLE_SERVER_SDK_KEY=
DEVCYCLE_CLIENT_SDK_KEY=
NEXT_PUBLIC_DEVCYCLE_CLIENT_SDK_KEY=

# Appwrite
APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
APPWRITE_PROJECT_ID=
APPWRITE_API_KEY=

Error Handling Pattern

import * as Sentry from '@sentry/node';
import Honeybadger from '@honeybadger-io/js';

export function captureError(
  error: Error,
  context?: Record<string, unknown>
): void {
  // Log to console in development
  if (process.env.NODE_ENV === 'development') {
    console.error(error);
  }

  // Report to Sentry
  Sentry.captureException(error, {
    extra: context,
  });

  // Report critical errors to Honeybadger
  if (error.name === 'CriticalError') {
    Honeybadger.notify(error, { context });
  }
}

// Usage
try {
  await riskyOperation();
} catch (error) {
  captureError(error, { userId: user.id, action: 'riskyOperation' });
}

Feature Flag Pattern

import { useVariableValue } from '@devcycle/react-client-sdk';

// Hook for feature checks
export function useFeature(key: string, defaultValue = false): boolean {
  return useVariableValue(key, defaultValue);
}

// Component usage
function MyComponent() {
  const showBeta = useFeature('beta-features');
  const maxItems = useVariableValue('max-items', 10);

  return (
    <div>
      {showBeta && <BetaBanner />}
      <ItemList max={maxItems} />
    </div>
  );
}

Testing with Mocks

// __mocks__/services.ts
export const mockDevcycle = {
  variableValue: jest.fn().mockResolvedValue(false),
};

export const mockSentry = {
  captureException: jest.fn(),
};

// test.spec.ts
jest.mock('@devcycle/nodejs-server-sdk', () => ({
  initializeDevCycle: () => mockDevcycle,
}));