@2bbelmiro/typeorm-query-buider-helper
v1.0.3
Published
Utilitário fluente e seguro para construção de consultas complexas no TypeORM sem exposição direta de operadores SQL manuais.
Downloads
615
Maintainers
Readme
TypeORM QueryBuilder Helper
Wrapper fluente e seguro para o SelectQueryBuilder do TypeORM. Esta biblioteca oferece uma API intuitiva para construir consultas complexas sem escrever SQL manual, prevenindo erros de digitação e vulnerabilidades de injeção SQL automaticamente.
Suportado para TypeORM 0.3.x e posteriores com suporte a PostgreSQL, MySQL, SQLite e outros bancos de dados.
Instalação
npm install @2bbelmiro/typeorm-query-buider-helperTabela de Conteúdos
- Quick Start
- Métodos Estáticos
- Filtros e Condições
- Joins e Relações
- Selects e Campos Extras
- Paginação
- Agrupamento e Agregação
- Busca Avançada
- JSON
- Geolocalização
- SQL Customizado
- Inspeção de Query
- Boas Práticas
Quick Start
import { DataSource } from "typeorm";
import { QueryBuilderHelper } from "@2bbelmiro/typeorm-query-buider-helper";
import { User } from "./entities/User";
const dataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 5432,
username: "user",
password: "pass",
database: "app",
entities: [User],
});
await dataSource.initialize();
const qb = dataSource.getRepository(User).createQueryBuilder("user");
const users = await new QueryBuilderHelper(qb)
.whereEqual("user.active", true)
.whereLike("user.name", "miguel")
.orderBy("user.createdAt", "DESC")
.limit(20)
.offset(0)
.getMany();Nota: para uma melhor visualização com destaque de sintaxe colorido, veja a versão web desta documentação em docs/index.html — todos os blocos de código são realçados com Prism.js para facilitar leitura e cópia.
Métodos Estáticos
createQueryBuilder - Fábrica segura para helpers
Cria um QueryBuilderHelper diretamente a partir de um EntityManager. Útil em contextos de injeção de dependência e transações.
import { QueryBuilderHelper } from "@2bbelmiro/typeorm-query-buider-helper";
import { EntityManager } from "typeorm";
import { User } from "./entities/User";
export class UserService {
constructor(private manager: EntityManager) {}
async getActiveUsers() {
const helper = QueryBuilderHelper.createQueryBuilder(
this.manager,
User,
"user",
);
return await helper
.whereEqual("user.active", true)
.orderBy("user.createdAt", "DESC")
.getMany();
}
}transaction - Gerenciamento fluente de transações
Executa operações dentro de uma transação segura, fornecendo um EntityManager transacional.
import { QueryBuilderHelper } from "@2bbelmiro/typeorm-query-buider-helper";
import { EntityManager } from "typeorm";
async function updateUserStatus(manager: EntityManager, userId: number) {
return await QueryBuilderHelper.transaction(manager, async (txManager) => {
// Todas as operações aqui ocorrem dentro de uma transação
const helper = QueryBuilderHelper.createQueryBuilder(
txManager,
User,
"user",
);
const user = await helper.whereEqual("user.id", userId).getOne();
if (user) {
user.status = "updated";
await txManager.save(user);
}
return user;
});
}EntityManagerHelper - API de manager e queries unificadas
Fornece um facade leve sobre EntityManager, expondo CRUD comuns e criando QueryBuilderHelper para consultas.
import { EntityManagerHelper } from "@2bbelmiro/typeorm-query-buider-helper";
import { EntityManager } from "typeorm";
export class UserService {
private helper: EntityManagerHelper;
constructor(manager: EntityManager) {
this.helper = new EntityManagerHelper(manager);
}
async getActiveUsers() {
return await this.helper
.createQueryBuilder(User, "user")
.whereEqual("user.active", true)
.orderBy("user.createdAt", "DESC")
.getMany();
}
}EntityManagerHelper - detalhes e exemplos
EntityManagerHelper é uma pequena fachada que encapsula um EntityManager injetado, expõe métodos CRUD comuns (find, save, update, delete, insert, upsert, etc.) e facilita a criação de QueryBuilderHelper através de createQueryBuilder(...).
Exemplo rápido de uso em serviço:
import { EntityManagerHelper } from "@2bbelmiro/typeorm-query-buider-helper";
import { EntityManager } from "typeorm";
import { User } from "./entities/User";
export class UserService {
private helper: EntityManagerHelper;
constructor(manager: EntityManager) {
this.helper = new EntityManagerHelper(manager);
}
async getActiveUsers() {
// Cria um QueryBuilderHelper a partir do manager interno
return await this.helper
.createQueryBuilder(User, "user")
.whereEqual("user.active", true)
.orderBy("user.createdAt", "DESC")
.getMany();
}
async saveUser(user: Partial<User>) {
// Encaminha para manager.save mas via fachada
return await this.helper.save(user as any);
}
}Veja a documentação web com blocos realçados em docs/index.html para exemplos adicionais.
Filtros e Condições
Igualdade e Desigualdade
const results = await new QueryBuilderHelper(qb)
.whereEqual("user.email", "[email protected]")
.whereNotEqual("user.status", "inactive")
.orWhereEqual("user.role", "admin")
.getMany();Comparações Numéricas
const results = await new QueryBuilderHelper(qb)
.whereGreaterThan("user.age", 18)
.whereGreaterThanOrEqual("user.score", 100)
.whereLessThan("user.credits", 50)
.whereLessThanOrEqual("user.balance", 1000)
.orWhereGreaterThan("user.reputation", 500)
.getMany();IN / NOT IN
const results = await new QueryBuilderHelper(qb)
.whereIn("user.id", [1, 2, 3, 4, 5])
.whereNotIn("user.role", ["guest", "banned", "suspended"])
.orWhereIn("user.status", ["active", "pending"])
.getMany();LIKE / Busca Textual
const results = await new QueryBuilderHelper(qb)
.whereLike("user.name", "joao")
.whereNotLike("user.email", "temp-")
.orWhereLike("user.bio", "%developer%")
.getMany();Nulos
const results = await new QueryBuilderHelper(qb)
.whereNull("user.deletedAt")
.orWhereNotNull("user.lastLogin")
.whereNull("user.verifiedAt")
.getMany();Booleanos
const results = await new QueryBuilderHelper(qb)
.whereBoolean("user.isVerified", true)
.orWhereBoolean("user.isPremium", false)
.getMany();Between (Intervalos)
const results = await new QueryBuilderHelper(qb)
.whereBetween("user.age", 18, 65)
.whereBetween(
"user.createdAt",
new Date("2026-01-01"),
new Date("2026-12-31"),
)
.getMany();Datas
const results = await new QueryBuilderHelper(qb)
.whereDate("user.updatedAt", new Date("2026-07-03"))
.whereDateRange(
"user.createdAt",
new Date("2026-01-01"),
new Date("2026-12-31"),
)
.whereNotDateRange(
"user.deletedAt",
new Date("2020-01-01"),
new Date("2020-12-31"),
)
.getMany();Joins e Relações
Left Join
const results = await new QueryBuilderHelper(qb)
.leftJoin("user.profile", "profile")
.whereEqual("profile.country", "BR")
.getMany();Left Join com Select
const results = await new QueryBuilderHelper(qb)
.leftJoinAndSelect("user.profile", "profile")
.leftJoinAndSelect("user.posts", "posts")
.whereEqual("profile.active", true)
.addOrderBy("posts.createdAt", "DESC")
.getMany();Left Join com Mapeamento
const results = await new QueryBuilderHelper(qb)
.leftJoinAndMapOne("user.profile", "user.profile", "profile")
.leftJoinAndMapMany(
"user.posts",
"user.posts",
"posts",
"posts.published = :published",
{ published: true },
)
.getMany();Inner Join
const results = await new QueryBuilderHelper(qb)
.innerJoinAndSelect("user.company", "company")
.whereEqual("company.active", true)
.getMany();Selects e Campos Extras
Traga dados adicionais que não fazem parte da entidade original. Perfeito para cálculos, agregações e dados de joins.
const results = await new QueryBuilderHelper(qb)
.leftJoin("user.profile", "profile")
.leftJoin("user.posts", "posts")
.addSelectContext("COUNT(posts.id)", "postCount")
.addSelectContext("profile.city", "userCity")
.addSelectContext("profile.country", "userCountry")
.groupBy("user.id")
.getRawMany();
// Resultado: { id, name, email, postCount, userCity, userCountry }Use select(...) para criar colunas calculadas personalizadas:
const results = await new QueryBuilderHelper(qb)
.select("YEAR(user.createdAt)", "year")
.select("COUNT(user.id)", "total")
.groupBy("YEAR(user.createdAt)")
.orderBy("year", "DESC")
.getRawMany();Paginação
A biblioteca oferece paginação estruturada com metadados completos.
const result = await new QueryBuilderHelper(qb)
.whereEqual("user.active", true)
.orderBy("user.createdAt", "DESC")
.paginate({
page: 2,
limit: 20,
});
console.log(result.items); // T[]
console.log(result.meta.currentPage); // 2
console.log(result.meta.totalPages); // Calculado automaticamente
console.log(result.meta.totalItems); // Total de registros
console.log(result.meta.itemCount); // Registros nesta páginaAgrupamento e Agregação
Group By com Having
const results = await new QueryBuilderHelper(qb)
.groupBy(["user.role", "user.department"])
.having("COUNT(user.id) > :minCount", { minCount: 5 })
.having("AVG(user.salary) > :minSalary", { minSalary: 50000 })
.orderBy("user.role", "ASC")
.getMany();Agregação com Subconsultas
const results = await new QueryBuilderHelper(qb)
.sum((sub) => {
sub.whereEqual("user.active", true);
}, "activeCount")
.sum((sub) => {
sub.whereEqual("user.role", "admin");
}, "adminCount")
.getMany();Busca Avançada
Pesquise em vários campos simultaneamente com proteção contra SQL injection.
// Busca em múltiplos campos
const results = await new QueryBuilderHelper(qb)
.whereSearch(["user.name", "user.email", "profile.city"], "maria")
.getMany();
// Busca negativa
const results = await new QueryBuilderHelper(qb)
.whereNotSearch(["user.status"], "inactive")
.orWhereSearch(["user.role"], "premium")
.getMany();JSON
Acesse e filtre campos JSON com suporte automático para PostgreSQL e MySQL.
const results = await new QueryBuilderHelper(qb)
.whereJsonContains("user.metadata", "address.city", "Sao Paulo")
.whereJsonContains("user.settings", "notifications.email", true)
.orWhereNotJsonContains("user.metadata", "status", "inactive")
.getMany();Geolocalização
Calcule distâncias e filtre por raio com geolocalização.
// Calcular distância (em metros, usando ST_Distance_Sphere)
const results = await new QueryBuilderHelper(qb)
.distancia(
"user.longitude",
"user.latitude",
-46.6333, // Longitude (São Paulo)
-23.5505, // Latitude (São Paulo)
"distancia", // Alias do resultado
)
.orderBy("distancia", "ASC")
.getMany();
// Filtrar por raio (5km = 5000 metros)
const results = await new QueryBuilderHelper(qb)
.distanciaRaio(
"user.longitude",
"user.latitude",
-46.6333,
-23.5505,
5000, // Raio em metros
)
.getMany();SQL Customizado
Para casos que requerem SQL manual, use operadores customizados:
const results = await new QueryBuilderHelper(qb)
.whereCustom("LOWER(user.name) LIKE LOWER(:term)", { term: "%maria%" })
.orWhereCustom("user.age BETWEEN :min AND :max", { min: 18, max: 65 })
.getMany();Grupos Lógicos (Brackets)
Crie lógica complexa com agrupamento de condições:
const results = await new QueryBuilderHelper(qb)
.whereEqual("user.active", true)
.andGroup((group) => {
group
.whereEqual("user.role", "admin")
.orWhereEqual("user.role", "moderator");
})
.orGroup((group) => {
group.whereGreaterThan("user.reputation", 1000).whereNull("user.bannedAt");
})
.getMany();Inspeção de Query
Visualize a SQL gerada e os parâmetros para depuração:
const helper = new QueryBuilderHelper(qb)
.whereEqual("user.name", "Alice")
.whereGreaterThan("user.age", 18);
console.log(helper.getSql());
// { sql: "SELECT ... WHERE user.name = :...", parameters: { ... } }
console.log(helper.getSqlCompiled());
// SELECT ... WHERE user.name = 'Alice' AND user.age > 18Boas Práticas
- Reutilize builders: Crie helpers reutilizáveis para queries frequentes
- Use transações: Aproveite
QueryBuilderHelper.transaction()para operações múltiplas - Encadeie métodos: A API fluente facilita leitura e manutenção
- Retorne ao nativo: Use
helper.build()para customizações pontuais - Valide entrada: Sempre valide dados do usuário antes de passar ao helper
- Inspecione SQL: Use
getSqlCompiled()para debugar queries complexas
Observações Importantes
- O helper é um wrapper leve que preserva o
SelectQueryBuilderoriginal - Retorne ao builder nativo com
helper.build()para operações não suportadas - Todos os parâmetros são automaticamente escapados contra SQL Injection
- A biblioteca suporta TypeORM 0.3.x e posteriores
- Compatível com PostgreSQL, MySQL, SQLite e outros bancos suportados pelo TypeORM
