Database Integration
Connect MoroJS to your database with built-in support for PostgreSQL, MySQL, SQLite, MongoDB, and more. Features connection pooling, migrations, and ORM integration.
Quick Setup
Get started with database integration in MoroJS with minimal configuration.
Database Adapter Setup
typescript
1import { createApp, createDatabaseAdapter, MySQLAdapter, PostgreSQLAdapter, SQLiteAdapter, z, validate } from '@morojs/moro';
2
3const app = createApp({
4 cors: true,
5 compression: true,
6 helmet: true
7});
8
9// Example 1: Using the factory function (recommended)
10const sqliteDb = createDatabaseAdapter('sqlite', {
11 filename: 'app.db',
12 memory: false
13});
14
15// Example 2: Direct instantiation for more control
16const mysqlDb = new MySQLAdapter({
17 host: 'localhost',
18 port: 3306,
19 user: 'root',
20 password: 'password',
21 database: 'myapp',
22 connectionLimit: 10
23});
24
25// Initialize database connection
26async function initializeDatabase() {
27 try {
28 await sqliteDb.connect();
29
30 // Create tables if they don't exist
31 await sqliteDb.query(`
32 CREATE TABLE IF NOT EXISTS users (
33 id INTEGER PRIMARY KEY AUTOINCREMENT,
34 name TEXT NOT NULL,
35 email TEXT UNIQUE NOT NULL,
36 age INTEGER NOT NULL,
37 created_at DATETIME DEFAULT CURRENT_TIMESTAMP
38 )
39 `);
40
41 console.log('Database initialized successfully');
42 } catch (error) {
43 console.error('Database initialization failed:', error);
44 }
45}
46
47// User schema for validation
48const UserSchema = z.object({
49 name: z.string().min(2).max(50),
50 email: z.string().email(),
51 age: z.number().int().min(18).max(120)
52});
53
54// Use database adapter in routes
55app.get('/users', async (req, res) => {
56 try {
57 const users = await sqliteDb.query('SELECT * FROM users ORDER BY created_at DESC');
58 return {
59 success: true,
60 data: users,
61 adapter: 'SQLite'
62 };
63 } catch (error) {
64 res.status(500);
65 return {
66 success: false,
67 error: 'Failed to fetch users'
68 };
69 }
70});
71
72app.post('/users',
73 validate({ body: UserSchema }),
74 async (req, res) => {
75 try {
76 const userData = req.body;
77 const newUser = await sqliteDb.insert('users', userData);
78
79 res.status(201);
80 return {
81 success: true,
82 data: newUser,
83 message: 'User created successfully'
84 };
85 } catch (error) {
86 res.status(500);
87 return {
88 success: false,
89 error: 'Failed to create user'
90 };
91 }
92 }
93);
94
95// Initialize and start
96initializeDatabase().then(() => {
97 app.listen(3000, () => {
98 console.log('Server running on http://localhost:3000');
99 });
100});
Supported Databases
SQL Databases
- • PostgreSQL
- • MySQL / MariaDB
- • SQLite
- • Microsoft SQL Server
NoSQL Databases
- • MongoDB
- • Redis (caching)
- • DynamoDB
- • Cassandra
Multiple Database Connections
Multiple Database Adapters
typescript
1import { createApp, createDatabaseAdapter, MySQLAdapter, PostgreSQLAdapter } from '@morojs/moro';
2
3const app = createApp({
4 cors: true,
5 compression: true
6});
7
8// Primary database (PostgreSQL)
9const primaryDb = createDatabaseAdapter('postgresql', {
10 host: 'localhost',
11 port: 5432,
12 user: 'postgres',
13 password: process.env.DB_PASSWORD,
14 database: 'myapp_primary',
15 connectionLimit: 20
16});
17
18// Analytics database (separate PostgreSQL instance)
19const analyticsDb = new PostgreSQLAdapter({
20 host: 'analytics.example.com',
21 port: 5432,
22 user: 'analytics_user',
23 password: process.env.ANALYTICS_DB_PASSWORD,
24 database: 'myapp_analytics',
25 connectionLimit: 10
26});
27
28// SQLite for local caching
29const cacheDb = createDatabaseAdapter('sqlite', {
30 filename: 'cache.db',
31 memory: false
32});
33
34// Initialize all database connections
35async function initializeDatabases() {
36 try {
37 await Promise.all([
38 primaryDb.connect(),
39 analyticsDb.connect(),
40 cacheDb.connect()
41 ]);
42
43 console.log('All databases connected successfully');
44 } catch (error) {
45 console.error('Database initialization failed:', error);
46 throw error;
47 }
48}
49
50// Use specific database adapters in routes
51app.get('/users/:id', async (req, res) => {
52 try {
53 const { id } = req.params;
54
55 // Query primary database
56 const user = await primaryDb.queryOne(
57 'SELECT * FROM users WHERE id = ?',
58 [parseInt(id)]
59 );
60
61 if (!user) {
62 res.status(404);
63 return { success: false, error: 'User not found' };
64 }
65
66 // Query analytics database
67 const analytics = await analyticsDb.query(
68 'SELECT * FROM user_analytics WHERE user_id = ?',
69 [parseInt(id)]
70 );
71
72 // Check cache database
73 const cachedData = await cacheDb.queryOne(
74 'SELECT data FROM user_cache WHERE user_id = ? AND expires_at > datetime("now")',
75 [parseInt(id)]
76 );
77
78 return {
79 success: true,
80 data: {
81 user,
82 analytics,
83 cached: cachedData ? JSON.parse(cachedData.data) : null
84 }
85 };
86 } catch (error) {
87 res.status(500);
88 return {
89 success: false,
90 error: 'Failed to fetch user data'
91 };
92 }
93});
94
95// Initialize and start
96initializeDatabases().then(() => {
97 app.listen(3000);
98});
Database Adapter Methods
Available Adapter Methods
typescript
1// Database adapter provides these methods:
2
3// Connection management
4await db.connect();
5await db.disconnect();
6
7// Basic queries
8const users = await db.query('SELECT * FROM users');
9const user = await db.queryOne('SELECT * FROM users WHERE id = ?', [1]);
10
11// CRUD operations
12const newUser = await db.insert('users', {
13 name: 'John Doe',
14 email: 'john@example.com',
15 age: 30
16});
17
18const updatedUser = await db.update('users',
19 { name: 'Jane Doe' },
20 { id: 1 }
21);
22
23const deletedCount = await db.delete('users', { id: 1 });
24
25// Transactions
26const result = await db.transaction(async (tx) => {
27 const user = await tx.insert('users', userData);
28 await tx.insert('user_profiles', { user_id: user.id, ...profileData });
29 return user;
30});
31
32// Raw SQL with parameters
33const complexQuery = await db.query(`
34 SELECT u.*, p.title, p.content
35 FROM users u
36 LEFT JOIN posts p ON p.author_id = u.id
37 WHERE u.created_at > ?
38 ORDER BY u.created_at DESC
39`, [new Date('2024-01-01')]);
40
41// Batch operations
42const users = await db.batchInsert('users', [
43 { name: 'User 1', email: 'user1@example.com' },
44 { name: 'User 2', email: 'user2@example.com' },
45 { name: 'User 3', email: 'user3@example.com' }
46]);
Adapter Factory Usage
typescript
1// Available database adapters
2const adapters = {
3 // SQLite - Great for development and small applications
4 sqlite: createDatabaseAdapter('sqlite', {
5 filename: 'app.db',
6 memory: false // Set to true for in-memory database
7 }),
8
9 // MySQL - Popular relational database
10 mysql: createDatabaseAdapter('mysql', {
11 host: 'localhost',
12 port: 3306,
13 user: 'root',
14 password: 'password',
15 database: 'myapp',
16 connectionLimit: 10
17 }),
18
19 // PostgreSQL - Advanced relational database
20 postgresql: createDatabaseAdapter('postgresql', {
21 host: 'localhost',
22 port: 5432,
23 user: 'postgres',
24 password: 'password',
25 database: 'myapp',
26 connectionLimit: 10,
27 ssl: false // Set to true for production
28 })
29};
30
31// Or use direct instantiation for more control
32const customMysql = new MySQLAdapter({
33 host: 'localhost',
34 port: 3306,
35 user: 'root',
36 password: 'password',
37 database: 'myapp',
38 connectionLimit: 10,
39 acquireTimeout: 60000,
40 timeout: 60000
41});
Database Transactions
Database Transactions with Adapters
typescript
1// Transaction handling with database adapters
2const TransferSchema = z.object({
3 fromUserId: z.number(),
4 toUserId: z.number(),
5 amount: z.number().positive()
6});
7
8app.post('/transfer',
9 validate({ body: TransferSchema }),
10 async (req, res) => {
11 try {
12 const { fromUserId, toUserId, amount } = req.body;
13
14 // Use database adapter transaction
15 const result = await primaryDb.transaction(async (tx) => {
16 // Check sender balance
17 const sender = await tx.queryOne(
18 'SELECT balance FROM accounts WHERE user_id = ? FOR UPDATE',
19 [fromUserId]
20 );
21
22 if (!sender || sender.balance < amount) {
23 throw new Error('Insufficient funds');
24 }
25
26 // Debit sender
27 await tx.query(
28 'UPDATE accounts SET balance = balance - ? WHERE user_id = ?',
29 [amount, fromUserId]
30 );
31
32 // Credit receiver
33 await tx.query(
34 'UPDATE accounts SET balance = balance + ? WHERE user_id = ?',
35 [amount, toUserId]
36 );
37
38 // Log transaction
39 const transaction = await tx.insert('transactions', {
40 from_user_id: fromUserId,
41 to_user_id: toUserId,
42 amount: amount,
43 created_at: new Date()
44 });
45
46 return transaction;
47 });
48
49 return {
50 success: true,
51 transaction: result,
52 message: 'Transfer completed successfully'
53 };
54 } catch (error) {
55 res.status(400);
56 return {
57 success: false,
58 error: error instanceof Error ? error.message : 'Transfer failed'
59 };
60 }
61 }
62);
63
64// Bulk operations with transactions
65app.post('/users/bulk',
66 validate({ body: z.object({
67 users: z.array(z.object({
68 name: z.string(),
69 email: z.string().email(),
70 age: z.number()
71 })).min(1).max(10)
72 }) }),
73 async (req, res) => {
74 try {
75 const { users } = req.body;
76
77 const result = await primaryDb.transaction(async (tx) => {
78 const createdUsers = [];
79
80 for (const userData of users) {
81 const newUser = await tx.insert('users', userData);
82 createdUsers.push(newUser);
83 }
84
85 return createdUsers;
86 });
87
88 res.status(201);
89 return {
90 success: true,
91 data: result,
92 message: `${result.length} users created successfully`
93 };
94 } catch (error) {
95 res.status(500);
96 return {
97 success: false,
98 error: 'Failed to create users in bulk'
99 };
100 }
101 }
102);
Health Checks & Monitoring
Database Health Monitoring
typescript
1// Health check with database status
2app.get('/health', async (req, res) => {
3 try {
4 // Test database connection
5 await primaryDb.query('SELECT 1');
6
7 return {
8 success: true,
9 status: 'healthy',
10 database: 'connected',
11 adapter: 'PostgreSQL',
12 timestamp: new Date().toISOString()
13 };
14 } catch (error) {
15 res.status(503);
16 return {
17 success: false,
18 status: 'unhealthy',
19 database: 'disconnected',
20 error: error instanceof Error ? error.message : String(error)
21 };
22 }
23});
24
25// Database adapter information endpoint
26app.get('/admin/db-info', async (req, res) => {
27 return {
28 success: true,
29 adapters: {
30 primary: {
31 type: 'PostgreSQL',
32 connected: primaryDb.isConnected(),
33 connectionLimit: 20
34 },
35 analytics: {
36 type: 'PostgreSQL',
37 connected: analyticsDb.isConnected(),
38 connectionLimit: 10
39 },
40 cache: {
41 type: 'SQLite',
42 connected: cacheDb.isConnected(),
43 filename: 'cache.db'
44 }
45 }
46 };
47});
48
49// Graceful shutdown with database cleanup
50process.on('SIGINT', async () => {
51 console.log('\n🛑 Shutting down gracefully...');
52 try {
53 await Promise.all([
54 primaryDb.disconnect(),
55 analyticsDb.disconnect(),
56 cacheDb.disconnect()
57 ]);
58 console.log('All databases disconnected');
59 process.exit(0);
60 } catch (error) {
61 console.error('❌ Error during shutdown:', error);
62 process.exit(1);
63 }
64});