Nest
Tutorial Nest: Filtrados en consultas de repositorios
Aprende a filtrar datos en repositorios NestJS usando find() y QueryBuilder para consultas eficientes y seguras con TypeORM.
Aprende Nest y certifícateEn el desarrollo de aplicaciones, especialmente aquellas que interactúan con bases de datos, es común la necesidad de filtrar o refinar los resultados obtenidos a partir de ciertos criterios. NestJS, proporciona herramientas para facilitar esta tarea cuando se trabaja con bases de datos.
En este contexto, el filtrado de datos se refiere a la práctica de definir y aplicar criterios específicos para limitar los datos recuperados de una base de datos. Esto se logra principalmente a través del uso de repositorios.
Repositorios en NestJS
Un repositorio es un patrón de diseño que separa la lógica que recupera los datos y la lógica que accede a ellos.
En NestJS, cuando se utiliza el paquete @nestjs/typeorm
, se puede acceder a estos repositorios y utilizarlos para interactuar con la base de datos.
Filtrando Datos con el Método find()
El método find()
de un repositorio es tu primera herramienta para obtener datos. Acepta un objeto de opciones (FindManyOptions
) que permite especificar criterios de filtrado (where
), ordenación (order
), paginación (skip
, take
), y más.
Vamos a usar una entidad Usuario
para nuestros ejemplos:
// src/usuario/usuario.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('usuarios')
export class Usuario {
@PrimaryGeneratedColumn()
id: number;
@Column()
nombre: string;
@Column()
edad: number;
@Column({ default: true })
isActive: boolean;
}
Ahora, en tu servicio, puedes inyectar el repositorio y usar find()
:
// src/usuario/usuario.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, MoreThan, LessThan, Like, In, And, Or } from 'typeorm'; // Importa los operadores de TypeORM
import { Usuario } from './usuario.entity';
@Injectable()
export class UsuarioService {
constructor(
@InjectRepository(Usuario)
private readonly usuarioRepository: Repository<Usuario>,
) {}
// Obtener todos los usuarios
async findAll(): Promise<Usuario[]> {
return this.usuarioRepository.find();
}
// Filtrar: Obtener usuarios mayores de 18 años
async obtenerAdultos(): Promise<Usuario[]> {
return this.usuarioRepository.find({
where: {
edad: MoreThan(18), // Operador: mayor que
},
});
}
// Filtrar: Combinando múltiples criterios (AND implícito)
// Obtener usuarios mayores de 18 años Y cuyo nombre comience con 'A'
async obtenerAdultosConA(): Promise<Usuario[]> {
return this.usuarioRepository.find({
where: {
edad: MoreThan(18),
nombre: Like('A%'), // Operador: coincide con el patrón (ej. 'A%', '%xyz%', '%abc')
},
});
}
// Filtrar: Usando operadores lógicos (OR explícito)
// Obtener usuarios que tienen 18 años O cuyo nombre empiece con 'B'
async obtenerPorEdadOInicialB(): Promise<Usuario[]> {
return this.usuarioRepository.find({
where: [ // Usar un array para condiciones OR
{ edad: 18 },
{ nombre: Like('B%') }
]
// Alternativa con operador Or explícito (menos común para ORs simples):
// where: {
// [Or]: [
// { edad: 18 },
// { nombre: Like('B%') }
// ]
// }
});
}
// Filtrar: Usando el operador IN
// Obtener usuarios con IDs específicos
async obtenerPorIds(ids: number[]): Promise<Usuario[]> {
return this.usuarioRepository.find({
where: {
id: In(ids), // Operador: El ID está dentro del array de IDs
},
});
}
}
Otros operadores comunes de comparación disponibles para where
:
Equal(value)
: Compara si es igual avalue
.Not(value)
: Compara si es diferente devalue
.Between(from, to)
: Compara si está entrefrom
yto
(inclusive).IsNull()
: Compara si el valor esNULL
.And(condition1, condition2)
: Combina condiciones con AND.Or(condition1, condition2)
: Combina condiciones con OR.
Ordenando y Paginando Resultados
Para controlar el orden y el número de resultados, se usan las propiedades order
, take
y skip
en el método find()
:
order
: Define el orden de los resultados (ascendenteASC
o descendenteDESC
).take
: Limita el número de resultados devueltos (el equivalente aLIMIT
).skip
: Salta un número determinado de resultados (el equivalente aOFFSET
), útil para la paginación.
// Dentro de UsuarioService
async obtenerTop10AdultosOrdenados(): Promise<Usuario[]> {
return this.usuarioRepository.find({
where: {
edad: MoreThan(18),
},
order: {
nombre: 'ASC', // Ordenar por nombre de forma ascendente
},
take: 10, // Obtener solo los primeros 10 resultados
});
}
// Ejemplo de Paginación
async obtenerUsuariosPaginados(page: number = 1, limit: number = 10): Promise<Usuario[]> {
const skip = (page - 1) * limit; // Calcular cuántos registros saltar
return this.usuarioRepository.find({
order: { nombre: 'ASC' },
take: limit, // Número máximo de resultados por página
skip: skip, // Número de resultados a saltar (offset)
});
}
Filtrado Personalizado Utilizando QueryBuilder
Cuando las necesidades de filtrado se vuelven más complejas (ej., múltiples JOIN
s, subconsultas, agrupaciones complejas), el **QueryBuilder**
de TypeORM ofrece la máxima flexibilidad. Te permite construir consultas SQL de forma programática y con seguridad de tipos.
Para usar QueryBuilder
, lo invocas desde tu repositorio inyectado.
// Dentro de UsuarioService
// Obtener usuarios mayores de 18 años, excluyendo aquellos cuyo nombre comience con 'A'
async obtenerAdultosExcluyendoA(): Promise<Usuario[]> {
return this.usuarioRepository // Usa el repositorio inyectado
.createQueryBuilder('usuario') // Crea un QueryBuilder, 'usuario' es el alias para la entidad Usuario
.where('usuario.edad > :edad', { edad: 18 }) // Define una condición WHERE con parámetros seguros
.andWhere('usuario.nombre NOT LIKE :nombre', { nombre: 'A%' }) // Añade otra condición AND
.getMany(); // Ejecuta la consulta y obtiene múltiples resultados
}
// Combinar entidades relacionadas (Ejemplo: Obtener usuarios que tienen un libro específico)
// Suponemos que la entidad Usuario tiene una relación 'OneToMany' con una entidad 'Libro'
// y que la entidad Libro tiene una columna 'titulo'.
async obtenerUsuariosConLibro(tituloLibro: string): Promise<Usuario[]> {
return this.usuarioRepository
.createQueryBuilder('usuario')
// Realiza un JOIN con la relación 'libros' del usuario, y asigna el alias 'libro'
.innerJoinAndSelect('usuario.libros', 'libro', 'libro.titulo = :titulo', {
titulo: tituloLibro,
})
.getMany(); // Obtiene los usuarios que cumplen el criterio
}
Con createQueryBuilder
, tienes un control mucho más detallado sobre la consulta SQL generada, lo que es importantísimo para escenarios avanzados.
Buenas prácticas
- Reutilización de Código: Si encuentras que repites lógicas de filtrado complejas, considera encapsularlas en métodos de servicio específicos o incluso en métodos personalizados del repositorio.
- Evitar Filtrados en Memoria: Siempre que sea posible, realiza los filtros directamente en la base de datos (usando
where
,order
,QueryBuilder
). Recuperar todos los datos y filtrarlos después en la aplicación es ineficiente y consume más recursos. - Documentación: Las consultas pueden volverse complejas. Es esencial documentar qué hace cada método de filtrado y cuál es su propósito para facilitar la comprensión y el mantenimiento por parte de otros desarrolladores (y tu "yo futuro").
- Parámetros Seguros: Siempre utiliza parámetros (
:nombre
,:edad
) en tus consultas deQueryBuilder
para evitar ataques de inyección SQL. TypeORM se encarga de la seguridad.
Conclusión
NestJS y TypeORM ofrecen herramientas poderosas y flexibles para el filtrado de datos en tus aplicaciones. Desde las consultas sencillas utilizando el método find()
de los repositorios con operadores declarativos, hasta las construcciones más avanzadas con QueryBuilder
para un control granular, puedes manejar una amplia gama de escenarios y necesidades de acceso a datos. Dominar estas técnicas te permitirá construir aplicaciones eficientes, rápidas y seguras.
Otras lecciones de Nest
Accede a todas las lecciones de Nest y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción E Instalación Nestjs
Introducción Y Entorno
Comandos Nestjs Cli
Introducción Y Entorno
Métodos Get En Controladores
Controladores
Métodos Post En Controladores
Controladores
Métodos Put En Controladores
Controladores
Métodos Delete En Controladores
Controladores
Ejercicios de programación de Nest
Evalúa tus conocimientos de esta lección Filtrados en consultas de repositorios con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.