Introducci贸n

1

驴Ya terminaste el Curso de NestJS: Programaci贸n Modular?

2

Platzi Store: presentaci贸n del proyecto e instalaci贸n

Database

3

C贸mo instalar Docker para este proyecto

4

Configuraci贸n de PostgresSQL en Docker

5

Explorando postgres con interfaces gr谩ficas y terminal

6

Integraci贸n de node-postgres con NestJS

7

Conexi贸n como inyectable y ejecutando un SELECT

8

Usando variables de ambiente

TypeORM

9

驴Qu茅 es un ORM? Instalando y configurando TypeORM Module

10

Creando tu primera entidad

11

TypeORM: active record vs. repositories

12

Crear, actualizar y eliminar

13

Cambiar a Mysql demo (opcional)

Migraciones

14

Sync Mode vs. Migraciones en TypeORM

15

Configurando migraciones y npm scripts

16

Corriendo migraciones

17

Modificando una entidad

Relaciones

18

Relaciones uno a uno

19

Resolviendo la relaci贸n uno a uno en el controlador

20

Relaciones uno a muchos

21

Resolviendo la relaci贸n uno a muchos en el controlador

22

Relaciones muchos a muchos

23

Resolviendo la relaci贸n muchos a muchos en el controlador

24

Manipulaci贸n de arreglos en relaciones muchos a muchos

25

Relaciones muchos a muchos personalizadas

26

Resolviendo la relaci贸n muchos a muchos personalizada en el controlador

Consultas

27

Paginaci贸n

28

Filtrando precios con operadores

29

Agregando indexadores

30

Modificando el naming

31

Serializar

Migraci贸n a NestJS 9 y TypeORM 0.3

32

Actualizando Dependencias para NestJS 9

33

Cambios en TypeORM 0.3

34

Migraciones en TypeORM 0.3

Pr贸ximos pasos

35

C贸mo solucionar una referencia circular entre m贸dulos

36

Contin煤a con el Curso de NestJS: Autenticaci贸n con Passport y JWT

No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Relaciones uno a uno

18/36
Recursos

Aportes 9

Preguntas 4

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Buenas, si no quieren colocar a cada entity los campos createAt y updateAt pueden crear una entidad b谩sica que contenga estos atributos:

import { CreateDateColumn, UpdateDateColumn } from 'typeorm';

export class BasicEntity {
  @CreateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createAt: Date;

  @UpdateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  updateAt: Date;
}

Y luego extender cada entidad que necesite estos campos:

import { BasicEntity } from 'src/database/base.entity';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

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

  @Column({ type: 'varchar', length: 255 })
  email: string;

  @Column({ type: 'varchar', length: 255 })
  password: string;

  @Column({ type: 'varchar', length: 100 })
  role: string;
}

De esta forma nos ahorramos un poco de tiempo al crear nuestras entidades

Vi un comentario que como har铆a para evitar estar repitiendo los campos de CreateAt y UpdateAt sin usar un herencia de clase, investigando un poco me tope con Embedded Entities. Dejo mi ejemplo el cual me funcion贸.

  • Cre茅 un archivo el cual la puse dentro de database.module: 鈥渄ateAt.entity.ts鈥
import { CreateDateColumn, UpdateDateColumn } from 'typeorm';

export class DateAt {
  @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  createAt: Date;

  @UpdateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  updateAt: Date;
}
  • En las entidades solo importe este archivo y dentro de column realize una arrow function hacia esta nueva clase.
import { PrimaryGeneratedColumn, Column, Entity } from 'typeorm';
import { DateAt } from '../../database/dateAt.entity';

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

  @Column({ type: 'varchar', length: 100 })
  name: string;

  @Column({ type: 'varchar', length: 100 })
  lastName: string;

  @Column({ type: 'varchar', length: 20 })
  phone: string;

  @Column(() => DateAt)
  register: DateAt;
}

Me funcion贸, incluso corr铆 el comando npm run migrations:generate -- dateAt, el cual no me dio problema alguno.

Apuntes

Para realizar relaciones 1 a 1 debemos hacer lo siguiente

// src/user/entities/user.entity
@OneToOne(()=>Customer, (customer)=>customer.user, {nullable: true})
@JoinColumn()
customer: Customer;

// src/user/entities/customer.entity
@OneToOne(()=>User, (user)=> user.customer)
user:User;

La entidad que tenga el decorador @JoinColumn es el objeto que creara la llave foranea. Solo una entidad puede tener el decorador @JoinColumn

TypeORM permite tener una relacion bidireccional en las relaciones 1 a 1 sin necesidad de hacer queryes extras.

Lo que hicimos

//src/user/entities/customer.entity
import { Column, CreateDateColumn, Entity, OneToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
import { User } from "./user.entity";

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

  @Column({type: 'varchar', length: 255})
  name: string;

  @Column({type: 'varchar', length: 255})
  lastName: string;

  @Column({ type: 'varchar', length: 255 })
  phone: string;

  @CreateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createAt: Date;

  @UpdateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  updateAt: Date;

  @OneToOne(()=>User, (user)=> user.customer)
  user:User;
}
//src/user/entities/user.entity
import { Column, CreateDateColumn, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
import { Customer } from "./customer.entity";

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

  @Column({ type: 'varchar', length: 255})
  email: string;
  
  @Column({ type: 'varchar', length: 255})
  password: string;

  @Column({ type: 'varchar', length: 255})
  role: string;

  @CreateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP'
  })
  createAt: Date;

  @UpdateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  updateAt: Date;

  @OneToOne(()=>Customer, (customer)=>customer.user, {nullable: true})
  @JoinColumn()
  customer: Customer;

}
//src/user/services/customer.service
import { Injectable, NotFoundException } from '@nestjs/common';

import { Customer } from '../entities/customer.entity';
import { CreateCustomerDto, UpdateCustomerDto } from '../dtos/customer.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class CustomersService {
  constructor(
    @InjectRepository(Customer) private customerRepo: Repository<Customer>
  ){}

  async findAll() {
    return await this.customerRepo.find()
  }

  async findOne(id: number) {
    const customer = await this.customerRepo.findOne(id);
    if (!customer) {
      throw new NotFoundException(`Customer #${id} not found`);
    }
    return customer;
  }

  create(data: CreateCustomerDto) {
    const newCostumer = this.customerRepo.create(data);
    return this.customerRepo.save(newCostumer);
  }

  async update(id: number, changes: UpdateCustomerDto) {
    const customer = await this.findOne(id);
    this.customerRepo.merge(customer, changes);
    return this.customerRepo.save(customer);
  }

  remove(id: number) {
    return this.customerRepo.delete(id);
  }
}
//src/user/services/user.service
import { Injectable, NotFoundException, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

import { User } from '../entities/user.entity';
import { Order } from '../entities/order.entity';
import { CreateUserDto, UpdateUserDto } from '../dtos/user.dto';

import { ProductsService } from './../../products/services/products.service';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User) private userRepo: Repository<User>,
    private productsService: ProductsService,
    private configService: ConfigService,
  ) {}

  async findAll() {
    return await this.userRepo.find();
  }

  async findOne(id: number) {
    const user = await this.userRepo.findOne(id);
    if (!user) {
      throw new NotFoundException(`User #${id} not found`);
    }
    return user;
  }

  async create(data: CreateUserDto) {
    const newUser = this.userRepo.create(data);
    return await this.userRepo.save(newUser);
  }

  async update(id: number, changes: UpdateUserDto) {
    const user = await this.findOne(id);
    const updateUser = this.userRepo.merge(user, changes);
    return this.userRepo.save(updateUser);
  }

  remove(id: number) {
    return this.userRepo.delete(id);
  }

  async getOrderByUser(id: number): Promise<Order> {
    const user = await this.userRepo.findOne(id);
    return {
      date: new Date(),
      user,
      products: await this.productsService.findAll(),
    };
  }
}
npm run migrations:generate -- create-user-customer
npm run migrations:run

Comandos para acceder a la base de datos desde docker en la terminal:

$ docker-compose exec postgres bash
$ psql -h localhost -d my_db -U root
$ \d+
$ \d+ user
$ \d+ customer

Para salir deben ejecutar exit 2 veces.

jajajaj los momentos de los ajustes a 4x son los mejores

Relaciones uno a uno

Para manejar las relaciones debemos especificarlo directamente desde nuestras entidades, para entender como manejar las relaciones uno a uno vamos a tomar como caso a user.entity.ts y a customer.entity.ts, vamos a lograr que los usuarios tengan como par谩metro opcional un customer.

  • **src/user/entities/user.entity**:
import {
  // ...
  OneToOne,
  JoinColumn,
} from 'typeorm';

// importamos la entidad a la cual queremos relacionarnos
import { Customer } from './customer.entity';

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

  // manejamos la referencia la entidad con OneToOne
  // especificamos con una funci贸n flecha que resuelva la entidad que le indiquemos
  // el customer es opcional "{ nullable: true }"
  @OneToOne(() => Customer, { nullable: true })
  // este decorador crea la referencia con una llave foranea
  @JoinColumn()
  costomer: Customer;
}

Relaciones bidireccionales

TypeORM nos la la posibilidad de trabajar las relaciones de forma bidireccional. Lo que significa que le podemos comunicar tambi茅n al customer que el esta relacionado con un usuario:

  • **src/user/entities/customer.entity**:
import {
  // ...
  OneToOne,
} from 'typeorm';

// importamos al usuario
import { User } from './user.entity';

@Entity()
export class Customer {
  // ...

  // especificamos que resuleva la relaci贸n con el usuario
  // especificamos cual es el campo que referencia la relaci贸n, esto para tener comunicaci贸n bidireccional
  @OneToOne(() => User, (user) => user.customer, { nullable: true })
  user: User;
}

Ahora en el usuario, debemos indicar que esperamos recibir la comunicaci贸n con customer:

  • **src/user/entities/user.entity**:
import {
  Column,
  Entity,
  UpdateDateColumn,
  CreateDateColumn,
  PrimaryColumn,
  OneToOne,
  JoinColumn,
} from 'typeorm';

// importamos la entidad a la cual queremos relacionarnos
import { Customer } from './customer.entity';

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

  // con una funci贸n flecha le indicamos que esperamos una relaci贸n bidireccional con customer
  // le indicamos cual es el campo de customer que resuelve la relai贸n
	@OneToOne(
		() => Customer, 
		(customer) => customer.user, // <--
		{ nullable: true }
	)
  // este decorador crea la referencia con una llave foranea
  @JoinColumn()
  customer: Customer;
}

Es importante tener en cuenta que al establecer una relaci贸n bidireccional entre dos entidades en TypeORM, solo debemos especificar la referencia en una de las dos entidades. Esto significa que, si queremos establecer una relaci贸n bidireccional, solo una de las entidades tendr谩 el decorador @JoinColumn(), el cual ser谩 responsable de definir la clave for谩nea. En este caso, la entidad que contiene la referencia es la entidad 鈥淯ser鈥.

Ahora solo debemos correr una migraci贸n y con eso crearemos nuestra primera relaci贸n uno a uno.

Podemos configurar ONDELETE y ONUPDATE en nuestras entidades de la siguiente manera:

  @OneToOne(() => Customer, (customer) => customer.user, {
    nullable: true,
    onDelete: 'SET NULL',
    onUpdate: 'CASCADE',
  })
  @JoinColumn()
  customer: Customer;

Recomiendo hacer una entidad base con id, updateAd y createAd. Para que luego extender todas las demas entidades de esta base.
Ej:

import {
  CreateDateColumn,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';

export abstract class BaseEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @CreateDateColumn({
    type: 'timestamp',
    name: 'created_at',
  })
  createdAt: Date;

  @UpdateDateColumn({
    type: 'timestamp',
    name: 'updated_at',
  })
  updatedAt: Date;
}