Skip to content

TypeORM Adapter

Use ORMDB with TypeORM's decorator-based entity definitions.


Installation

npm install typeorm @ormdb/typeorm-adapter reflect-metadata
# or
yarn add typeorm @ormdb/typeorm-adapter reflect-metadata

Add to your tsconfig.json:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Setup

1. Define Entities

// entities/User.ts
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  CreateDateColumn,
  OneToMany,
} from 'typeorm';
import { Post } from './Post';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;

  @Column({ default: 'active' })
  status: string;

  @CreateDateColumn()
  createdAt: Date;

  @OneToMany(() => Post, (post) => post.author)
  posts: Post[];
}
// entities/Post.ts
import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  ManyToOne,
  OneToMany,
  CreateDateColumn,
} from 'typeorm';
import { User } from './User';
import { Comment } from './Comment';

@Entity('posts')
export class Post {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  title: string;

  @Column({ nullable: true })
  content: string;

  @Column({ default: false })
  published: boolean;

  @ManyToOne(() => User, (user) => user.posts)
  author: User;

  @Column()
  authorId: string;

  @OneToMany(() => Comment, (comment) => comment.post)
  comments: Comment[];

  @CreateDateColumn()
  createdAt: Date;
}

2. Create Data Source

// data-source.ts
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { OrmdbDriver } from '@ormdb/typeorm-adapter';
import { User } from './entities/User';
import { Post } from './entities/Post';
import { Comment } from './entities/Comment';

export const AppDataSource = new DataSource({
  type: 'ormdb' as any,
  driver: new OrmdbDriver({
    host: 'localhost',
    port: 8080,
  }),
  entities: [User, Post, Comment],
  synchronize: true,  // Auto-sync schema in development
});

3. Initialize Connection

// index.ts
import { AppDataSource } from './data-source';

async function main() {
  await AppDataSource.initialize();
  console.log('Connected to ORMDB');
}

main().catch(console.error);

Repository Pattern

Get Repository

const userRepository = AppDataSource.getRepository(User);

Find Methods

// Find all
const users = await userRepository.find();

// Find with conditions
const activeUsers = await userRepository.find({
  where: { status: 'active' },
});

// Find one
const user = await userRepository.findOne({
  where: { id: 'user-123' },
});

// Find one or fail
const user = await userRepository.findOneOrFail({
  where: { email: 'alice@example.com' },
});

// Find by IDs
const users = await userRepository.findByIds(['user-1', 'user-2']);

Relations

// Load relations
const user = await userRepository.findOne({
  where: { id: 'user-123' },
  relations: ['posts'],
});

// Nested relations
const user = await userRepository.findOne({
  where: { id: 'user-123' },
  relations: ['posts', 'posts.comments'],
});

// Selective loading
const user = await userRepository.findOne({
  where: { id: 'user-123' },
  relations: {
    posts: true,
    profile: true,
  },
});

Select Specific Columns

const users = await userRepository.find({
  select: ['id', 'name', 'email'],
});

// With relations
const users = await userRepository.find({
  select: {
    id: true,
    name: true,
    posts: {
      id: true,
      title: true,
    },
  },
  relations: ['posts'],
});

Query Builder

Basic Query

const users = await userRepository
  .createQueryBuilder('user')
  .where('user.status = :status', { status: 'active' })
  .getMany();

Complex Conditions

const users = await userRepository
  .createQueryBuilder('user')
  .where('user.status = :status', { status: 'active' })
  .andWhere('user.age >= :age', { age: 18 })
  .orWhere('user.role = :role', { role: 'admin' })
  .getMany();

Joins

const users = await userRepository
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.posts', 'post')
  .where('post.published = :published', { published: true })
  .getMany();

Ordering and Pagination

const users = await userRepository
  .createQueryBuilder('user')
  .orderBy('user.name', 'ASC')
  .skip(20)
  .take(10)
  .getMany();

Mutations

Save (Insert/Update)

// Create new
const user = new User();
user.name = 'Alice';
user.email = 'alice@example.com';
await userRepository.save(user);

// Update existing
user.name = 'Alice Smith';
await userRepository.save(user);

// Save multiple
await userRepository.save([user1, user2, user3]);

Insert

// Single insert
await userRepository.insert({
  name: 'Alice',
  email: 'alice@example.com',
});

// Bulk insert
await userRepository.insert([
  { name: 'Alice', email: 'alice@example.com' },
  { name: 'Bob', email: 'bob@example.com' },
]);

Update

// Update by criteria
await userRepository.update(
  { status: 'pending' },
  { status: 'active' }
);

// Update by ID
await userRepository.update('user-123', {
  name: 'Alice Smith',
});

Delete

// Delete by criteria
await userRepository.delete({ status: 'banned' });

// Delete by ID
await userRepository.delete('user-123');

// Soft delete (ORMDB default)
await userRepository.softDelete('user-123');

// Restore soft-deleted
await userRepository.restore('user-123');

Transactions

Using Transaction Manager

await AppDataSource.transaction(async (manager) => {
  const user = manager.create(User, {
    name: 'Alice',
    email: 'alice@example.com',
  });
  await manager.save(user);

  const post = manager.create(Post, {
    title: 'Welcome Post',
    authorId: user.id,
  });
  await manager.save(post);
});

Using Query Runner

const queryRunner = AppDataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

try {
  await queryRunner.manager.save(user);
  await queryRunner.manager.save(post);
  await queryRunner.commitTransaction();
} catch (err) {
  await queryRunner.rollbackTransaction();
  throw err;
} finally {
  await queryRunner.release();
}

Entity Listeners

@Entity()
export class User {
  // ... fields

  @BeforeInsert()
  generateId() {
    this.id = uuid();
  }

  @AfterLoad()
  computeFullName() {
    this.fullName = `${this.firstName} ${this.lastName}`;
  }

  @BeforeUpdate()
  updateTimestamp() {
    this.updatedAt = new Date();
  }
}

Subscribers

@EventSubscriber()
export class UserSubscriber implements EntitySubscriberInterface<User> {
  listenTo() {
    return User;
  }

  beforeInsert(event: InsertEvent<User>) {
    console.log('Before insert:', event.entity);
  }

  afterInsert(event: InsertEvent<User>) {
    console.log('After insert:', event.entity);
  }
}

Eager Relations

@Entity()
export class Post {
  @ManyToOne(() => User, { eager: true })
  author: User;
}

// Author is automatically loaded
const post = await postRepository.findOne({
  where: { id: 'post-123' },
});
console.log(post.author.name);

Cascade Operations

@Entity()
export class User {
  @OneToMany(() => Post, (post) => post.author, {
    cascade: true,
  })
  posts: Post[];
}

// Posts are saved automatically
const user = new User();
user.name = 'Alice';
user.posts = [
  { title: 'Post 1' },
  { title: 'Post 2' },
];
await userRepository.save(user);

ORMDB-Specific Features

Access Native Client

import { getOrmdbClient } from '@ormdb/typeorm-adapter';

const ormdb = getOrmdbClient(AppDataSource);

// Use native ORMDB features
const result = await ormdb.query(/* ... */);

Query Budget

// Via query builder extension
const users = await userRepository
  .createQueryBuilder('user')
  .setParameter('$budget', { maxEntities: 100 })
  .leftJoinAndSelect('user.posts', 'post')
  .getMany();

Include Soft-Deleted

const allUsers = await userRepository.find({
  withDeleted: true,
});

Migration from PostgreSQL

1. Update Data Source

// Before
export const AppDataSource = new DataSource({
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  database: 'mydb',
  // ...
});

// After
import { OrmdbDriver } from '@ormdb/typeorm-adapter';

export const AppDataSource = new DataSource({
  type: 'ormdb' as any,
  driver: new OrmdbDriver({
    host: 'localhost',
    port: 8080,
  }),
  // ...
});

2. Sync Schema

// Enable synchronize to create schema
const AppDataSource = new DataSource({
  // ...
  synchronize: true,
});

Limitations

Feature Status Notes
Raw SQL Not supported Use native client
Query cache Not supported ORMDB has built-in cache
Views Not supported
Migrations Partial Use ORMDB migrations
Many-to-many auto Partial Define explicit junction

Next Steps