
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@arikajs/database
Advanced tools
Database layer for the ArikaJS framework with MySQL & PostgreSQL support.
@arikajs/database is the official database layer for the ArikaJS framework.
It provides a powerful, extensible, and framework-integrated database system with an elegant, fluent API — but designed natively for Node.js & TypeScript.
This package powers DB facade, Models, Migrations, and Query Builder across all ArikaJS applications.
DB.table())| Driver | Status |
|---|---|
| MySQL | ✅ Supported |
| PostgreSQL | ✅ Supported |
| SQLite | ✅ Supported |
| MongoDB | ✅ Supported |
npm install @arikajs/database
config/database.ts
import { env } from '@arikajs/config';
export default {
default: env('DB_CONNECTION', 'mysql'),
connections: {
mysql: {
driver: 'mysql',
host: env('DB_HOST', '127.0.0.1'),
port: env('DB_PORT', 3306),
database: env('DB_DATABASE', 'arikajs'),
username: env('DB_USERNAME', 'root'),
password: env('DB_PASSWORD', ''),
},
pgsql: {
driver: 'pgsql',
host: env('DB_HOST', '127.0.0.1'),
port: env('DB_PORT', 5432),
database: env('DB_DATABASE', 'arikajs'),
username: env('DB_USERNAME', 'postgres'),
password: env('DB_PASSWORD', ''),
},
sqlite: {
driver: 'sqlite',
database: env('DB_DATABASE', 'database.sqlite'),
},
mongodb: {
driver: 'mongodb',
host: env('DB_HOST', '127.0.0.1'),
port: env('DB_PORT', 27017),
database: env('DB_DATABASE', 'arikajs'),
username: env('DB_USERNAME', ''),
password: env('DB_PASSWORD', ''),
},
},
};
import { DB } from '@arikajs/database';
const users = await DB.table('users')
.where('active', true)
.orderBy('id', 'desc')
.get();
await DB.table('users').insert({
name: 'John',
email: 'john@example.com',
});
import { Model } from '@arikajs/database';
export class User extends Model {
static table = 'users';
}
const users = await User.where('active', true).get();
const user = await User.find(1);
// Create new record
const user = new User();
user.name = 'John';
user.email = 'john@example.com';
await user.save();
// Update existing record
const user = await User.find(1);
user.name = 'Updated Name';
await user.save();
// Delete record
await user.deleteInstance();
// Dirty checking
user.name = 'New Name';
console.log(user.isDirty()); // true
console.log(user.getDirty()); // { name: 'New Name' }
ArikaJS Model system supports defining exact types your database inputs should cast to. Using a Proxy behind the scenes, you can retrieve any attribute implicitly and add overrides (mutators/accessors).
class User extends Model {
// Instruct ArikaJS to cast these properties when saving/retrieving
protected casts = {
is_active: 'boolean',
metadata: 'json',
last_login: 'datetime'
};
// Mutator: Set Password Hash
setPasswordAttribute(value: string) {
this.attributes['password'] = hash(value);
}
// Accessor: Get Full Name
getFullNameAttribute() {
return `${this.first_name} ${this.last_name}`;
}
}
const user = new User();
user.is_active = true; // Implicit proxy saving!
user.password = 'my_secure_pass'; // Triggers setPasswordAttribute!
console.log(user.full_name); // Magic accessor triggered!
class User extends Model {
static table = 'users';
posts() {
return this.hasMany(Post, 'user_id');
}
profile() {
return this.hasOne(Profile, 'user_id');
}
}
class Post extends Model {
static table = 'posts';
author() {
return this.belongsTo(User, 'user_id');
}
tags() {
return this.belongsToMany(Tag, 'post_tag', 'post_id', 'tag_id');
}
}
// Using relationships
const user = await User.find(1);
const posts = await user.posts().get();
const profile = await user.profile().get();
// Eager load relationships
const users = await User.with('posts').get();
// Eager load with constraints
const users = await User.with({
posts: q => q.where('published', true),
}).get();
// Multiple relationships
const users = await User.with(['posts', 'profile']).get();
class User extends Model {
static table = 'users';
static active<T extends Model>(this: typeof Model) {
return this.query<T>().where('active', true);
}
static verified<T extends Model>(this: typeof Model) {
return this.query<T>().where('email_verified', true);
}
}
// Using scopes
const activeUsers = await User.active().get();
const verifiedActiveUsers = await User.active().verified().get();
import { Model, withSoftDeletes } from '@arikajs/database';
class User extends withSoftDeletes(Model) {
static table = 'users';
}
// Soft delete (sets deleted_at timestamp)
await User.delete(1);
// Get all records including soft deleted
const allUsers = await User.withTrashed().get();
// Get only soft deleted records
const trashedUsers = await User.onlyTrashed().get();
// Restore a soft deleted record
await User.restore(1);
// Permanently delete
await User.forceDelete(1);
ArikaJS offers a fluent schema builder for creating and altering tables.
import { Schema } from '@arikajs/database';
// 1. Create a table
await Schema.create('users', (table) => {
table.id();
table.string('name');
table.string('email').unique();
table.boolean('is_active').default(true);
table.timestamps();
});
// 2. Alter an existing table
await Schema.table('users', (table) => {
table.string('phone_number').nullable(); // Adds phone_number
table.dropColumn('is_active'); // Drops a column
table.index(['name', 'email']); // Adds a compound index
});
// 3. Drop tables
await Schema.dropIfExists('users');
(Note: A CLI tool to automatically generate and run these migrations is planned: arika make:migration create_users_table).
database/
├── src/
│ ├── Connections
│ │ ├── MongoDBConnection.ts
│ │ ├── MySQLConnection.ts
│ │ ├── PostgreSQLConnection.ts
│ │ └── SQLiteConnection.ts
│ ├── Contracts
│ │ ├── Database.ts
│ │ └── Schema.ts
│ ├── Migrations
│ │ ├── Migration.ts
│ │ └── Migrator.ts
│ ├── Model
│ │ ├── GlobalScope.ts
│ │ ├── Model.ts
│ │ ├── Observer.ts
│ │ ├── Relations.ts
│ │ └── SoftDeletes.ts
│ ├── Query
│ │ ├── Expression.ts
│ │ ├── QueryBuilder.ts
│ │ └── QueryLogger.ts
│ ├── Schema
│ │ ├── Grammars
│ │ │ ├── Grammar.ts
│ │ │ ├── MySQLGrammar.ts
│ │ │ ├── PostgreSQLGrammar.ts
│ │ │ └── SQLiteGrammar.ts
│ │ ├── Schema.ts
│ │ └── SchemaBuilder.ts
│ ├── Seeders
│ │ ├── Seeder.ts
│ │ └── SeedRunner.ts
│ ├── Transactions
│ │ └── TransactionManager.ts
│ ├── Database.ts
│ ├── DatabaseManager.ts
│ └── index.ts
├── tests/
├── package.json
├── tsconfig.json
└── README.md
The following features are designed and planned and will be introduced incrementally without breaking existing APIs.
class User extends Model {
static booted() {
this.creating((user) => {
user.uuid = generateUuid();
});
this.updated((user) => {
// Clear cache, send notifications, etc.
});
}
}
Caching is natively supported inline using the .cache() builder method. It integrates directly with @arikajs/cache when registered inside the framework.
const users = await User.where('active', true)
.cache(3600) // Cache for 1 hour
.get();
Quickly retrieve paginated models using .paginate(). It directly queries out metadata alongside the specific models.
const { data, meta } = await User.where('status', 'active').paginate(1, 15);
// Output:
// {
// data: [...], // Array of 15 User model instances
// meta: {
// total: 50,
// per_page: 15,
// current_page: 1,
// last_page: 4,
// first_page: 1
// }
// }
Executing a bulk operation gives you massive optimization boosts across a large SaaS platform. ArikaJS simplifies doing arrays of records sequentially.
await User.insert([
{ first_name: 'John', last_name: 'Doe' },
{ first_name: 'Jane', last_name: 'Smith' },
{ first_name: 'William', last_name: 'James' }
]);
Scale-out your database layer without touching any model or query code. ArikaJS automatically routes SELECT queries to the read replica pool and INSERT/UPDATE/DELETE to the write master.
// config/database.ts
{
driver: 'mysql',
database: 'myapp',
// One write master
write: { host: 'db-master.internal', username: 'root', password: 'secret' },
// Multiple read replicas — one is chosen at random per query
read: [
{ host: 'db-replica-1.internal' },
{ host: 'db-replica-2.internal' },
],
pool: { min: 2, max: 20 }
}
// No code changes needed — ArikaJS routes automatically!
Use Database.transaction() for clean, auto-rollback-on-error transactions. Nested calls automatically use SQL savepoints.
await Database.transaction(async (trx) => {
await DB.table('accounts').where('id', fromId).update({ balance: DB.raw('balance - 500') });
await DB.table('accounts').where('id', toId).update({ balance: DB.raw('balance + 500') });
await DB.table('transfers').insert({ from_id: fromId, to_id: toId, amount: 500 });
});
// If any statement throws, the entire block is rolled back automatically!
// Manual control:
const txm = await Database.getTransactionManager();
await txm.begin();
try {
await txm.commit();
} catch {
await txm.rollback();
}
Keep your models lean. Attach lifecycle listeners externally without polluting the model with inline logic.
class UserObserver {
async creating(user: User) {
user.role = user.role ?? 'member';
}
async created(user: User) {
await EmailService.sendWelcomeEmail(user.email);
}
// Return false to CANCEL the operation
async deleting(user: User) {
if (user.role === 'admin') return false; // Block admin deletions!
}
async deleted(user: User) {
await AuditLog.record(`User ${user.id} was deleted`);
}
}
// Register once in a ServiceProvider or boot file:
User.observe(new UserObserver());
Introspect every SQL query fired — perfect for debugging slow endpoints or verifying your query plans.
// Enable logging:
Database.enableQueryLog();
await User.where('status', 'active').get();
await Post.where('user_id', 1).limit(5).get();
// Inspect queries:
const logs = Database.getQueryLog();
console.log(logs);
// [
// { sql: 'SELECT * FROM users WHERE status = ?', bindings: ['active'], time: 1.2, timestamp: Date },
// { sql: 'SELECT * FROM posts WHERE user_id = ? LIMIT ?', bindings: [1, 5], time: 0.8, timestamp: Date },
// ]
// Or listen live:
QueryLogger.listen((log) => {
if (log.time > 100) console.warn(`[SLOW QUERY] ${log.sql} (${log.time}ms)`);
});
Database.flushQueryLog(); // Clear when done
Automatic query constraints applied to every query without the caller needing to remember them — ideal for multi-tenancy and similar patterns.
// Register a scope at boot time:
User.addGlobalScope('active_only', (builder) => {
builder.where('status', 'active');
});
// From now on ALL User queries are scoped:
await User.all(); // SELECT * FROM users WHERE status = 'active'
await User.where('role', 'admin').get(); // Adds status constraint automatically
// Escape the scope when needed:
await User.withoutGlobalScopes().all(); // SELECT * FROM users (no scope)
// Or use a full class:
class TenantScope implements GlobalScope {
constructor(private tenantId: number) {}
apply(builder: any) {
builder.where('tenant_id', this.tenantId);
}
}
Post.addGlobalScope('tenant', new TenantScope(currentTenantId));
ArikaJS supports advanced relationship types, bringing you power usually found only in heavy, complex ORMs.
Relate models through an intermediate model.
class Country extends Model {
// Country has many Posts, through Users
posts() {
return this.hasManyThrough(Post, User, 'country_id', 'user_id');
}
}
// Get all posts for users in a specific country
const posts = await country.posts().get();
A model can belong to more than one other model on a single association.
class Image extends Model {
imageable() {
return this.morphTo('imageable');
}
}
class Post extends Model {
image() {
return this.morphOne(Image, 'imageable');
}
}
class User extends Model {
image() {
return this.morphOne(Image, 'imageable');
}
}
// Inserting polymorphic models
const post = await Post.find(1);
await post.image().create({ url: '/foo.png' }); // sets imageable_id=1, imageable_type='Post'
ArikaJS provides robust helpers for BelongsToMany pivot tables:
// 1. Fetching extra pivot columns:
class User extends Model {
roles() {
return this.belongsToMany(Role).withPivot('expires_at', 'assigned_by');
}
}
const user = await User.find(1);
const firstRole = (await user.roles().get())[0];
console.log(firstRole.pivot.expires_at);
// 2. Modifying the relationship (Attach/Detach/Sync/Toggle)
const roleId = 5;
// Simply add the link:
await user.roles().attach(roleId, { assigned_by: 'admin' });
// Remove the link:
await user.roles().detach(roleId);
// Sync: Only keep these exact IDs and remove the rest
await user.roles().sync([1, 2, 3]);
// Toggle: Add if missing, remove if present
await user.roles().toggle([1, 4]);
You can dynamically count the number of related objects without loading them simply by using withCount(). ArikaJS uses optimized subqueries.
const posts = await Post.withCount('comments').all();
console.log(posts[0].comments_count); // e.g: 4
// Count multiple relationships at once
const users = await User.withCount(['posts', 'followers']).all();
console.log(users[0].posts_count);
Filter parents based on the existence of children using whereHas or whereDoesntHave. This runs powerful EXISTS subqueries behind the scenes:
// Fetch users who have at least one published post
const activeBloggers = await User.whereHas('posts', q => q.where('status', 'published')).get();
// Fetch users with no comments
const lurkers = await User.whereDoesntHave('comments').get();
Simply call .paginate(), .simplePaginate(), or .cursorPaginate() to instantly receive a JSON response structure that is ready to be sent to your frontend:
// Traditional offset pagination (page 2, 15 items per page)
const results = await User.paginate(2, 15, '/api/users');
/*
{
"data": [...],
"meta": { "total": 100, "current_page": 2, "last_page": 7, ... },
"links": { "prev_page_url": "/api/users?page=1", "next_page_url": "/api/users?page=3" }
}
*/
// Ultra-fast Cursor pagination for infinite scrolling (WHERE id > last_id LIMIT)
const infinite = await User.cursorPaginate('cursor_id_here', 15);
If you need to process tens of thousands of records, all() will crash your Node process. Use chunk to iterate over them safely:
// Fetches 1000 users at a time, keeping RAM usage perfectly flat
await User.chunk(1000, async (users, page) => {
for (const user of users) {
await emailService.sendNewsletter(user.email);
}
});
"Power without magic."
MIT License © ArikaJS
FAQs
Database layer for the ArikaJS framework with MySQL & PostgreSQL support.
We found that @arikajs/database 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
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

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.