Database Integration with TypeORM

Connecting to a database (e.g., PostgreSQL, MySQL) using TypeORM. Defining entities, repositories, and performing CRUD operations.


Defining Entities with TypeORM in NestJS

TypeORM is a powerful ORM (Object-Relational Mapper) that allows you to interact with databases using TypeScript. In a NestJS application, TypeORM provides a clean and efficient way to define your database schema as entities, which are essentially classes that map to database tables. This guide explains how to define these entities, their columns, data types, and relationships.

Creating Entity Classes

Entities represent the structure of your database tables. You create them as TypeScript classes and use TypeORM decorators to define how they map to the database.

Here's a basic example of an entity representing a `User` table:

 import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity() // This decorator marks the class as a TypeORM entity
export class User {
    @PrimaryGeneratedColumn() // This column is the primary key and will be auto-generated
    id: number;

    @Column({ length: 100 }) // This column represents the username with a maximum length of 100 characters
    username: string;

    @Column({ unique: true }) // This column represents the email and must be unique
    email: string;

    @Column({ nullable: true }) // This column represents the age and is allowed to be null
    age: number;
} 

Explanation:

  • @Entity(): This decorator marks the User class as a TypeORM entity. This tells TypeORM that this class represents a table in your database. By default, the table name will be the same as the class name (user in this example, TypeORM automatically lowercases and pluralizes by default unless configured otherwise). You can specify a custom table name using @Entity('users').
  • @PrimaryGeneratedColumn(): This decorator designates the id property as the primary key column. The PrimaryGeneratedColumn decorator automatically generates a unique value for each new record.
  • @Column(): This decorator defines a column in the table. You can specify various options within the decorator, such as:
    • length: Sets the maximum length of a string column.
    • unique: Enforces uniqueness for the column's values.
    • nullable: Indicates whether the column can contain null values. Defaults to false.
    • type: Specifies the data type of the column (e.g., 'varchar', 'int', 'text', 'boolean', 'date', 'json'). If not specified, TypeORM will attempt to infer the type from the TypeScript property type.
    • default: Sets a default value for the column if no value is provided during insertion.

Data Types

TypeORM supports various data types. Here are some common examples:

  • string: Maps to VARCHAR or TEXT depending on the length option.
  • number: Maps to INT, BIGINT, FLOAT, or DOUBLE depending on the precision and scale.
  • boolean: Maps to BOOLEAN or TINYINT(1).
  • Date: Maps to DATE, DATETIME, or TIMESTAMP.
  • JSON: Maps to JSON or TEXT depending on the database.

Defining Relationships

TypeORM simplifies defining relationships between entities. Common relationships include:

  • One-to-One: One entity is related to exactly one other entity.
  • One-to-Many: One entity is related to multiple entities.
  • Many-to-One: Multiple entities are related to one entity (the inverse of One-to-Many).
  • Many-to-Many: Multiple entities are related to multiple other entities.

Let's illustrate a One-to-Many relationship between a `User` and a `Post` entity:

 // post.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.entity'; // Assuming user.entity.ts exists

@Entity()
export class Post {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    title: string;

    @Column()
    content: string;

    @ManyToOne(() => User, (user) => user.posts) // Defines the Many-to-One relationship to User
    @JoinColumn({ name: 'userId' })  // Defines the foreign key column name in the Post table
    user: User;
} 
 // user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Post } from './post.entity';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    username: string;

    @OneToMany(() => Post, (post) => post.user) // Defines the One-to-Many relationship to Post
    posts: Post[];
} 

Explanation:

  • @ManyToOne(() => User, (user) => user.posts): In the Post entity, this decorator defines a Many-to-One relationship with the User entity. The first argument, () => User, specifies the target entity. The second argument, (user) => user.posts, is a function that points to the property in the User entity that represents the inverse side of the relationship (posts). This is crucial for TypeORM to manage the relationship correctly.
  • @JoinColumn({ name: 'userId' }): This decorator, also in the Post entity, specifies the foreign key column name in the Post table that will store the User's ID. Without @JoinColumn, TypeORM would create a default column name (e.g., `userId`). Specifying the name explicitly is generally good practice for clarity and control.
  • @OneToMany(() => Post, (post) => post.user): In the User entity, this decorator defines a One-to-Many relationship with the Post entity. The first argument, () => Post, specifies the target entity. The second argument, (post) => post.user, points to the property in the Post entity (user) that represents the inverse side of the relationship.
  • posts: Post[]: This property in the User entity is an array of Post objects. TypeORM will automatically populate this array when you fetch a User and request its related posts.

By defining entities and relationships in this way, TypeORM allows you to work with your database in an object-oriented manner, making your code more readable, maintainable, and easier to test within your NestJS application.