> Manuales > Manual de NestJS

Qué son los repositorios de TypeORM, cómo usar repositorios dentro de Nest Framework, inyectando cómodamente las clases en los servicios. Ejemplos de accesos y modificaciones de registros en la base de datos en NestJS usando el ORM.

Repositorios en TypeORM y Nest

Los repositorios son una de las piezas de software que están implementadas en el lado de TypeORM y que sirven para proveer de las funcionalidades típicas de acceso a los datos y, por supuesto, las operaciones de escritura en las tablas.

Los repositorios están limitados al trabajo con una entidad particular. Por ejemplo, el repositorio de "product" trabajará con los productos.

Una vez tengamos el repositorio "product" podemos invocar sobre él los métodos como findOne(), save(), insert() y muchos otros, todos disponibles gracias a TypeORM.

Incluso podemos agregar métodos a los repositorios por medio de la realización de un "custom repository", del que podemos ver más detalles más adelante o en la documentación de TypeORM.

Los repositorios los podemos conseguir generar de manera explícita mediante el API de TypeORM, pero Nest ya nos facilita las cosas, aportando la posibilidad de inyectar repositorios de una entidad en los servicios donde lo necesitemos.

Cómo usar repositorios en NestJS

En el artículo anterior dedicado a las entidades ya explicamos que en los módulos que implementan una entidad necesitamos usar el método forFeature() de TypeOrmModule, indicando el nombre de la entidad.

Esto es todo lo que necesitamos para poder usar un repositorio en un servicio, pero antes debemos configurar el propio servicio inyectando el repositorio que necesita.

Inyección de repositorios en un servicio

Como hemos dicho, una vez hemos definida una entidad en ese módulo, somos capaces de inyectar el repositorio de esa entidad en un servicio. Para ello vamos a usar un nuevo decorador que nos proporciona la integración de Nest con TypeORM, llamado @InjectRepository().

Para poder usarlo vamos a necesitar importar dos declaraciones en el servicio:

import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

Una es el decorador que acabamos de mencionar y otra es la propia clase Repository que nos ofrece TypeORM.

Nuestro servicio ahora requiere la inyección del repositorio de producto, que vamos a realizar en el constructor.

@Injectable()
export class ProductsService {
  
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
  ) {}

}

Como puedes apreciar, en el constructor se declara una propiedad privada para este servicio llamada productsRepository. Esta propiedad obtendrá el repositorio de producto, algo que conseguimos:

Ahora ya estamos en condiciones de usar nuestro repositorio de producto dentro del servicio.

Invocando los métodos del repositorio en un servicio

Hasta ahora nuestro servicio de producto hacía uso de la memoria para almacenar los datos. Esto ya lo podemos cambiar y empezar a trabajar con bases de datos!! Para ello simplemente debemos ir invocando los métodos adecuados del repositorio inyectado en el servicio.

Por ejemplo, si queremos seleccionar elementos de la tabla usamos el método find() del repositorio.

Así nos quedaría el método getAll() del servicio. Ahora no devuelve el array que teníamos en memoria, sino que accede a los productos mediante find() del objeto productsRepository.

getAll() {
    return this.productsRepository.find();
}

Como puedes imaginar, si no enviamos parámetros al método find() nos devuelve todos los elementos de una entidad.

Existe un método del repositorio que podemos usar para conseguir un único elemento, llamado findOne(), que usamos en el método getId(). En el caso de findOne() tenemos que enviarle el identificador que permite seleccionar un elemento de la entidad de manera única, es decir, su clave primaria.

getId(id: number): Promise<Product> {
    return this.productsRepository.findOne(id);
}

Aparte de consultas para obtener datos de una entidad, con los repositorios podemos hacer todo tipo de operaciones, como crear elementos de la entidad o editarlos. Los vamos a usar en los próximos métodos.

Este sería el método que permite insertar un producto en la tabla de la entidad de producto.

async insert(body: ProductDto) {
  const product = this.productsRepository.create(body);
  await this.productsRepository.save(product);
  return product;
}

Este método tiene varios puntos que deben considerarse:

Es además clave entender por qué lo hemos tenido que firmar como async. Dado que la operación de salvado del producto es asíncrona, y queremos enviar como valor de retorno del método el producto salvado, necesitamos aplicar un await en la línea del método save(). La especificación async la colocamos porque todo método que usa await debe firmarse como async.

Ahora podemos ver otro método del servicio, que hace el guardado de un dato que se desea actualizar.

async update(id: number, body: any) {
  const userProduct = {
    id,
    ...body,
  }
  const product = await this.productsRepository.preload(userProduct);
  if(product) {
    return this.productsRepository.save(product);
  }
  throw new NotFoundException(`No se encuentra el producto ${id}`);
}

En este caso necesitamos actualizar un elemento de la base de datos. Esta operación la realizamos con el método preload() del repositorio. Hay algunos puntos que nos debemos fijar:

Igual que en el método anterior, este método es imporante declararlo como asíncrono para poder hacer uso de await dentro.

Por último, el método de borrado se ve de esta manera:

async delete(id: number) {
  const product = await this.productsRepository.findOne(id);
  if (product) {
    return this.productsRepository.remove(product);
  }
  throw new NotFoundException(`No se encuentra el producto ${id}`);
}

Así es como hemos definido, método a método, el servicio, incorporando el acceso a la base de datos a todas las operaciones que ofrece.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual