Database Integration
Connect your Azura API to different databases using our adapters.
Database Adapters
Azura provides a unified interface for working with different databases through adapters. Each adapter implements the same methods, making it easy to switch between database systems.
export interface DBAdapter {
connect(): Promise<void>;
find<T = any>(collection: string, query?: any): Promise<T[]>;
insert<T = any>(collection: string, doc: T): Promise<T>;
update(collection: string, query: any, update: any): Promise<number>;
delete(collection: string, query: any): Promise<number>;
}
This consistent interface allows you to build database-agnostic applications that can work with different database backends without changing your core code.
Available Adapters
JSONAdapter
A simple file-based JSON database adapter for development and small applications:
import { JSONAdapter } from '@azura/framework';
// Create a JSON database that stores data in db.json
const db = new JSONAdapter('db.json');
// Connect to the database (creates file if it doesn't exist)
await db.connect();
// Use the database
const users = await db.find('users');
const newUser = await db.insert('users', { name: 'John', email: 'john@example.com' });
const updated = await db.update('users', { name: 'John' }, { verified: true });
const deleted = await db.delete('users', { name: 'John' });
MongoAdapter
Connect to a MongoDB database:
import { MongoAdapter } from '@azura/framework';
// Create a MongoDB adapter
const db = new MongoAdapter('mongodb://localhost:27017', 'my-app');
// Connect to the database
await db.connect();
// Use the database
const users = await db.find('users', { active: true });
const newUser = await db.insert('users', { name: 'John', email: 'john@example.com' });
const updated = await db.update('users', { name: 'John' }, { verified: true });
const deleted = await db.delete('users', { name: 'John' });
MongoDB Driver
npm install mongodb
PostgresAdapter
Connect to a PostgreSQL database:
import { PostgresAdapter } from '@azura/framework';
// Create a PostgreSQL adapter
const db = new PostgresAdapter({
host: 'localhost',
port: 5432,
user: 'postgres',
password: 'secret',
database: 'my-app'
});
// Connect to the database
await db.connect();
// Use the database (tables must already exist)
const users = await db.find('users');
const newUser = await db.insert('users', { name: 'John', email: 'john@example.com' });
const updated = await db.update('users', { id: 1 }, { verified: true });
const deleted = await db.delete('users', { id: 1 });
PostgreSQL Driver
npm install pg
Using Adapters with Controllers
Here's how to use database adapters in your controllers:
import { Controller, Get, Post, Put, Delete, Param, Body } from '@azura/framework';
import { DBAdapter } from '@azura/framework';
@Controller('/users')
export class UserController {
constructor(private db: DBAdapter) {}
@Get('/')
async getAllUsers() {
return await this.db.find('users');
}
@Get('/:id')
async getUserById(@Param('id') id: string) {
const users = await this.db.find('users', { id });
if (!users.length) {
throw new Error('User not found');
}
return users[0];
}
@Post('/')
async createUser(@Body() userData: any) {
return await this.db.insert('users', userData);
}
@Put('/:id')
async updateUser(@Param('id') id: string, @Body() userData: any) {
const updated = await this.db.update('users', { id }, userData);
if (!updated) {
throw new Error('User not found');
}
return { id, ...userData };
}
@Delete('/:id')
async deleteUser(@Param('id') id: string) {
const deleted = await this.db.delete('users', { id });
if (!deleted) {
throw new Error('User not found');
}
return { deleted: true };
}
}
Creating a Custom Adapter
You can create a custom adapter for any database system by implementing the DBAdapter
interface:
import { DBAdapter } from '@azura/framework';
import { Client } from 'your-database-client';
export class CustomAdapter implements DBAdapter {
private client: Client;
constructor(connectionString: string) {
this.client = new Client(connectionString);
}
async connect(): Promise<void> {
await this.client.connect();
}
async find<T = any>(collection: string, query?: any): Promise<T[]> {
return await this.client.find(collection, query);
}
async insert<T = any>(collection: string, doc: T): Promise<T> {
await this.client.insert(collection, doc);
return doc;
}
async update(collection: string, query: any, update: any): Promise<number> {
return await this.client.update(collection, query, update);
}
async delete(collection: string, query: any): Promise<number> {
return await this.client.delete(collection, query);
}
}
Advanced Usage
Transactions
Some database adapters support transactions for atomic operations:
// Example with PostgreSQL adapter (requires custom implementation)
import { PostgresAdapter } from '@azura/framework';
const db = new PostgresAdapter({
/* connection options */
});
await db.connect();
// Start a transaction
const client = await db.pool.connect();
try {
await client.query('BEGIN');
// Perform multiple operations
await client.query('INSERT INTO users(name, email) VALUES($1, $2)', ['John', 'john@example.com']);
await client.query('UPDATE accounts SET balance = balance - $1 WHERE user_id = $2', [100, 1]);
// Commit the transaction
await client.query('COMMIT');
} catch (e) {
// Rollback on error
await client.query('ROLLBACK');
throw e;
} finally {
// Release the client back to the pool
client.release();
}
Connection Pooling
The PostgreSQL adapter uses connection pooling by default. You can configure the pool size:
import { PostgresAdapter } from '@azura/framework';
const db = new PostgresAdapter({
host: 'localhost',
port: 5432,
user: 'postgres',
password: 'secret',
database: 'my-app',
max: 20, // Maximum number of clients in the pool
idleTimeoutMillis: 30000, // How long a client is allowed to remain idle before being closed
});
Raw Queries
You can access the underlying database client for raw queries:
// MongoDB raw queries
const mongoAdapter = new MongoAdapter('mongodb://localhost:27017', 'my-app');
await mongoAdapter.connect();
// Access the MongoDB client directly
const collection = mongoAdapter.db.collection('users');
const result = await collection.aggregate([
{ $match: { age: { $gt: 30 } } },
{ $group: { _id: '$city', count: { $sum: 1 } } }
]).toArray();
// PostgreSQL raw queries
const pgAdapter = new PostgresAdapter({ /* connection options */ });
await pgAdapter.connect();
const result = await pgAdapter.pool.query(
'SELECT * FROM users WHERE age > $1 ORDER BY name',
[30]
);
Best Practices
- Use connection pooling for production applications
- Handle database errors gracefully in your controllers
- Use transactions for operations that require atomicity
- Consider using an ORM like TypeORM or Prisma for complex applications