A flexible database adapter library that provides a unified interface for working with different database providers.
The library supports the following database providers:
- MongoDB - Standard MongoDB database
- Azure Cosmos DB with MongoDB API - Azure Cosmos DB using the MongoDB API
- Azure Cosmos DB - Native Azure Cosmos DB using the Cosmos SDK
- AWS DynamoDB - Amazon DynamoDB
- Supabase - Supabase database
- Unified API for multiple database types
- Type-safe with full TypeScript support
- Validation using Zod schemas
- Multi-database federation for distributing collections across different databases
- Feature detection to check database capabilities
- Browser compatible with appropriate security measures
- Error handling with standardized error types
npm install permas-database-adapter
const { createDatabaseAdapter, DatabaseProvider } = require('permas-database-adapter');
// Create a database adapter for MongoDB
const adapter = createDatabaseAdapter(DatabaseProvider.MONGODB, {
uri: 'mongodb://localhost:27017/mydb',
databaseName: 'mydb',
options: {
useNewUrlParser: true,
useUnifiedTopology: true
}
});
// Connect to the database
await adapter.connect();
// Create a document
const document = await adapter.create('users', {
name: 'John Doe',
email: 'john@example.com'
});
// Find documents
const users = await adapter.find('users', {
filter: { name: 'John Doe' }
});
// Disconnect
await adapter.disconnect();
This library provides a specialized adapter for Azure Cosmos DB with MongoDB API that handles the specific requirements and limitations of Cosmos DB:
const { createDatabaseAdapter, DatabaseProvider } = require('permas-database-adapter');
// Method 1: Using the factory function with COSMOS_MONGODB provider
const adapter = createDatabaseAdapter(DatabaseProvider.COSMOS_MONGODB, {
uri: 'mongodb://username:password@account.mongo.cosmos.azure.com:10255/?ssl=true&replicaSet=globaldb&retrywrites=false&maxIdleTimeMS=120000&appName=@account@',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
retryWrites: false,
ssl: true
}
});
// Method 2: Using the dedicated factory function
const { createCosmosMongoDBAdapter } = require('permas-database-adapter');
const adapter = createCosmosMongoDBAdapter({
uri: 'mongodb://username:password@account.mongo.cosmos.azure.com:10255/?ssl=true&replicaSet=globaldb&retrywrites=false&maxIdleTimeMS=120000&appName=@account@',
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
retryWrites: false,
ssl: true
}
});
Key features of the Cosmos MongoDB adapter:
- Automatically uses the
admin
database as recommended by Microsoft - Sanitizes collection names for Cosmos DB compatibility
- Intelligently handles MongoDB ObjectIds and string IDs
- Optimizes queries for Cosmos DB's limitations
- Safely handles complex update operations that might otherwise fail in Cosmos DB
The adapter provides specialized methods to handle array operations that are commonly challenging with Cosmos DB:
// Update a specific array element using a compatible approach
await adapter.updateArrayField(
'events',
eventId,
'sessions', // array field name
{ title: 'Session X' }, // item identifier
{ duration: 90, room: 'A101' } // update data
);
// Update all array elements in one operation
await adapter.updateAllArrayItems(
'events',
eventId,
'sessions',
{ status: 'confirmed', lastUpdated: new Date() }
);
For operations that would normally require array filters (which aren't supported in Cosmos DB), the adapter uses a fetch-modify-update pattern automatically:
// This operation would normally require array filters in MongoDB,
// but will work seamlessly with our adapter on Cosmos DB
await adapter.updateById('events', eventId, {
complexNestedData: { ... },
deeplyNestedArray: [ ... ],
'deeply.nested.property': 'value'
});
The adapter intelligently handles these Cosmos DB limitations:
- No support for
arrayFilters
in updates - Complex update operators may cause conflicts
- Limited transaction support
- Specific requirements for indexing
- Automatic detection and routing of complex operations through safer patterns
For using the native Azure Cosmos DB SDK:
const { createDatabaseAdapter, DatabaseProvider } = require('permas-database-adapter');
const adapter = createDatabaseAdapter(DatabaseProvider.COSMOSDB, {
endpoint: 'https://your-account.documents.azure.com:443/',
key: 'your-primary-key',
databaseId: 'mydb',
partitionKey: '/id'
});
const { createDatabaseAdapter, DatabaseProvider } = require('permas-database-adapter');
const adapter = createDatabaseAdapter(DatabaseProvider.DYNAMODB, {
region: 'us-east-1',
tableName: 'my-table',
credentials: {
accessKeyId: 'your-access-key',
secretAccessKey: 'your-secret-key'
}
});
const { createDatabaseAdapter, DatabaseProvider } = require('permas-database-adapter');
const adapter = createDatabaseAdapter(DatabaseProvider.SUPABASE, {
url: 'https://your-project.supabase.co',
apiKey: 'your-api-key',
schema: 'public'
});
You can distribute collections across different databases:
import { createMultiDatabaseAdapter } from 'permas-database-adapter';
const db = createMultiDatabaseAdapter({
// Primary database for all collections not explicitly mapped
primary: {
provider: 'mongodb',
config: { uri: 'mongodb://localhost:27017/primary' }
},
// Collection-specific database mappings
collections: {
'users': {
provider: 'mongodb',
config: { uri: 'mongodb://localhost:27017/users' }
},
'logs': {
provider: 'dynamodb',
config: {
region: 'us-east-1',
credentials: {
accessKeyId: 'YOUR_ACCESS_KEY',
secretAccessKey: 'YOUR_SECRET_KEY'
}
}
}
}
});
The library uses Zod for validation of configuration and data:
import { mongodbConfigSchema, validateSchema } from 'permas-database-adapter';
// This will throw a detailed error if invalid
const validConfig = validateSchema(
mongodbConfigSchema,
{
uri: 'mongodb://localhost:27017/mydb',
options: { useNewUrlParser: true }
},
'Invalid MongoDB configuration'
);
All errors are standardized:
import { DatabaseAdapterError, DatabaseErrorType } from 'permas-database-adapter';
try {
await db.findById('users', 'invalid-id');
} catch (error) {
if (error instanceof DatabaseAdapterError) {
console.log(error.type); // e.g., DatabaseErrorType.NOT_FOUND
console.log(error.message); // Descriptive error message
// Handle different error types
switch (error.type) {
case DatabaseErrorType.CONNECTION:
// Handle connection errors
break;
case DatabaseErrorType.VALIDATION:
// Handle validation errors
break;
// etc.
}
}
}
For security reasons, direct database access from browsers is not recommended. However, you can use the library's types and validation utilities:
import { DatabaseAdapter } from 'permas-database-adapter';
// Define your API client that implements the DatabaseAdapter interface
class ApiClient implements DatabaseAdapter {
// ...implement the methods by calling your backend API
}
Check if a specific feature is supported:
if (db.supportsFeature('transactions')) {
// Use transactions
}
All database adapters implement the following interface:
interface DatabaseAdapter {
// Connect to the database
connect(): Promise<void>;
// Disconnect from the database
disconnect(): Promise<void>;
// Check if connected to the database
isConnected(): boolean;
// Create a new item
create<T>(collection: string, data: Partial<T>): Promise<T>;
// Find items with optional filters and pagination
find<T>(collection: string, options?: FindOptions): Promise<PaginatedResult<T>>;
// Find a single item by ID
findById<T>(collection: string, id: string): Promise<T | null>;
// Find a single item by custom filter
findOne<T>(collection: string, filter: Record<string, any>): Promise<T | null>;
// Update an item by ID
updateById<T>(collection: string, id: string, data: Partial<T>): Promise<T | null>;
// Delete an item by ID
deleteById(collection: string, id: string): Promise<boolean>;
// Count documents matching a filter
count(collection: string, filter?: Record<string, any>): Promise<number>;
// Get the native client/driver for direct operations
getNativeClient(): any;
}
The CosmosMongoDBAdapter implements additional methods specifically for working with Azure Cosmos DB:
interface CosmosMongoDBAdapter extends DatabaseAdapter {
// Update a specific element within an array field
updateArrayField<T>(
collection: string,
id: string,
arrayField: string,
arrayItemFilter: Record<string, unknown>,
updateData: Record<string, unknown>
): Promise<T | null>;
// Update all items in an array field
updateAllArrayItems<T>(
collection: string,
id: string,
arrayField: string,
updateData: Record<string, unknown>
): Promise<T | null>;
// Get the MongoDB collection object for direct operations
getCollection(collection: string): Collection;
}
-
createDatabaseAdapter(provider, config)
- Create a database adapter -
createMultiDatabaseAdapter(config)
- Create a multi-database adapter -
createCosmosMongoDBAdapter(config)
- Create an Azure Cosmos DB with MongoDB API adapter
-
DatabaseProvider.MONGODB
- MongoDB adapter -
DatabaseProvider.COSMOS_MONGODB
- Azure Cosmos DB with MongoDB API adapter -
DatabaseProvider.COSMOSDB
- Azure CosmosDB native adapter -
DatabaseProvider.DYNAMODB
- AWS DynamoDB adapter -
DatabaseProvider.SUPABASE
- Supabase adapter
-
connect()
- Connect to the database -
disconnect()
- Disconnect from the database -
isConnected()
- Check if connected to the database -
create(collection, data)
- Create a document -
find(collection, options)
- Find documents -
findById(collection, id)
- Find a document by ID -
findOne(collection, filter)
- Find a document by filter -
updateById(collection, id, data)
- Update a document by ID -
deleteById(collection, id)
- Delete a document by ID -
count(collection, filter)
- Count documents -
getNativeClient()
- Get the native database client -
supportsFeature(feature)
- Check if a feature is supported
MIT