Adapters Deep Dive
Universal SDK Configuration (Framework-Agnostic)
This page provides a comprehensive explanation of adapters in our React marketplace.
Remember: This is our marketplace's pattern. Your marketplace can organize adapters differently (Level 1 Personalization).
What Are Adapters?
Adapters configure ONE SDK or technology in a framework-agnostic way.
Key Principle
Adapters must be framework-agnostic:
- β No Next.js specific code
- β No Remix specific code
- β No framework imports
- β Universal SDK configuration
- β Can be imported by connectors
- β Works with ANY framework
Why Do We Need Adapters?
The Problem Without Adapters
Without adapters, every connector duplicates SDK configuration:
// β BAD: Duplication everywhere
// connectors/auth/better-auth-nextjs/config.ts
export const auth = betterAuth({
database: { provider: 'postgresql', url: process.env.DATABASE_URL },
emailAndPassword: { enabled: true },
// ... 50 lines of config
});
// connectors/auth/better-auth-remix/config.ts
export const auth = betterAuth({
database: { provider: 'postgresql', url: process.env.DATABASE_URL },
emailAndPassword: { enabled: true },
// ... same 50 lines duplicated!
});Problems:
- π΄ Configuration duplicated across connectors
- π΄ Update one place, forget the other
- π΄ Inconsistencies creep in
- π΄ Single SDK instance not guaranteed
The Solution: Adapters
With adapters, configuration is centralized:
// β
GOOD: Single source of truth
// adapters/auth/better-auth/config.ts
export const auth = betterAuth({
database: { provider: 'postgresql', url: process.env.DATABASE_URL },
emailAndPassword: { enabled: true },
// ... 50 lines of config (ONCE!)
});
// connectors/auth/better-auth-nextjs/config.ts
import { auth as baseAuth } from '@/lib/auth/better-auth';
export const auth = betterAuth({
...baseAuth.options, // β Import base config
plugins: [nextCookies()], // β Add Next.js specific
});
// connectors/auth/better-auth-remix/config.ts
import { auth as baseAuth } from '@/lib/auth/better-auth';
export const auth = betterAuth({
...baseAuth.options, // β Same base config
plugins: [remixCookies()], // β Add Remix specific
});Benefits:
- β Configuration defined once
- β Connectors import and extend
- β Single SDK instance
- β Consistency guaranteed
Adapter Structure
Minimal Adapter
adapters/[category]/[technology]/
βββ adapter.json (Metadata)
βββ blueprint.ts (Generation logic)
βββ templates/ (Code templates)
β βββ config.ts.tpl
β βββ client.ts.tpl
β βββ types.ts.tpl
βββ README.md (Documentation)Example: Better Auth Adapter
adapters/auth/better-auth/
βββ adapter.json
βββ blueprint.ts
βββ templates/
β βββ better-auth-config.ts.tpl (Universal auth config)
β βββ better-auth-client.ts.tpl (Auth client)
β βββ types.ts.tpl (Core types)
βββ README.mdWhat it deploys:
Generated in user's project:
src/lib/auth/
βββ better-auth-config.ts (Universal config)
βββ better-auth-client.ts (Client instance)
βββ types.ts (Auth types)Adapter Metadata (adapter.json)
{
"name": "Better Auth",
"id": "adapters/auth/better-auth",
"type": "adapter",
"category": "auth",
"version": "1.0.0",
"description": "Better Auth universal authentication configuration",
"provides": [
{
"name": "better-auth",
"version": "1.0.0",
"description": "Authentication SDK"
}
],
"capabilities": {
"provides": ["auth", "session-management", "better-auth"],
"requires": []
}
}Key Fields:
id: Unique identifier (full path)category: Grouping (auth, payment, ai, email, etc.)provides: Packages installedcapabilities.provides: What this adapter enablescapabilities.requires: Dependencies (usually empty for adapters)
Adapter Blueprint (blueprint.ts)
// adapters/auth/better-auth/blueprint.ts
import { defineBlueprint } from '@architech/core';
export default defineBlueprint({
actions: [
// 1. Install Better Auth package
{
action: 'install_packages',
packages: [
'better-auth@^1.0.0',
'@better-auth/drizzle@^1.0.0',
],
},
// 2. Deploy universal config
{
action: 'deploy_template',
template: 'better-auth-config.ts.tpl',
target: '{{paths.auth_config}}/better-auth-config.ts',
priority: 1, // Base config (connectors can extend)
},
// 3. Deploy client
{
action: 'deploy_template',
template: 'better-auth-client.ts.tpl',
target: '{{paths.auth_config}}/better-auth-client.ts',
priority: 1,
},
// 4. Deploy types
{
action: 'deploy_template',
template: 'types.ts.tpl',
target: '{{paths.auth_config}}/types.ts',
priority: 1,
},
],
});Key Concepts:
priority: 1: Base configuration (connectors can override with priority 2){{paths.auth_config}}: Deployment path variable- Templates:
.tplfiles with placeholders
Adapter Templates
Example: Universal Config Template
// adapters/auth/better-auth/templates/better-auth-config.ts.tpl
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "@better-auth/drizzle";
import { db } from "@/db/client";
/**
* Better Auth Configuration (Universal)
*
* This configuration works with any framework.
* Connectors extend this with framework-specific plugins.
*/
export const auth = betterAuth({
// Database adapter (framework-agnostic)
database: drizzleAdapter(db, {
provider: "pg",
}),
// Email and password authentication
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
// Session configuration
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
// User configuration
user: {
additionalFields: {
role: {
type: "string",
required: false,
defaultValue: "user",
},
},
},
});
export type Auth = typeof auth;Note: No framework-specific code! Works with Next.js, Remix, etc.
Real Examples
Example 1: Auth Adapter
Structure:
adapters/auth/better-auth/
βββ adapter.json
βββ blueprint.ts
βββ templates/
βββ better-auth-config.ts.tpl (Universal auth config)
βββ better-auth-client.ts.tpl (Client instance)
βββ types.ts.tpl (Auth types)What it provides:
- β Better Auth SDK configuration
- β Database adapter setup
- β Session configuration
- β Auth client instance
- β Core auth types
What connectors can add:
- Next.js cookies plugin
- Remix cookies plugin
- API routes
- Middleware
Example 2: Payment Adapter
Structure:
adapters/payment/stripe/
βββ adapter.json
βββ blueprint.ts
βββ templates/
βββ stripe-config.ts.tpl (Stripe client config)
βββ types.ts.tpl (Payment types)What it provides:
// stripe-config.ts.tpl
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-06-20',
typescript: true,
});
export const STRIPE_CONFIG = {
currency: 'usd',
successUrl: `${process.env.NEXT_PUBLIC_URL}/payment/success`,
cancelUrl: `${process.env.NEXT_PUBLIC_URL}/payment/cancel`,
};Framework-agnostic: No Next.js, no Remix, just Stripe client.
Example 3: AI Adapter
Structure:
adapters/ai/vercel-ai-sdk/
βββ adapter.json
βββ blueprint.ts
βββ templates/
βββ ai-config.ts.tpl (AI SDK config)
βββ types.ts.tpl (AI types)What it provides:
// ai-config.ts.tpl
import { createOpenAI } from '@ai-sdk/openai';
import { createAnthropic } from '@ai-sdk/anthropic';
export const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export const anthropic = createAnthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
export const AI_CONFIG = {
defaultProvider: 'openai',
defaultModel: 'gpt-4-turbo',
temperature: 0.7,
maxTokens: 2000,
};When to Create an Adapter
β Create an Adapter When:
1. Configuring a New SDK/Library
// β
Good: Universal SDK config
adapters/email/resend/
β Resend client configuration
β Email templates
β Universal email types2. Configuration Can Be Shared Across Frameworks
// β
Good: Works with Next.js, Remix, etc.
adapters/database/drizzle/
β Drizzle ORM config
β Schema setup
β Database client3. You Want Single Source of Truth
// β
Good: One config, multiple connectors
adapters/monitoring/sentry/
β Sentry DSN config
β Error tracking setup
β Universal Sentry client
connectors/monitoring/sentry-nextjs/ β Imports adapter config
connectors/monitoring/sentry-remix/ β Imports adapter configβ Don't Create an Adapter When:
1. Configuration Is Framework-Specific
// β Bad: Next.js specific = connector, not adapter
adapters/framework/nextjs/
β This should be framework/nextjs, not an "adapter"2. No SDK to Configure
// β Bad: Just utilities, no SDK
adapters/utils/lodash/
β Just install lodash directly3. It's Application-Specific Logic
// β Bad: Business logic = feature, not adapter
adapters/teams/custom-teams/
β This should be a feature, not adapterAdapter vs Connector
Key Differences
| Aspect | Adapter | Connector |
|---|---|---|
| Purpose | Configure SDK universally | Integrate SDK + Framework |
| Framework-Specific? | β No | β Yes |
| Requires | Nothing (or other adapters) | One or more adapters |
| Provides | SDK configuration | Framework integration |
| Example | adapters/auth/better-auth/ | connectors/auth/better-auth-nextjs/ |
The Flow
1. Adapter (Universal)
β
2. Connector (Framework-Specific)
β
3. Feature (Business Logic)Example:
adapters/auth/better-auth/
β Universal Better Auth config
connectors/auth/better-auth-nextjs/
β Imports adapter config
β Adds Next.js plugins
β Creates API routes
β Provides SDK-native hooks
features/auth/
β Uses hooks from connector
β Provides UI componentsBest Practices
1. Keep Adapters Framework-Agnostic
β Good:
// adapters/auth/better-auth/config.ts.tpl
export const auth = betterAuth({
database: drizzleAdapter(db), // β
Universal
emailAndPassword: { enabled: true },
});β Bad:
// adapters/auth/better-auth/config.ts.tpl
import { cookies } from 'next/headers'; // β Next.js import!
export const auth = betterAuth({
plugins: [nextCookies()], // β Framework-specific!
});2. Use Deployment Paths
β Good:
{
action: 'deploy_template',
target: '{{paths.auth_config}}/config.ts', // β
Path variable
}β Bad:
{
action: 'deploy_template',
target: 'src/lib/auth/config.ts', // β Hardcoded path
}3. Set Priority Appropriately
β Good:
{
action: 'deploy_template',
target: '{{paths.auth_config}}/config.ts',
priority: 1, // β
Base config (connectors can override)
}4. Export SDK Instance
β Good:
// Export SDK instance for connectors to import
export const stripe = new Stripe(/* ... */);
export const auth = betterAuth(/* ... */);
export const openai = createOpenAI(/* ... */);Common Patterns
Pattern 1: SDK Client Adapter
Purpose: Provide configured SDK client
// adapters/payment/stripe/templates/config.ts.tpl
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-06-20',
typescript: true,
});Usage:
// connectors/payment/stripe-nextjs/server.ts
import { stripe } from '@/lib/payment/stripe'; // Import from adapter
export async function createCheckout() {
return stripe.checkout.sessions.create({ /* ... */ });
}Pattern 2: Configuration Object Adapter
Purpose: Provide configuration constants
// adapters/ai/vercel-ai-sdk/templates/config.ts.tpl
export const AI_CONFIG = {
defaultProvider: 'openai',
defaultModel: 'gpt-4-turbo',
temperature: 0.7,
maxTokens: 2000,
} as const;Pattern 3: Factory Function Adapter
Purpose: Provide factory for creating instances
// adapters/database/drizzle/templates/client.ts.tpl
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const connectionString = process.env.DATABASE_URL!;
const client = postgres(connectionString);
export const db = drizzle(client);Summary
Key Takeaways
-
Adapters are framework-agnostic
- No Next.js, no Remix, no framework imports
- Universal SDK configuration
-
Adapters are imported by connectors
- Connectors extend adapter config
- Single source of truth
-
Adapters provide base configuration
- Priority 1 (connectors can override with priority 2)
- SDK instances and config constants
-
Adapters enable reusability
- One adapter, multiple connectors
- Consistency across frameworks
Decision Tree
Do I need to configure an SDK?
ββ YES β Is the config framework-specific?
β ββ NO β β
CREATE ADAPTER
β ββ YES β β CREATE CONNECTOR (not adapter)
β
ββ NO β Is it business logic?
ββ YES β β CREATE FEATURE (not adapter)
ββ NO β β JUST INSTALL PACKAGENext Steps
- Connectors Deep Dive β - Learn how connectors use adapters
- Features Deep Dive β - Learn about feature architecture
- Architecture Overview β - High-level concepts
- Contributing β - Build your own adapter
Remember: Adapters provide universal SDK configuration. Connectors add framework-specific integration!