
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@apso/better-auth-adapter
Advanced tools
Better Auth database adapter that interfaces with Apso-generated CRUD REST endpoints
A production-ready database adapter for Better Auth that seamlessly interfaces with Apso-generated CRUD REST endpoints. This adapter enables Better Auth to work with any REST API following Apso/nestjsx/crud conventions, providing enterprise-grade authentication for modern applications.
npm install @apso/better-auth-adapter
yarn add @apso/better-auth-adapter
pnpm add @apso/better-auth-adapter
import { betterAuth } from 'better-auth';
import { apsoAdapter } from '@apso/better-auth-adapter';
export const auth = betterAuth({
database: apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
}),
emailAndPassword: {
enabled: true,
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
},
});
Create a .env.local file in your project root:
APSO_BASE_URL=https://your-apso-api.com
APSO_API_KEY=your-secret-api-key
import { apsoAdapter } from '@apso/better-auth-adapter';
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
// Retry configuration for resilience
retryConfig: {
maxRetries: 3,
initialDelayMs: 1000,
maxDelayMs: 10000,
retryableStatuses: [429, 500, 502, 503, 504],
},
// Performance optimization
cacheConfig: {
enabled: true,
ttlMs: 300000, // 5 minutes
maxSize: 1000,
},
// Batch operations
batchConfig: {
batchSize: 100,
concurrency: 5,
delayBetweenBatches: 100,
},
// Request timeout
timeout: 30000, // 30 seconds
// Email normalization
emailNormalization: true,
// Observability
observability: {
metricsEnabled: true,
tracingEnabled: true,
logLevel: 'info',
},
});
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
multiTenancy: {
enabled: true,
scopeField: 'tenantId',
getScopeValue: async () => {
// Get tenant ID from context, headers, etc.
return getCurrentTenantId();
},
},
});
import { createHighThroughputApsoAdapter } from '@apso/better-auth-adapter';
const adapter = createHighThroughputApsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
// Optimized for high volume
batchConfig: {
batchSize: 500,
concurrency: 10,
},
// Connection pooling
fetchImpl: new HttpClient({
connectionPool: {
maxConnections: 100,
maxConnectionsPerHost: 20,
keepAlive: true,
},
}),
});
// app/lib/auth.ts
import { betterAuth } from 'better-auth';
import { apsoAdapter } from '@apso/better-auth-adapter';
export const auth = betterAuth({
database: apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
}),
emailAndPassword: {
enabled: true,
},
trustedOrigins: [process.env.NEXT_PUBLIC_APP_URL!],
});
// app/api/auth/[...auth]/route.ts
import { auth } from '@/lib/auth';
export const { GET, POST } = auth.handler;
// server.ts
import express from 'express';
import { betterAuth } from 'better-auth';
import { apsoAdapter } from '@apso/better-auth-adapter';
const app = express();
const auth = betterAuth({
database: apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
}),
emailAndPassword: {
enabled: true,
},
});
app.use('/api/auth/*', auth.handler);
apsoAdapter(config: ApsoAdapterConfig)Creates a Better Auth adapter that interfaces with Apso CRUD APIs.
| Parameter | Type | Required | Description |
|---|---|---|---|
baseUrl | string | ✅ | Base URL of your Apso API |
apiKey | string | ❌ | API key for authentication |
timeout | number | ❌ | Request timeout in milliseconds (default: 10000) |
retryConfig | RetryConfig | ❌ | Retry configuration for failed requests |
cacheConfig | CacheConfig | ❌ | Response caching configuration |
batchConfig | BatchConfig | ❌ | Batch operations configuration |
multiTenancy | MultiTenancyConfig | ❌ | Multi-tenancy settings |
observability | ObservabilityConfig | ❌ | Metrics and tracing configuration |
emailNormalization | boolean | ❌ | Enable email normalization (default: true) |
debugMode | boolean | ❌ | Enable debug logging (default: false) |
// Reliable adapter with enhanced retry logic
const reliableAdapter = createReliableApsoAdapter(config);
// High-throughput adapter optimized for performance
const fastAdapter = createHighThroughputApsoAdapter(config);
// Check adapter health
const isHealthy = await checkAdapterHealth(adapter);
// Get adapter metrics
const metrics = adapter.getMetrics();
console.log(`Success rate: ${metrics.successfulRequests / metrics.totalRequests * 100}%`);
// Close all adapters (useful for graceful shutdown)
await closeAllAdapters();
import { auth } from './lib/auth';
// Sign up new user
const result = await auth.api.signUpEmail({
body: {
email: 'user@example.com',
password: 'securePassword123',
name: 'John Doe',
},
});
if (result.data?.user) {
console.log('User created:', result.data.user.id);
}
// Sign in user
const result = await auth.api.signInEmail({
body: {
email: 'user@example.com',
password: 'securePassword123',
},
});
if (result.data?.session) {
console.log('Session created:', result.data.session.token);
}
// Request password reset
await auth.api.forgetPassword({
body: {
email: 'user@example.com',
redirectTo: 'https://yourdomain.com/reset-password',
},
});
// Reset password with token
await auth.api.resetPassword({
body: {
token: 'reset-token',
password: 'newSecurePassword123',
},
});
The adapter provides comprehensive error handling with specific error codes:
import { AdapterError, AdapterErrorCode } from '@apso/better-auth-adapter';
try {
await auth.api.signInEmail({
body: { email: 'invalid', password: 'wrong' }
});
} catch (error) {
if (error instanceof AdapterError) {
switch (error.code) {
case AdapterErrorCode.VALIDATION_ERROR:
console.error('Invalid input:', error.details);
break;
case AdapterErrorCode.NOT_FOUND:
console.error('User not found');
break;
case AdapterErrorCode.UNAUTHORIZED:
console.error('Invalid credentials');
break;
case AdapterErrorCode.RATE_LIMIT:
console.error('Too many requests, retry after:', error.details.retryAfter);
break;
default:
console.error('Unexpected error:', error.message);
}
}
}
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
cacheConfig: {
enabled: true,
ttlMs: 600000, // 10 minutes
maxSize: 2000,
},
});
// Clear cache when needed
adapter.clearCache();
// Batch create users
const users = await adapter.createMany({
model: 'user',
data: [
{ email: 'user1@example.com', name: 'User 1' },
{ email: 'user2@example.com', name: 'User 2' },
{ email: 'user3@example.com', name: 'User 3' },
],
});
import { HttpClient } from '@apso/better-auth-adapter';
const httpClient = new HttpClient({
connectionPool: {
maxConnections: 50,
maxConnectionsPerHost: 10,
keepAlive: true,
keepAliveTimeout: 30000,
},
});
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
fetchImpl: httpClient,
});
Never hardcode sensitive values. Always use environment variables:
// ✅ Good
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
apiKey: process.env.APSO_API_KEY,
});
// ❌ Bad
const adapter = apsoAdapter({
baseUrl: 'https://api.example.com',
apiKey: 'secret-key-123',
});
The adapter automatically validates and sanitizes all inputs:
// Email normalization is enabled by default
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
emailNormalization: true, // Converts emails to lowercase, trims whitespace
});
Always use HTTPS endpoints in production:
// ✅ Good
const adapter = apsoAdapter({
baseUrl: 'https://api.example.com',
});
// ❌ Bad (HTTP in production)
const adapter = apsoAdapter({
baseUrl: 'http://api.example.com',
});
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
observability: {
metricsEnabled: true,
tracingEnabled: true,
logLevel: 'info',
},
});
// Get detailed metrics
const metrics = adapter.getMetrics();
console.log({
totalRequests: metrics.totalRequests,
successRate: metrics.successfulRequests / metrics.totalRequests,
averageLatency: metrics.averageLatency,
p95Latency: metrics.p95Latency,
cacheHitRate: metrics.cacheHitRate,
});
import { Logger } from '@apso/better-auth-adapter';
const customLogger: Logger = {
debug: (message, meta) => console.debug(message, meta),
info: (message, meta) => console.info(message, meta),
warn: (message, meta) => console.warn(message, meta),
error: (message, meta) => console.error(message, meta),
};
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
logger: customLogger,
});
See our comprehensive Migration Guide for detailed instructions on:
Common issues and solutions can be found in our Troubleshooting Guide.
Connection timeout errors:
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
timeout: 30000, // Increase timeout to 30 seconds
});
Rate limiting issues:
const adapter = apsoAdapter({
baseUrl: process.env.APSO_BASE_URL!,
retryConfig: {
maxRetries: 5,
initialDelayMs: 2000,
maxDelayMs: 30000,
},
});
For complete working examples, see:
We welcome contributions! Please see our Contributing Guide for details on:
See CHANGELOG.md for a detailed history of changes.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by the Mavric Team
FAQs
Better Auth database adapter that interfaces with Apso-generated CRUD REST endpoints
We found that @apso/better-auth-adapter demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.