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

No se trata de lo que quieres comprar, sino de quién quieres ser. Invierte en tu educación con el precio especial

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

12 Días
2 Hrs
30 Min
59 Seg

Relaciones uno a uno

18/36
Recursos

Aportes 9

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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: “dateAt.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 “User”.

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;
}