npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

nextjs-nango-plugin

v0.4.3

Published

Easy Nango integration for Next.js apps with flexible database support

Readme

Next.js Nango Plugin

Seamless OAuth integration for Next.js apps using Nango with complete database flexibility through dependency injection.

Features

  • 🚀 Zero-config mode - Start without any database - pure Nango API proxy
  • 🔌 Optional services architecture - Use only what you need, when you need it
  • 🎯 Universal ownership model - Adapter pattern works with any user/team/org structure
  • 🗄️ Database agnostic - Optional dependency injection supports any database (Supabase, Prisma, PostgreSQL, MongoDB, etc.)
  • 🔄 Dynamic provider support - No hardcoded provider list - works with any Nango-configured provider
  • 🪝 Automatic webhook handling - Real-time connection status updates
  • 🎨 Production-ready React components - Beautiful, customizable connection management UI
  • 🛠️ Interactive CLI - Guided setup and configuration with automatic file generation
  • 🔒 Security first - Built-in RLS support, secure token handling, webhook signature verification, and optional credential encryption
  • 📦 Lightweight - Minimal dependencies, tree-shakeable exports
  • 🧪 Fully tested - Comprehensive test coverage with Jest

Installation

npm install nextjs-nango-plugin

Quick Start

Zero-Config Mode (NEW! 🚀)

Get started in seconds without any database setup:

import { createNangoHandler } from 'nextjs-nango-plugin';

// app/api/nango/[...path]/route.ts
export const handler = createNangoHandler({
  nango: {
    secretKey: process.env.NANGO_SECRET_KEY!
  }
});

export const { GET, POST, PUT, DELETE } = handler;

That's it! You now have a working Nango integration.

Standard Setup

1. Initialize the plugin

npx nextjs-nango-plugin init

This interactive CLI will:

  • Guide you through database adapter selection (Supabase, Prisma, Custom)
  • Create API routes at /api/nango/[...path]/route.ts
  • Generate type-safe configuration in /lib/nango-config.ts
  • Add required environment variables to .env.local
  • Create example integration pages based on your app structure

2. Choose your configuration mode

The plugin now supports multiple configuration modes with all services being optional:

Zero-Config Mode (NEW! - No database required)

// Simplest setup - pure Nango API proxy
export const nangoConfig: NangoPluginConfig = {
  nango: {
    secretKey: process.env.NANGO_SECRET_KEY!,
  },
};

With Connection Tracking (Most common)

export const nangoConfig: NangoPluginConfig = {
  nango: {
    secretKey: process.env.NANGO_SECRET_KEY!,
  },
  createConnectionService: async (request?) => {
    // Your database adapter for tracking connections
    // The plugin works with ANY database through this interface
  },
};

3. Create your database schema

Create a table for storing Nango connections with these required fields:

CREATE TABLE nango_connections (
  id VARCHAR(255) PRIMARY KEY,
  owner_id VARCHAR(255) NOT NULL,        -- User/entity that owns this connection
  organization_id VARCHAR(255),          -- Optional: for multi-tenant scenarios
  provider VARCHAR(100) NOT NULL,        -- Provider config key from Nango
  connection_id VARCHAR(255) UNIQUE NOT NULL,
  status VARCHAR(20) NOT NULL,           -- ACTIVE, INACTIVE, ERROR, EXPIRED
  metadata JSONB,                        -- Additional custom data
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),

  INDEX idx_owner (owner_id),
  INDEX idx_org (organization_id),
  INDEX idx_connection (connection_id)
);

4. Configure Nango

In your Nango dashboard:

  1. Add your OAuth providers (GitHub, Notion, etc.)
  2. Set your webhook URL to: https://your-app.com/api/nango/webhooks

5. Use the ConnectionManager component

import { ConnectionManager } from 'nextjs-nango-plugin';

export default function IntegrationsPage() {
  const user = await getUser(); // Your auth logic

  return (
    <ConnectionManager
      sessionData={{
        end_user: {
          id: user.id,
          email: user.email,
          display_name: user.name
        }
      }}
      providers={['github', 'notion', 'slack']} // Optional: auto-fetches if not provided
      onConnectionUpdate={() => {
        console.log('Connection updated!');
      }}
      className="max-w-4xl mx-auto"
    />
  );
}

Configuration

Optional Services Architecture (NEW in v0.3.0)

All services are now optional with automatic Nango API fallback:

Available Services

  1. ConnectionService (Optional)

    • Tracks OAuth connections in your database
    • Manages ownership and access control
    • Falls back to Nango when not provided
  2. IntegrationService (Optional)

    • Caches provider metadata locally
    • Reduces API calls for better performance
    • Falls back to Nango API when not provided
  3. SecretsService (Optional)

    • Stores encrypted credentials locally
    • Enables offline token refresh
    • Falls back to Nango's secure storage when not provided

Progressive Enhancement Pattern

Start simple and add services as your needs grow:

// 1. Start with zero-config
const handler = createNangoHandler({
  nango: { secretKey: process.env.NANGO_SECRET_KEY! }
});

// 2. Add connection tracking when needed
const handler = createNangoHandler({
  createConnectionService: async (req) => new MyConnectionService(req),
  nango: { secretKey: process.env.NANGO_SECRET_KEY! }
});

// 3. Add integration caching for performance
const handler = createNangoHandler({
  createConnectionService: async (req) => new MyConnectionService(req),
  createIntegrationService: async () => new CachedIntegrationService(),
  nango: { secretKey: process.env.NANGO_SECRET_KEY! }
});

// 4. Add secure credential storage
const handler = createNangoHandler({
  createConnectionService: async (req) => new MyConnectionService(req),
  createIntegrationService: async () => new CachedIntegrationService(),
  createSecretsService: async (req) => new EncryptedSecretsService(req),
  nango: { secretKey: process.env.NANGO_SECRET_KEY! }
});

Dependency Injection Architecture

When you need database storage, the plugin uses a powerful dependency injection pattern that adapts to ANY database and ownership model:

interface Connection {
  id: string;                    // Unique identifier in your database
  owner_id: string;              // Required: User/entity that owns this connection
  organization_id?: string;      // Optional: Organization/team identifier
  provider: string;              // Provider config key from Nango
  connection_id: string;         // Connection identifier in Nango
  status: 'ACTIVE' | 'INACTIVE' | 'ERROR' | 'EXPIRED';
  metadata?: Record<string, any>; // Additional custom data
  created_at: string;
  updated_at: string;
}

interface ConnectionService {
  getConnections(filters?: Record<string, any>): Promise<Connection[]>;
  createConnection(
    provider: string,
    connectionId: string,
    ownerId: string,
    organizationId?: string,
    metadata?: Record<string, any>
  ): Promise<Connection>;
  getConnection?(connectionId: string): Promise<Connection | null>;
  updateConnectionStatus(connectionId: string, status: Connection['status']): Promise<Connection>;
  deleteConnection(connectionId: string): Promise<boolean>;
}

Your implementation determines:

  • How to extract the owner_id from your authentication system
  • Which database/ORM to use
  • How to handle multi-tenancy with organization_id
  • What additional metadata to store

Database Adapter Examples

Supabase Adapter

import { createClient } from '@supabase/supabase-js';

export const nangoConfig: NangoPluginConfig = {
  createConnectionService: async (request?) => {
    // Get auth from request headers
    const token = request?.headers.get('authorization')?.replace('Bearer ', '');
    const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
      global: { headers: { Authorization: `Bearer ${token}` } }
    });

    return {
      async getConnections() {
        const { data } = await supabase
          .from('nango_connections')
          .select('*');
        return data || [];
      },
      async createConnection(provider, connectionId, ownerId, organizationId, metadata) {
        const { data } = await supabase
          .from('nango_connections')
          .insert({
            provider,
            connection_id: connectionId,
            owner_id: ownerId,
            organization_id: organizationId,
            metadata,
            status: 'ACTIVE'
          })
          .select()
          .single();
        return data;
      },
      // ... other methods
    };
  },
};

Prisma Adapter

import { prisma } from '@/lib/prisma';
import { getServerSession } from 'next-auth';

export const nangoConfig: NangoPluginConfig = {
  createConnectionService: async (request?) => {
    const session = await getServerSession(authOptions);
    const userId = session?.user?.id;

    return {
      async getConnections() {
        return await prisma.nangoConnection.findMany({
          where: { userId }
        });
      },
      async createConnection(provider, connectionId, ownerId, organizationId, metadata) {
        return await prisma.nangoConnection.create({
          data: {
            owner_id: ownerId,
            organization_id: organizationId,
            provider,
            connection_id: connectionId,
            metadata,
            status: 'ACTIVE'
          }
        });
      },
      // ... other methods
    };
  },
};

MongoDB Adapter

import { MongoClient } from 'mongodb';

export const nangoConfig: NangoPluginConfig = {
  createConnectionService: async (request?) => {
    const client = new MongoClient(MONGODB_URI);
    const db = client.db('myapp');
    const userId = await getUserIdFromRequest(request);

    return {
      async getConnections() {
        return await db.collection('connections')
          .find({ userId })
          .toArray();
      },
      // ... other methods
    };
  },
};

API Reference

Core Handler

createNangoHandler

Creates all the API route handlers for your Next.js app.

import { createNangoHandler } from 'nextjs-nango-plugin';
import { nangoConfig } from '@/lib/nango-config';

const handlers = createNangoHandler(nangoConfig);

export const { GET, POST, PUT, DELETE } = handlers;

This single handler manages:

  • GET /api/nango/connections - List user's connections
  • GET /api/nango/integrations - List available providers
  • POST /api/nango/auth/session - Create OAuth session
  • POST /api/nango/webhooks - Handle Nango webhooks (auto-creates connections on successful auth)
  • PUT /api/nango/connections/[id] - Update connection status
  • DELETE /api/nango/connections/[id] - Delete connection (removes from both database and Nango)

Components

ConnectionManager

Main component for managing OAuth connections.

interface ConnectionManagerProps {
  sessionData?: {                // Session data for Nango Connect
    end_user: {
      id: string;                // Required: User identifier
      email?: string;            // Optional: User email
      display_name?: string;     // Optional: Display name
    };
    organization?: {             // Optional: Organization context
      id: string;
      display_name?: string;
    };
  };
  providers?: string[];          // List of providers (auto-fetches if not provided)
  onConnectionUpdate?: () => void; // Callback on connection change
  apiEndpoint?: string;          // API endpoint (default: '/api/nango')
  className?: string;            // Additional CSS classes
}

IntegrationCard

Individual provider card component with automatic provider metadata.

interface IntegrationCardProps {
  provider: string;              // Provider key (e.g., 'github', 'notion')
  connection?: {                 // Current connection state
    status: 'ACTIVE' | 'INACTIVE' | 'ERROR' | 'EXPIRED';
    lastSync?: string;
  };
  onConnect: () => void;         // Connect callback
  onDisconnect?: () => void;     // Disconnect callback
  isConnecting: boolean;         // Loading state
  className?: string;            // Additional CSS classes
  showStatus?: boolean;          // Show connection status badge
}

The component automatically:

  • Fetches and displays provider logos
  • Shows human-readable provider names
  • Handles loading and error states
  • Displays connection status with color coding

Core Services

NangoService

Type-safe wrapper for Nango API operations.

import { NangoService } from 'nextjs-nango-plugin';

const nango = new NangoService(secretKey, host?);

// Create OAuth session for Nango Connect
const session = await nango.createSession(
  userId: string,
  organizationId: string,
  integrationId?: string,
  userEmail?: string
);

// List all configured integrations
const integrations = await nango.listIntegrations();
// Returns: Integration[] with provider metadata

// Get specific connection details
const connection = await nango.getConnection(connectionId);

// Delete connection from Nango
await nango.deleteConnection(connectionId, providerConfigKey);

ConnectionService Interface

Your adapter must implement this interface:

interface ConnectionService {
  // Get all connections for the current context
  // Optional filters parameter for querying
  getConnections(filters?: Record<string, any>): Promise<Connection[]>;

  // Create a new connection record with explicit ownership
  createConnection(
    provider: string,
    connectionId: string,
    ownerId: string,
    organizationId?: string,
    metadata?: Record<string, any>
  ): Promise<Connection>;

  // Update connection status
  updateConnectionStatus(
    connectionId: string,
    status: ConnectionStatus
  ): Promise<Connection>;

  // Delete connection record
  deleteConnection(connectionId: string): Promise<void>;
}

// Connection status enum
enum ConnectionStatus {
  ACTIVE = 'ACTIVE',
  INACTIVE = 'INACTIVE',
  ERROR = 'ERROR',
  EXPIRED = 'EXPIRED'
}

Dynamic Provider Support

The plugin automatically works with ANY provider configured in Nango - no code changes needed:

1. Configure in Nango Dashboard

  • Add any OAuth provider (standard or custom)
  • Configure OAuth credentials and scopes
  • Set up sync scripts if needed

2. Use immediately in your app

<ConnectionManager
  sessionData={{
    end_user: {
      id: userId,
      email: userEmail,
      display_name: userName
    }
  }}
  providers={[
    'github',           // Standard providers
    'notion',
    'slack',
    'custom-erp',       // Your custom providers
    'internal-api',
    'legacy-system'
  ]}
/>

3. Provider metadata is automatic

The plugin automatically:

  • Fetches provider names and logos from Nango
  • Handles OAuth flow for any provider
  • Manages connection lifecycle
  • Syncs webhook events

Webhook Handling

The plugin automatically processes Nango webhook events with signature verification:

Supported Events

  • auth:success - Connection authorized → Status: ACTIVE
  • auth:error - Authorization failed → Status: ERROR
  • connection:deleted - Connection removed → Status: INACTIVE
  • sync:success - Sync completed → Updates lastSync timestamp
  • sync:error - Sync failed → Status: ERROR
  • connection:expired - Token expired → Status: EXPIRED

Security

  • Webhook signatures are automatically verified using Nango's built-in verification
  • Events are processed atomically with proper error handling
  • Connection IDs are validated before database updates

Environment Variables

# Supabase
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key

# Nango
NANGO_SECRET_KEY=your-nango-secret-key

CLI Commands

init

Interactive setup wizard for your Next.js app.

npx nextjs-nango-plugin init

Options:

  • --database <type> - Skip interactive mode (supabase | prisma | custom)
  • --path <path> - API route path (default: /api/nango)
  • --skip-env - Don't create .env.local file

The CLI will:

  1. Detect your Next.js app structure (App Router/Pages Router)
  2. Guide you through database adapter selection
  3. Generate all necessary files with proper types
  4. Add environment variables
  5. Provide next steps documentation

TypeScript Support

Full TypeScript support with exported types for maximum type safety:

import type {
  // Configuration
  NangoPluginConfig,

  // Services
  ConnectionService,
  NangoService,

  // Data types
  Connection,
  ConnectionStatus,
  Integration,

  // Component props
  ConnectionManagerProps,
  IntegrationCardProps,

  // Webhook types
  WebhookEvent,
  WebhookPayload
} from 'nextjs-nango-plugin';

All components and services are fully typed with strict mode compatibility.

Architecture Benefits

Dependency Injection

The plugin's architecture provides:

  • Database Independence: Works with any database or ORM
  • Auth Flexibility: Integrates with any authentication system
  • Custom Logic: Add business logic in your ConnectionService
  • Type Safety: Full TypeScript support with generics
  • Testing: Easy to mock and test with dependency injection

Production Ready

  • Error Handling: Comprehensive error boundaries and fallbacks
  • Performance: Optimized with React.memo and SWR caching
  • Accessibility: WCAG 2.1 AA compliant components
  • Security: Webhook verification, CSRF protection ready
  • Monitoring: Detailed logging and error tracking support

Testing

npm test        # Run all tests
npm test:watch  # Watch mode
npm test:coverage # Coverage report

The plugin includes:

  • Unit tests for all services
  • Component tests with React Testing Library
  • Integration tests for API handlers
  • Mock implementations for testing

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT

Examples

See the /examples directory for complete implementations:

  • nextjs-example - Basic Next.js App Router setup
  • with-supabase - Supabase integration with RLS
  • with-prisma - Prisma ORM with PostgreSQL
  • multi-tenant - Organization-based multi-tenancy

Support

Acknowledgments

Built with:

  • Nango - Unified OAuth for developers
  • Supabase - Open source Firebase alternative
  • Next.js - The React framework