Skip to main content

ORM Overview

@carno.js/orm is a lightweight, Data Mapper-style ORM built for Bun. It supports PostgreSQL and MySQL.

Two Ways to Work

Carno ORM supports two distinct ways to work with entities. Both use @Entity(), but they have different responsibilities.

PatternEntity shapeMain APIBest for
Active Recordclass User extends BaseEntityUser.find(), user.save()Smaller domains, direct entity-centric persistence
Repositoryclass User or class User extends BaseEntityuserRepository.find(), userRepository.create()Service-oriented code, separation of concerns, larger applications

Important Rule

Choose the pattern per entity based on how you want to access persistence:

  • If you want save(), remove(), isPersisted() and static methods like User.find(), extend BaseEntity.
  • If you want to keep persistence logic in repositories, BaseEntity is optional.
  • A class decorated with @Entity() still gets ORM metadata in both cases.
  • Rich serialization works in both cases as long as the class is decorated with @Entity().

What BaseEntity Adds

BaseEntity is not what makes a class an entity. The @Entity() decorator does that.

BaseEntity only adds the Active Record behavior:

  • Static query methods such as find, findOne, findAll and create
  • Instance persistence methods such as save and remove
  • Persistence state helpers such as isPersisted()
  • Dirty tracking for instance-based updates

If you do not need those behaviors, you can use a plain decorated class and access the database exclusively through a repository.

Why Carno ORM?

Unlike many other Node.js ORMs, Carno ORM does not rely on external query builder kernels (like Knex.js). instead, it is built directly on top of Bun's native bun:sqlite (compatible interfaces) and optimized drivers for PostgreSQL and MySQL. This architecture allows for raw performance, lower overhead, and zero legacy Node.js dependencies.

Installation

bun install @carno.js/orm

Configuration

Register the CarnoOrm plugin in your application.

import { Carno } from '@carno.js/core';
import { CarnoOrm } from '@carno.js/orm';

const app = new Carno()
.use(CarnoOrm);

await app.listen(3000);

If you are starting from scratch:

  1. Read Entities to understand what @Entity() does.
  2. Read Repository if you want a service/repository-oriented architecture.
  3. Read Active Record only if you want persistence methods directly on the entity class.

Connection Settings

You need to provide connection settings via carno.config.ts in your project root or programmatically (though carno.config.ts is preferred for tools).

carno.config.ts

import { ConnectionSettings, BunPgDriver } from '@carno.js/orm';

const config: ConnectionSettings = {
driver: BunPgDriver, // or BunMysqlDriver
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'my_db',
// Optional:
migrationPath: './migrations',
};

export default config;

Debugging SQL

To see executed SQL queries, enable the debug flag in your carno.config.ts:

import { BunPgDriver } from '@carno.js/orm';

export default {
driver: BunPgDriver,
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'my_db',

// Enable SQL logging
debug: true,
};

When debug: true is set, the ORM will log all SQL queries with execution time:

[DEBUG] SQL: SELECT u1."id" as "u1_id", u1."name" as "u1_name" FROM "public"."users" u1 WHERE (u1.id = 1) [2ms]

Logger Integration

The ORM automatically detects if @carno.js/logger is installed:

  • With @carno.js/logger: Uses the full-featured LoggerService with colors and formatting
  • Without @carno.js/logger: Falls back to console.log

To use the enhanced logger:

bun install @carno.js/logger

Programmatic Control

You can also enable/disable debug mode programmatically:

import { setDebugEnabled } from '@carno.js/orm';

// Enable debug logging
setDebugEnabled(true);

// Disable debug logging
setDebugEnabled(false);