TypeORM

repository·master·Indexed 12 days ago

https://github.com/typeorm/typeorm

A multi-platform Data-Mapper ORM for TypeScript and ES2023+ that supports both Data Mapper and ActiveRecord patterns. It is compatible with Node.js and browser environments and supports MySQL, MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, and MongoDB databases. Version 1.1.0 introduces the DataSource class as a replacement for Connection.

Tokens
130.3K
Snippets
402
Records
487
Agent score
96%

What's inside TypeORM

  1. Introduction to TypeORM performance optimization

    master

    Performance optimization in TypeORM focuses on minimizing latency and resource usage by addressing common ORM challenges. Key areas for optimization include:

    • Reducing Query Volume: Minimizing the total number of SQL queries sent to the database.
    • Solving N+1 Problems: Preventing scenarios where a single query triggers many subsequent queries for related data.
    • Optimizing Query Execution: Refining complex queries to run faster.
    • Data Retrieval Strategies: Choosing between Eager loading and Lazy loading to ensure efficient data fetching.
    • Leveraging Database Features: Utilizing indexing and caching to speed up data retrieval.
  2. Project structure of the TypeORM SQLite Example

    master

    The playground project follows this structure:

    • src/entity/User.ts: Defines the User entity.
    • src/index.ts: Contains the main application logic.
    • src/ormconfig.ts: Contains the database configuration.
    src/
      ├── entity/
      │   └── User.ts         # User entity definition
      ├── index.ts            # Main application code
      └── ormconfig.ts        # Database configuration
  3. Explore TypeORM extensions

    master

    TypeORM has several community-driven extensions to simplify common tasks such as database modeling, fixture loading, ER diagram generation, and database seeding.

    Key extensions include:

    • Model Generation: typeorm-model-generator (generate models from an existing database).
    • Fixtures: typeorm-fixtures-cli (load fixtures).
    • ER Diagrams: typeorm-uml or erdia.
    • Database Management: typeorm-extension (create, drop, and seed databases).
    • Syncing: typeorm-codebase-sync (automatically update data-source.ts after generating entities).
    • Relations: typeorm-relations (manipulate relations objects) and typeorm-relations-graphql (generate relations based on GraphQL queries).
  4. What is a QueryBuilder?

    master

    A QueryBuilder is a powerful tool in TypeORM that allows you to build SQL queries using a fluent, programmatic syntax. It handles the complexity of SQL construction and automatically transforms the resulting database rows into entity instances.

    const firstUser = await dataSource
        .getRepository(User)
        .createQueryBuilder("user")
        .where("user.id = :id", { id: 1 })
        .getOne()
  5. What is a DataSource?

    master

    A DataSource is the central object in TypeORM used to interact with your database. It holds your database connection settings and manages the initial connection or connection pool based on your RDBMS.

    Key lifecycle methods:

    • initialize(): Must be called to establish the initial connection or connection pool.
    • destroy(): Closes all connections in the pool. In long-running backend servers, you typically call initialize() during application bootstrap and do not call destroy() unless the server is shutting down.
  6. What is an Entity and how to define one

    master

    An Entity is a class that maps to a database table (or a collection in MongoDB). You define an entity by decorating a class with @Entity().

    Key Requirements:

    • Every entity MUST have a primary column (or an ObjectId for MongoDB).
    • If you define a constructor for your entity, its arguments must be optional because the ORM instantiates these classes without knowledge of your constructor arguments when loading data from the database.

    To use a custom table name, pass the name to the decorator: @Entity("my_table_name").

    import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
    
    @Entity()
    export class User {
        @PrimaryGeneratedColumn()
        id: number
    
        @Column()
        firstName: string
    
        @Column()
        lastName: string
    
        @Column()
        isActive: boolean
    }
  7. What is a QueryRunner?

    master

    A QueryRunner represents a single connection to the database.

    • If your RDBMS supports connection pooling, each new QueryRunner instance takes one connection from the pool.
    • For databases without connection pooling, it uses the same connection across the entire DataSource.

    Use a QueryRunner when you need to ensure that multiple operations are executed on the exact same database connection (for example, when managing manual transactions).

  8. Access additional data in custom loggers via QueryRunner

    master

    When implementing a custom logger, methods can accept a QueryRunner instance. This is useful for accessing metadata passed during repository operations. For example, if you pass data via the options object in a .save() call, you can retrieve it from queryRunner.data inside your logger.

    // 1. Pass data during a repository operation
    await postRepository.save(post, { data: { request: request } });
    
    // 2. Access that data in your custom logger implementation
    logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
        const requestUrl = queryRunner && queryRunner.data["request"] 
            ? "(" + queryRunner.data["request"].url + ") " 
            : "";
        console.log(requestUrl + "executing query: " + query);
    }
  9. How migrations work

    master

    A migration is a single file containing SQL queries used to update a database schema and apply changes to an existing database.

    When you change an entity (for example, renaming a property), you create a migration file containing the specific SQL command required to transform the existing database structure to match the new entity definition. Once the migration is executed, the database schema is synchronized with your updated codebase without losing existing data.

    // Example: An existing entity
    @Entity()
    export class Post {
        @PrimaryGeneratedColumn()
        id: number
    
        @Column()
        title: string
    
        @Column()
        text: string
    }
    
    // To rename 'title' to 'name', you would use a migration with SQL like:
    // ALTER TABLE "post" RENAME COLUMN "title" TO "name";
  10. Implement One-to-One relations

    master

    A One-to-One relation is a relationship where one instance of Entity A contains exactly one instance of Entity B, and vice versa.

    To implement this:

    1. Use the @OneToOne decorator on the property representing the relation.
    2. Use the @JoinColumn decorator on exactly one side of the relation. The side decorated with @JoinColumn will be the 'owner' of the relation, meaning its database table will contain the foreign key column.

    Uni-directional vs Bi-directional:

    • Uni-directional: The @OneToOne decorator is only present on one entity.
    • Bi-directional: The @OneToOne decorator is present on both entities. To make it bi-directional, you must pass the inverse side as the second argument to the @OneToOne decorator on both sides.
    @Entity()
    export class User {
        @PrimaryGeneratedColumn()
        id: number
    
        @Column()
        name: string
    
        @OneToOne(() => Profile)
        @JoinColumn()
        profile: Profile
    }
  11. Use entity inheritance to reduce code duplication

    master

    You can use standard TypeScript class inheritance to share common columns (like id, title, or description) across multiple entities. By creating an abstract class as a base, all columns, relations, and embeds from the parent will be inherited and created in the final child entities. Note that child classes must still be decorated with @Entity() to be recognized as database tables.

    export abstract class Content {
        @PrimaryGeneratedColumn()
        id: number
    
        @Column()
        title: string
    
        @Column()
        description: string
    }
    
    @Entity()
    export class Photo extends Content {
        @Column()
        size: string
    }
    
    @Entity()
    export class Question extends Content {
        @Column()
        answersCount: number
    }