Connectors Deep Dive

SDK + Framework Integration

This page provides a comprehensive explanation of connectors in our React marketplace.

Remember: This is our marketplace's pattern. Your marketplace can organize connectors differently (Level 1 Personalization).


What Are Connectors?

Connectors connect and integrate technologies/adapters together.

Key Principle

"A connector cannot exist without the adapters or technologies it tries to connect."

Connectors:

  • βœ… Require one or more adapters/technologies
  • βœ… Import adapter configurations
  • βœ… Extend with framework-specific features
  • βœ… Generate integration code
  • βœ… Can override tech-stack hooks (priority system for SDK-Backend type)

SDK-Backend Connectors - A Special Case

Most connectors simply bridge technologies together. But SDK-Backend Connectors are a special pattern that fundamentally changes how you build features.

Regular Connectors (Most Common)

Purpose: Connect and integrate technologies.

Examples:

  • connectors/database/drizzle-nextjs - Connects Drizzle ORM to Next.js
  • connectors/deployment/docker-nextjs - Integrates Docker with Next.js
  • connectors/monitoring/sentry-nextjs - Integrates Sentry with Next.js
  • connectors/database/drizzle-postgres-docker - Connects Drizzle + Postgres + Docker
  • connectors/infrastructure/tanstack-query-nextjs - Sets up TanStack Query for Next.js

Characteristics:

  • Bridge adapters/technologies
  • Provide wiring, configuration, infrastructure
  • Various roles/patterns depending on what they connect
  • Do NOT replace feature backends

What they typically provide:

{
  "provides": [
    "technology-wiring",
    "framework-integration",
    "configuration"
  ]
}

SDK-Backend Connectors (Special Case) πŸš€

Purpose: REPLACE the entire backend layer for a feature using SDK capabilities.

This is a fundamentally different pattern!

Examples (Only These):

  • connectors/auth/better-auth-nextjs - Complete auth backend via Better Auth SDK
  • connectors/payment/stripe-nextjs-drizzle - Complete payment backend via Stripe SDK

Characteristics:

  • role: "sdk-backend-connector"
  • pattern: "sdk-driven"
  • Provide complete backend functionality
  • Provide SDK-native hooks
  • Eliminate the need for features/[name]/backend/

What they MUST provide:

{
  "provides": [
    "feature-backend-complete",      // ← Backend replacement!
    "feature-sdk-native-hooks",      // ← SDK hooks (e.g., authClient.useSession())
    "feature-api-routes",            // ← API endpoints
    "feature-middleware"             // ← Optional middleware
  ],
  "role": "sdk-backend-connector",
  "pattern": "sdk-driven"
}

πŸ”‘ Key Insight: When you use an SDK-Backend Connector, you DON'T create a backend/ folder in your feature. The connector IS the backend!

Why so few? Most SDKs don't provide complete backend functionality. Only SDKs like Better Auth (full auth) or Stripe (full payments) qualify.


Decision Tree

What does this connector do?
β”‚
β”œβ”€ Bridges/integrates technologies
β”‚  └─ Regular Connector
β”‚     Examples: drizzle-nextjs, docker-nextjs, sentry-nextjs
β”‚
└─ Provides COMPLETE backend for a feature via SDK
   └─ SDK-Backend Connector
      Examples: better-auth-nextjs, stripe-nextjs-drizzle
      Must have: "feature-backend-complete" in provides

Why Do We Need Connectors?

The Problem Without Connectors

Without connectors, adapters would need framework-specific code:

// ❌ BAD: Adapter with framework coupling
// adapters/auth/better-auth/config.ts
import { nextCookies } from 'better-auth/next-js';  // ❌ Next.js import!
 
export const auth = betterAuth({
  plugins: [nextCookies()],  // ❌ Framework-specific!
});

Problems:

  • πŸ”΄ Adapter becomes framework-specific
  • πŸ”΄ Can't reuse with Remix, Astro, etc.
  • πŸ”΄ Violates framework-agnostic principle

The Solution: Connectors

With connectors, adapters stay universal, connectors add framework integration:

// βœ… GOOD: Universal adapter
// adapters/auth/better-auth/config.ts
export const auth = betterAuth({
  database: drizzleAdapter(db),
  emailAndPassword: { enabled: true },
  // No framework-specific code!
});
 
// βœ… GOOD: Next.js connector
// connectors/auth/better-auth-nextjs/config.ts
import { auth as baseAuth } from '@/lib/auth/better-auth';  // Import adapter
import { nextCookies } from 'better-auth/next-js';
 
export const auth = betterAuth({
  ...baseAuth.options,  // ← Use adapter config
  plugins: [nextCookies()],  // ← Add Next.js specific
});

Benefits:

  • βœ… Adapter remains framework-agnostic
  • βœ… Connector adds framework integration
  • βœ… Can have better-auth-remix, better-auth-astro, etc.
  • βœ… Single adapter, multiple connectors

The Adapter β†’ Connector Pattern

The Flow

1. Adapter (Universal SDK Config)
   ↓  imports
2. Connector (Framework Integration)
   ↓  provides hooks to
3. Feature (Business Logic)

Real Example: Auth

Step 1: Adapter (Universal)

// adapters/auth/better-auth/config.ts
export const auth = betterAuth({
  database: drizzleAdapter(db),
  emailAndPassword: { enabled: true },
  session: { expiresIn: 60 * 60 * 24 * 7 },
});

Deployed to: src/lib/auth/better-auth-config.ts
Framework-agnostic: βœ…

Step 2: Connector (Next.js)

// connectors/auth/better-auth-nextjs/config.ts
import { auth as baseAuth } from '@/lib/auth/better-auth-config';
import { nextCookies } from 'better-auth/next-js';
 
export const auth = betterAuth({
  ...baseAuth.options,  // ← Import adapter
  plugins: [
    nextCookies(),  // ← Add Next.js cookies plugin
    ...baseAuth.options.plugins || [],
  ],
});

Deployed to: src/lib/auth/config.ts
Next.js-specific: βœ…

Step 3: Also Generates

  • API routes: /api/auth/[...all]/route.ts
  • Middleware: middleware.ts
  • Client hooks: src/lib/auth/client.ts
  • Native hooks: src/lib/auth/hooks.ts (priority 2)

Connector Structure

Minimal Connector

connectors/[category]/[name]/
β”œβ”€β”€ connector.json       (Metadata - requires adapters!)
β”œβ”€β”€ blueprint.ts         (Generation logic)
β”œβ”€β”€ templates/           (Framework-specific templates)
β”‚   β”œβ”€β”€ config.ts.tpl
β”‚   β”œβ”€β”€ api-routes.ts.tpl
β”‚   β”œβ”€β”€ middleware.ts.tpl
β”‚   └── hooks.ts.tpl
└── README.md            (Documentation)

Example: Better Auth Next.js Connector

connectors/auth/better-auth-nextjs/
β”œβ”€β”€ connector.json
β”œβ”€β”€ blueprint.ts
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ config.ts.tpl              (Extends adapter config)
β”‚   β”œβ”€β”€ api-auth-route.ts.tpl      (Next.js API route)
β”‚   β”œβ”€β”€ middleware.ts.tpl           (Next.js middleware)
β”‚   β”œβ”€β”€ client.ts.tpl               (Auth client)
β”‚   └── hooks.ts.tpl                (SDK-native hooks, priority 2)
└── README.md

Connector Metadata (connector.json)

{
  "name": "Better Auth Next.js Connector",
  "id": "connectors/auth/better-auth-nextjs",
  "type": "connector",
  "category": "auth",
  "version": "1.0.0",
  "description": "Integrates Better Auth with Next.js",
  
  "requires": [
    "adapters/auth/better-auth",  // ← Must have adapter!
    "framework/nextjs"             // ← Must have framework!
  ],
  
  "provides": ["auth-nextjs", "session-middleware"],
  
  "capabilities": {
    "provides": ["auth-integration", "api-routes", "middleware"],
    "requires": ["auth", "nextjs"]
  }
}

Key Fields:

  • requires: CRITICAL! Lists required adapters and framework
  • category: Same as adapter (auth, payment, ai, etc.)
  • provides: What this connector enables
  • capabilities.requires: What must exist before this runs

Category-First Organization

Why Category-First?

Our connectors are organized by business domain:

connectors/
β”œβ”€β”€ auth/                    (Authentication)
β”‚   └── better-auth-nextjs/
β”œβ”€β”€ payment/                 (Payments)
β”‚   └── stripe-nextjs-drizzle/
β”œβ”€β”€ ai/                      (AI Integration)
β”‚   └── vercel-ai-nextjs/
β”œβ”€β”€ email/                   (Email Services)
β”‚   └── resend-nextjs/
β”œβ”€β”€ database/                (Database)
β”‚   └── drizzle-neon/
β”œβ”€β”€ infrastructure/          (Core Infrastructure)
β”‚   β”œβ”€β”€ tanstack-query-nextjs/
β”‚   β”œβ”€β”€ zustand-nextjs/
β”‚   └── rhf-zod-shadcn/
β”œβ”€β”€ monitoring/              (Observability)
β”‚   └── sentry-nextjs/
β”œβ”€β”€ testing/                 (Testing)
β”‚   └── vitest-nextjs/
└── deployment/              (Deployment)
    └── docker-nextjs/

Why this structure?

  • βœ… Easy discovery ("I need auth for Next.js" β†’ connectors/auth/)
  • βœ… Scalable (add more auth connectors as needed)
  • βœ… Clear purpose (folder name = business domain)
  • βœ… Groups related connectors

The Backend Overwrites Hooks Pattern

What Is It?

Connectors can provide SDK-native hooks that override generic tech-stack hooks using a priority system.

The Priority System

Priority 1: Tech-Stack (Generic Fallback)

// features/auth/tech-stack/hooks.ts
export function useSession() {
  return useQuery({
    queryKey: ['session'],
    queryFn: async () => {
      const res = await fetch('/api/auth/session');
      return res.json();
    },
  });
}

Deployed to: src/lib/auth/hooks.ts (priority: 1)

Priority 2: Connector (SDK-Native - OVERWRITES!)

// connectors/auth/better-auth-nextjs/hooks.ts
export { useSession, signIn, signOut } from '@/lib/auth/client';

Deployed to: src/lib/auth/hooks.ts (priority: 2) ← WINS!

How It Works

1. Tech-stack provides generic hooks:

// features/auth/tech-stack/blueprint.ts
{
  action: 'deploy_template',
  template: 'hooks.ts.tpl',
  target: '{{paths.auth_config}}/hooks.ts',
  priority: 1,  // ← Generic fallback
}

2. Connector provides SDK-native hooks:

// connectors/auth/better-auth-nextjs/blueprint.ts
{
  action: 'deploy_template',
  template: 'hooks.ts.tpl',
  target: '{{paths.auth_config}}/hooks.ts',
  priority: 2,  // ← Overwrites tech-stack!
}

3. Frontend imports from unified path:

// Frontend component
import { useSession } from '@/lib/auth';  // Gets Better Auth native hook!

Why This Pattern?

Progressive Enhancement:

  • Start with generic hooks (work with any backend)
  • Connector adds optimized SDK-native hooks
  • Frontend code doesn't change

SDK Advantages:

// Generic hook (priority 1):
useQuery({ queryFn: () => fetch('/api/session') })
 
// Better Auth native hook (priority 2):
useSession()
  βœ… Built-in SSR support
  βœ… Automatic revalidation
  βœ… Optimized caching
  βœ… Type-safe
  βœ… Real-time updates

Tech-Stack Remains Agnostic:

  • Tech-stack doesn't know about Better Auth
  • Connector adds Better Auth integration
  • Can swap auth providers without touching tech-stack

Real Examples

Example 1: Better Auth Next.js Connector

What it does:

1. Imports adapter config:

// templates/config.ts.tpl
import { auth as baseAuth } from '@/lib/auth/better-auth-config';
export const auth = betterAuth({
  ...baseAuth.options,
  plugins: [nextCookies()],
});

2. Generates API routes:

// templates/api-auth-route.ts.tpl
import { auth } from '@/lib/auth/config';
import { toNextJsHandler } from 'better-auth/next-js';
 
export const { GET, POST } = toNextJsHandler(auth);

Deployed to: src/app/api/auth/[...all]/route.ts

3. Generates middleware:

// templates/middleware.ts.tpl
import { auth } from '@/lib/auth/config';
import { NextResponse } from 'next/server';
 
export async function middleware(request) {
  const session = await auth.api.getSession({
    headers: request.headers,
  });
  
  if (!session && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  
  return NextResponse.next();
}

Deployed to: middleware.ts

4. Provides SDK-native hooks:

// templates/hooks.ts.tpl
export {
  useSession,
  signIn,
  signOut,
  signUp,
} from '@/lib/auth/client';

Deployed to: src/lib/auth/hooks.ts (priority 2)


Example 2: Stripe Next.js Drizzle Connector

What it does:

1. Imports from both adapters:

// templates/server.ts.tpl
import { stripe } from '@/lib/payment/stripe';  // From adapter
import { db } from '@/db/client';                // From database adapter
 
export async function createCheckout(userId: string) {
  return stripe.checkout.sessions.create({
    customer: userId,
    // ...
  });
}

2. Generates webhook handler:

// templates/webhook-route.ts.tpl
import { stripe } from '@/lib/payment/stripe';
import { db } from '@/db/client';
import { headers } from 'next/headers';
 
export async function POST(request: Request) {
  const body = await request.text();
  const signature = headers().get('stripe-signature')!;
  
  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  );
  
  // Handle events and update database
  await handleStripeEvent(event, db);
  
  return new Response('OK', { status: 200 });
}

Deployed to: src/app/api/webhooks/stripe/route.ts

3. Generates checkout API:

// templates/checkout-route.ts.tpl
export async function POST(request: Request) {
  const { priceId } = await request.json();
  const session = await createCheckout(priceId);
  return Response.json({ url: session.url });
}

Example 3: Vercel AI Next.js Connector

What it does:

1. Imports adapter config:

// templates/hooks.ts.tpl
export { useChat, useCompletion } from 'ai/react';

2. Generates streaming API route:

// templates/api-chat-route.ts.tpl
import { openai } from '@/lib/ai/config';
import { streamText } from 'ai';
 
export async function POST(request: Request) {
  const { messages } = await request.json();
  
  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages,
  });
  
  return result.toDataStreamResponse();
}

Deployed to: src/app/api/chat/route.ts


When to Create a Connector

βœ… Create a Connector When:

1. Bridging SDK + Framework

// βœ… Good: Adapter + Framework integration
connectors/auth/better-auth-nextjs/
  β†’ Requires: adapters/auth/better-auth
  β†’ Requires: framework/nextjs
  β†’ Provides: Next.js API routes, middleware

2. Framework-Specific Integration Required

// βœ… Good: Sentry needs Next.js config
connectors/monitoring/sentry-nextjs/
  β†’ Next.js specific instrumentation
  β†’ Error boundary setup
  β†’ API route integration

3. SDK Has Native Framework Support

// βœ… Good: Better Auth has Next.js plugins
connectors/auth/better-auth-nextjs/
  β†’ nextCookies() plugin
  β†’ toNextJsHandler() helper
  β†’ Next.js optimized hooks

4. Generating Framework Boilerplate

// βœ… Good: TanStack Query needs SSR setup
connectors/infrastructure/tanstack-query-nextjs/
  β†’ Query client provider
  β†’ SSR hydration
  β†’ Next.js app directory setup

❌ Don't Create a Connector When:

1. Adapter Is Already Framework-Specific

// ❌ Bad: Framework adapter doesn't need connector
adapters/framework/nextjs/
  β†’ Already Next.js specific
  β†’ No connector needed

2. No Framework Integration Needed

// ❌ Bad: Just install directly
connectors/utils/lodash-nextjs/
  β†’ lodash doesn't need Next.js integration
  β†’ Just install package directly

3. Feature Backend Handles It

// ❌ Bad: Custom logic = feature backend
connectors/teams/custom-teams-nextjs/
  β†’ Custom business logic should be in:
  β†’ features/teams/backend/nextjs/

Best Practices

1. Always Require Adapters

βœ… Good:

{
  "requires": [
    "adapters/auth/better-auth",  // ← Required!
    "framework/nextjs"
  ]
}

❌ Bad:

{
  "requires": [
    "framework/nextjs"  // ← Missing adapter!
  ]
}

2. Import From Adapters

βœ… Good:

// Import adapter config
import { auth as baseAuth } from '@/lib/auth/better-auth-config';
 
export const auth = betterAuth({
  ...baseAuth.options,  // ← Use adapter config
  plugins: [nextCookies()],
});

❌ Bad:

// Create new instance (duplication!)
export const auth = betterAuth({
  database: { /* duplicate config */ },  // ❌
  plugins: [nextCookies()],
});

3. Use Priority System Appropriately

βœ… Good:

{
  action: 'deploy_template',
  template: 'hooks.ts.tpl',
  target: '{{paths.auth_config}}/hooks.ts',
  priority: 2,  // ← Overwrites tech-stack (priority 1)
}

4. Organize by Category

βœ… Good:

connectors/[category]/[name]/
connectors/auth/better-auth-nextjs/

❌ Bad:

connectors/better-auth-nextjs/  // ← No category

Connector Patterns

Pattern 1: API Route Generator

Purpose: Generate framework-specific API routes

// connectors/auth/better-auth-nextjs/templates/api-route.ts.tpl
import { auth } from '@/lib/auth/config';
import { toNextJsHandler } from 'better-auth/next-js';
 
export const { GET, POST } = toNextJsHandler(auth);

Pattern 2: Middleware Generator

Purpose: Generate framework middleware

// connectors/auth/better-auth-nextjs/templates/middleware.ts.tpl
export async function middleware(request) {
  const session = await auth.api.getSession({
    headers: request.headers,
  });
  // ... middleware logic
}

Pattern 3: Hook Overwriter

Purpose: Provide SDK-native hooks (priority 2)

// connectors/auth/better-auth-nextjs/templates/hooks.ts.tpl
export { useSession, signIn, signOut } from '@/lib/auth/client';
// Deployed with priority: 2 (overwrites tech-stack)

Pattern 4: Provider Generator

Purpose: Generate framework providers

// connectors/infrastructure/tanstack-query-nextjs/templates/provider.tsx.tpl
'use client';
 
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useState } from 'react';
 
export function QueryProvider({ children }) {
  const [queryClient] = useState(() => new QueryClient());
  
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

Summary

Key Takeaways

  1. Connectors bridge adapters + frameworks

    • Always require one or more adapters
    • Add framework-specific integration
  2. Connectors import from adapters

    • Extend adapter configuration
    • Add framework-specific plugins/features
  3. Connectors can override hooks

    • Use priority system (2 > 1)
    • Provide SDK-native hooks
  4. Connectors are organized by category

    • Easy discovery
    • Scalable structure

Decision Tree

Do I need to integrate SDK + Framework?
β”œβ”€ YES β†’ Does adapter exist for SDK?
β”‚  β”œβ”€ YES β†’ βœ… CREATE CONNECTOR
β”‚  └─ NO  β†’ ❌ CREATE ADAPTER FIRST, then connector
β”‚
└─ NO β†’ Is this just framework setup?
   β”œβ”€ YES β†’ ❌ Use framework adapter directly
   └─ NO  β†’ ❌ Feature backend or not needed

The Golden Rule

"A connector cannot exist without the adapters it tries to connect."

Always:

  1. βœ… Check if adapter exists
  2. βœ… Declare adapter in requires field
  3. βœ… Import from adapter (don't duplicate)
  4. βœ… Add framework-specific features

Next Steps


Remember: Connectors bridge universal adapters with specific frameworks. They cannot exist without the adapters they connect!