npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hemia/db-manager

v0.0.12

Published

Hemia Database Manager

Readme

@hemia/db-manager

Repositorio genérico y flexible para manejar operaciones de base de datos en proyectos Hemia. Compatible con MongoDB (NoSQL), SQL (MySQL/MSSQL) y PostgreSQL con Drizzle ORM, este paquete ofrece una capa de abstracción para repositorios basados en modelos, consultas crudas y stored procedures.

npm version License: MIT

📋 Tabla de Contenidos


Instalación

npm install @hemia/db-manager   

Dependencias adicionales según tu base de datos:

# Para MongoDB
npm install mongoose

# Para MySQL/MSSQL
npm install sequelize mysql2 # o mssql

# Para PostgreSQL con Drizzle
npm install drizzle-orm postgres

Componentes incluidos

NoSQLRepository<T>

Repositorio genérico para colecciones MongoDB usando Mongoose.

Características:

  • Validaciones basadas en esquemas Mongoose
  • CRUD genérico (create, find, findOne, update, delete, etc.)
  • Transacciones (startSession, commitTransaction, abortTransaction, withTransaction)
  • Soporte para aggregate y múltiples validaciones (validateSync*)

Ejemplo:

const repo = new NoSQLRepository<UserDocument>(mongoConnector, 'users', userSchema);
const user = await repo.create({ name: 'Juan' });

SQLRepository<M>

Repositorio tipado basado en Sequelize para una entidad específica.

Características:

  • Select, insert, update, delete por ID o filtros
  • Control de cierre de conexión
  • Transacciones (withTransaction)
  • Tipado fuerte con InferAttributes<M>

Ejemplo:

const userRepo = new SQLRepository<UserModel>(sqlConnector, 'users');
const users = await userRepo.find({ active: true });

DrizzleRepository<T> 🆕

Repositorio moderno para PostgreSQL usando Drizzle ORM con DOS formas de uso:

🎯 Opción 1: API Abstracta (Compatible con Sequelize)

Sintaxis familiar con inferencia automática de columnas para migraciones fáciles:

import { DrizzlePostgresConnector } from '@hemia/db-connector';
import { DrizzleRepository } from '@hemia/db-manager';
import { pgTable, serial, varchar, integer } from 'drizzle-orm/pg-core';

// Definir schema
const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 255 }).notNull(),
  email: varchar('email', { length: 255 }).notNull(),
  age: integer('age'),
  status: varchar('status', { length: 50 })
});

type User = typeof users.$inferSelect;

// Configurar
const connector = new DrizzlePostgresConnector(credentials);
await connector.connect();
connector.registerTableSchema('users', users);

// ⚠️ IMPORTANTE: Pasar el schema para usar API Nativa
const repository = new DrizzleRepository<User, number, typeof users>(
  connector,
  'users',
  'id',
  users // ⬅️ Necesario para withDb()
);

// ✅ API abstracta con inferencia de columnas
const adults = await repository.find({ 
  age: { $gte: 18 },      // ✅ TypeScript infiere las columnas
  status: 'active'         // ✅ Autocompletado
});

const user = await repository.findOne({ email: '[email protected]' });
await repository.updateById(1, { name: 'Updated' });
await repository.deleteById(1);

// ❌ Error de compilación si usas columnas inexistentes
const invalid = await repository.find({ 
  nonExistentColumn: 'value' // ❌ Error de TypeScript
});

🚀 Opción 2: API Nativa de Drizzle (Queries Complejas)

Usa withDb() para JOINs, agregaciones y queries complejas:

import { eq, and, gt, count, sum } from 'drizzle-orm';

// ✅ LEFT JOIN - Maneja la conexión automáticamente
const usersWithPosts = await repository.withDb(async (db, users) => {
  return await db
    .select({
      userId: users.id,
      userName: users.name,
      postTitle: posts.title
    })
    .from(users)
    .leftJoin(posts, eq(users.id, posts.userId))
    .where(eq(users.status, 'active'));
});

// ✅ INNER JOIN con filtros
const activeUsersWithPosts = await repository.withDb(async (db, users) => {
  return await db
    .select()
    .from(users)
    .innerJoin(posts, eq(users.id, posts.userId))
    .where(and(
      eq(users.status, 'active'),
      gt(posts.views, 100)
    ));
});

// ✅ Agregaciones (COUNT, SUM, AVG)
const stats = await repository.withDb(async (db, users) => {
  return await db
    .select({
      userId: users.id,
      userName: users.name,
      totalPosts: count(posts.id),
      totalViews: sum(posts.views)
    })
    .from(users)
    .leftJoin(posts, eq(users.id, posts.userId))
    .groupBy(users.id, users.name);
});

// ✅ Seleccionar columnas específicas
const selected = await repository.withDb(async (db, users) => {
  return await db
    .select({
      id: users.id,
      name: users.name
      // email NO se incluye
    })
    .from(users);
});

// ✅ INSERT con returning
const newUser = await repository.withDb(async (db, users) => {
  const [user] = await db
    .insert(users)
    .values({
      name: 'Jane Doe',
      email: '[email protected]',
      age: 30
    })
    .returning();
  return user;
});

⚠️ Importante: getDb() vs withDb()

// ❌ getDb() NO maneja la conexión automáticamente
const db = repository.getDb();
const result = await db.select().from(users); // ❌ Error: Base de datos no conectada

// ✅ withDb() maneja la conexión automáticamente
const result = await repository.withDb(async (db, users) => {
  return await db.select().from(users); // ✅ Funciona correctamente
});

Características de DrizzleRepository:

  • Dos APIs en una: Abstracta (fácil) + Nativa (poderosa)
  • Inferencia de columnas: TypeScript autocompleta las propiedades del modelo
  • Tipado TypeScript 100% con autocompletado IDE
  • withDb(): Maneja la conexión automáticamente para queries complejas
  • 3-4x más rápido que Sequelize en queries complejas
  • Bundle 50% más pequeño
  • Transacciones automáticas con commit/rollback
  • Operadores avanzados: $gt, $gte, $like, $ilike, $in, etc.
  • JOINs (INNER, LEFT, RIGHT, FULL) con tipado completo
  • Agregaciones (COUNT, SUM, AVG) nativas
  • Migrations con Drizzle Kit

Operadores Disponibles en API Abstracta:

// Operadores de comparación
{ age: { $eq: 18 } }      // age = 18
{ age: { $ne: 18 } }      // age != 18
{ age: { $gt: 18 } }      // age > 18
{ age: { $gte: 18 } }     // age >= 18
{ age: { $lt: 18 } }      // age < 18
{ age: { $lte: 18 } }     // age <= 18

// Operadores de texto
{ name: { $like: 'John%' } }    // LIKE 'John%'
{ name: { $ilike: 'john%' } }   // ILIKE 'john%' (case-insensitive)

// Operadores de arrays
{ id: { $in: [1, 2, 3] } }      // id IN (1, 2, 3)
{ id: { $notIn: [1, 2] } }      // id NOT IN (1, 2)

// Operadores NULL
{ email: { $isNull: true } }    // email IS NULL
{ email: { $isNotNull: true } } // email IS NOT NULL

Comparación de APIs:

┌────────────────────┬────────────────────────┬────────────────────────────┐
│ Característica     │ API Abstracta          │ withDb()            │
├────────────────────┼────────────────────────┼────────────────────────────┤
│ Sintaxis           │ { age: { $gte: 18 } }  │ gt(users.age, 18)          │
│ CRUD Simple        │ ✓✓ Ideal               │ ✓ Más verboso              │
│ Inferencia columnas│ ✓✓ Automática          │ ✓✓ Automática              │
│ Queries Complejas  │ ✗ No soportado         │ ✓✓ Nativo tipado           │
│ JOINs              │ ✗ No soportado         │ ✓✓ Tipado completo         │
│ Agregaciones       │ ✗ No soportado         │ ✓✓ COUNT, SUM, AVG, etc.   │
│ Selección columnas │ ✗ Siempre SELECT *     │ ✓✓ Columnas específicas    │
│ Migración fácil    │ ✓✓ Muy fácil           │ ✓ Curva aprendizaje        │
│ Performance        │ ✓ Buena                │ ✓✓ Óptima (3-4x)           │
│ Manejo conexión    │ ✓✓ Automático          │ ✓✓ Automático              │
└────────────────────┴────────────────────────┴────────────────────────────┘

💡 Mejor práctica: ¡Combina ambas APIs según tus necesidades!

// ✅ API abstracta para CRUD simple
const user = await repository.findById(123);
const activeUsers = await repository.find({ status: 'active' });
const count = await repository.count({ age: { $gte: 18 } });

// ✅ withDb() para queries complejas
const stats = await repository.withDb(async (db, users) => {
  return await db
    .select({
      status: users.status,
      count: count(users.id),
      avgAge: avg(users.age)
    })
    .from(users)
    .groupBy(users.status);
});

Métodos Disponibles:

API Abstracta (Queries Simples):

  • find(filter?, options?) - Buscar múltiples registros
  • findOne(filter) - Buscar un único registro
  • findById(id) - Buscar por ID
  • findByIds(ids[]) - Buscar múltiples por IDs
  • insert(data) - Insertar un registro
  • insertMany(data[]) - Insertar múltiples registros
  • update(filter, data) - Actualizar registros
  • updateById(id, data) - Actualizar por ID
  • delete(filter) - Eliminar registros
  • deleteById(id) - Eliminar por ID
  • count(filter?) - Contar registros
  • exists(id) - Verificar si existe
  • query(sql, params?) - Query SQL raw
  • withTransaction(fn) - Ejecutar en transacción

API Nativa (Queries Complejas):

  • withDb(fn) - Ejecuta con conexión automática ✨ NUEVO
  • getDb() - Obtiene instancia de Drizzle (sin manejo de conexión)
  • getTableSchema() - Obtiene el schema de la tabla
  • getConnector() - Obtiene el conector

📖 Documentación completa: DRIZZLE_README.md
🔗 Guía de JOINs: DRIZZLE_JOINS_GUIDE.md
Solución de errores: SOLUCION_CONEXION.md
💻 Ejemplos completos: examples/joins-example.ts
🚀 Inicio rápido: examples/quick-start.ts

Comparación de Repositories

| Repository | Base de Datos | ORM/Driver | Uso Principal | Performance | Tipado | |------------|---------------|------------|---------------|-------------|--------| | NoSQLRepository | MongoDB | Mongoose | Documentos flexibles | ⚡⚡ | ✓ | | SQLRepository | MySQL/MSSQL | Sequelize | Relacional tradicional | ⚡⚡ | ✓ | | DrizzleRepository | PostgreSQL | Drizzle ORM | Relacional moderno | ⚡⚡⚡⚡ | ✓✓ | | SQLRawRepository | MySQL/MSSQL | Raw SQL | Queries personalizadas | ⚡⚡⚡ | ✗ | | OLAPRepository | Cualquiera | Adaptable | Análisis de datos | ⚡⚡⚡ | ✓ |

SQLRawRepository

Repositorio especializado en consultas SQL crudas y stored procedures.

Características:

  • Ejecuta queries SQL simples (SELECT, INSERT, UPDATE, etc.)
  • Llamada a procedimientos almacenados con parámetros (sp)
  • Tipado de resultado y parámetros

Ejemplo:

const rawRepo = new SQLRawRepository(sqlConnector);
const result = await rawRepo.sp('get_active_users', new StoredProcedureParamsBuilder().add('status', 'active'));

OLAPRepository

Repositorio especializado para operaciones OLAP (Online Analytical Processing).

Características:

  • Operaciones de análisis y reporting
  • Consultas optimizadas para lectura
  • Agregaciones y análisis de datos

Guías y Documentación

📚 Documentación por Repository

💡 Tipado Completo con TypeScript

El DrizzleRepository incluye helpers para tipado automático:

import { 
  DrizzleRepository, 
  TypedDrizzleRepository,
  InferSelectModel,
  InferInsertModel 
} from '@hemia/db-manager';
import { pgTable, serial, varchar } from 'drizzle-orm/pg-core';

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 255 }).notNull(),
  email: varchar('email', { length: 255 }).notNull()
});

// Opción 1: Tipado manual
const repo1 = new DrizzleRepository<
  typeof users.$inferSelect,
  number,
  typeof users
>(connector, 'users', 'id', users);

// Opción 2: Con helper (más simple)
const repo2: TypedDrizzleRepository<typeof users> = 
  new DrizzleRepository(connector, 'users', 'id', users);

// Ambos tienen tipado completo
const db = repo2.getDb();        // PostgresJsDatabase tipado
const schema = repo2.getTableSchema(); // typeof users

// Queries con autocompletado completo
const users = await db.select().from(schema); // User[] tipado

Para más ejemplos, ver examples/drizzle-complete-example.ts.

🧪 Tests

npm test

Los tests incluyen ejemplos de uso para cada repository.


💡 Ejemplos Prácticos de Uso

Ejemplo 1: CRUD Completo con DrizzleRepository

import { DrizzlePostgresConnector } from '@hemia/db-connector';
import { DrizzleRepository } from '@hemia/db-manager';
import { pgTable, serial, varchar, integer, timestamp } from 'drizzle-orm/pg-core';

// 1. Definir schema
const products = pgTable('products', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 255 }).notNull(),
  price: integer('price').notNull(),
  stock: integer('stock').default(0),
  category: varchar('category', { length: 100 }),
  createdAt: timestamp('created_at').defaultNow()
});

type Product = typeof products.$inferSelect;

// 2. Configurar repository
const connector = new DrizzlePostgresConnector({
  host: 'localhost',
  database: 'store_db',
  user: 'postgres',
  password: 'password'
});

await connector.connect();
const productRepo = new DrizzleRepository<Product>(
  connector, 'products', 'id', products
);

// 3. Crear productos
const laptop = await productRepo.insert({
  name: 'Laptop Dell XPS',
  price: 1200,
  stock: 10,
  category: 'electronics'
});

// 4. Buscar con filtros
const affordableElectronics = await productRepo.find({
  category: 'electronics',
  price: { $lte: 1500 },
  stock: { $gt: 0 }
});

// 5. Actualizar
await productRepo.updateById(laptop.id, { stock: 8 });

// 6. Eliminar productos sin stock
await productRepo.delete({ stock: { $lte: 0 } });

Ejemplo 2: Sistema de Autenticación

const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  username: varchar('username', { length: 100 }).notNull(),
  passwordHash: varchar('password_hash', { length: 255 }).notNull(),
  role: varchar('role', { length: 50 }).default('user'),
  isActive: boolean('is_active').default(true),
  lastLogin: timestamp('last_login')
});

const userRepo = new DrizzleRepository(connector, 'users', 'id', users);

// Registrar usuario
async function registerUser(email: string, username: string, password: string) {
  const passwordHash = await bcrypt.hash(password, 10);
  return await userRepo.insert({ email, username, passwordHash });
}

// Login
async function loginUser(email: string, password: string) {
  const user = await userRepo.findOne({ email, isActive: true });
  
  if (!user || !await bcrypt.compare(password, user.passwordHash)) {
    throw new Error('Credenciales inválidas');
  }
  
  await userRepo.updateById(user.id, { lastLogin: new Date() });
  return user;
}

// Buscar administradores activos
const admins = await userRepo.find({ role: 'admin', isActive: true });

Ejemplo 3: Queries Complejas con JOINs

import { eq, and, sql } from 'drizzle-orm';

const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: varchar('title', { length: 255 }),
  authorId: integer('author_id'),
  status: varchar('status', { length: 50 })
});

const comments = pgTable('comments', {
  id: serial('id').primaryKey(),
  postId: integer('post_id'),
  userId: integer('user_id'),
  content: text('content')
});

const postRepo = new DrizzleRepository(connector, 'posts', 'id', posts);
const db = postRepo.getDb();

// Posts con estadísticas
const postsWithStats = await db
  .select({
    id: posts.id,
    title: posts.title,
    author: users.username,
    commentCount: sql<number>`count(${comments.id})`.as('comments'),
    viewCount: posts.viewCount
  })
  .from(posts)
  .leftJoin(users, eq(posts.authorId, users.id))
  .leftJoin(comments, eq(posts.id, comments.postId))
  .where(eq(posts.status, 'published'))
  .groupBy(posts.id, users.username)
  .orderBy(sql`comments DESC`)
  .limit(10);

Ejemplo 4: Transacciones

// Transferencia de stock entre productos
await productRepo.withTransaction(async (tx) => {
  // Reducir stock del producto origen
  await productRepo.update(
    { id: 1 },
    { stock: sql`stock - 5` }
  );
  
  // Aumentar stock del producto destino
  await productRepo.update(
    { id: 2 },
    { stock: sql`stock + 5` }
  );
  
  // Registrar movimiento
  await movementRepo.insert({
    fromProduct: 1,
    toProduct: 2,
    quantity: 5,
    date: new Date()
  });
  
  // Si algo falla, todo se revierte automáticamente
});

Ejemplo 5: Paginación con Filtros Dinámicos

async function searchProducts(filters, pagination) {
  const where: Record<string, any> = {};
  
  if (filters.category) where.category = filters.category;
  if (filters.minPrice) where.price = { $gte: filters.minPrice };
  if (filters.search) where.name = { $ilike: `%${filters.search}%` };
  if (filters.inStock) where.stock = { $gt: 0 };
  
  const offset = (pagination.page - 1) * pagination.pageSize;
  
  const products = await productRepo.find(where, {
    limit: pagination.pageSize,
    offset
  });
  
  const total = await productRepo.count(where);
  
  return {
    data: products,
    pagination: {
      page: pagination.page,
      pageSize: pagination.pageSize,
      total,
      totalPages: Math.ceil(total / pagination.pageSize)
    }
  };
}

// Usar
const result = await searchProducts(
  { category: 'electronics', minPrice: 100, search: 'laptop' },
  { page: 1, pageSize: 20 }
);

Ejemplo 6: Soft Delete (Eliminación Lógica)

const tasks = pgTable('tasks', {
  id: serial('id').primaryKey(),
  title: varchar('title', { length: 255 }),
  userId: integer('user_id'),
  deletedAt: timestamp('deleted_at')
});

const taskRepo = new DrizzleRepository(connector, 'tasks', 'id', tasks);

// Buscar solo tareas activas (no eliminadas)
async function findActiveTasks(userId: number) {
  return await taskRepo.find({
    userId,
    deletedAt: null
  });
}

// Soft delete (no elimina físicamente)
async function softDeleteTask(taskId: number) {
  return await taskRepo.updateById(taskId, {
    deletedAt: new Date()
  });
}

// Restaurar tarea
async function restoreTask(taskId: number) {
  return await taskRepo.updateById(taskId, {
    deletedAt: null
  });
}

// Eliminar permanentemente tareas antiguas (30+ días)
const db = taskRepo.getDb();
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 30);

await db
  .delete(tasks)
  .where(and(
    isNotNull(tasks.deletedAt),
    lt(tasks.deletedAt, cutoffDate)
  ));

Ejemplo 7: Agregaciones y Reportes

// Reporte de ventas por categoría
const salesReport = await db
  .select({
    category: products.category,
    totalProducts: sql<number>`count(*)`,
    avgPrice: sql<number>`avg(${products.price})`,
    totalStock: sql<number>`sum(${products.stock})`,
    minPrice: sql<number>`min(${products.price})`,
    maxPrice: sql<number>`max(${products.price})`
  })
  .from(products)
  .groupBy(products.category)
  .having(sql`count(*) > 5`)
  .orderBy(sql`totalStock DESC`);

console.log('Reporte de ventas:', salesReport);

Ejemplo 8: Búsqueda con Múltiples Tablas (NoSQL)

import { NoSQLRepository } from '@hemia/db-manager';
import { Schema } from 'mongoose';

const orderSchema = new Schema({
  userId: String,
  products: [{ productId: String, quantity: Number, price: Number }],
  total: Number,
  status: String,
  createdAt: { type: Date, default: Date.now }
});

const orderRepo = new NoSQLRepository(mongoConnector, 'orders', orderSchema);

// Buscar órdenes por usuario
const userOrders = await orderRepo.find({ userId: '12345' });

// Agregar nueva orden
const newOrder = await orderRepo.create({
  userId: '12345',
  products: [
    { productId: 'prod1', quantity: 2, price: 100 },
    { productId: 'prod2', quantity: 1, price: 250 }
  ],
  total: 450,
  status: 'pending'
});

// Actualizar estado
await orderRepo.update({ _id: newOrder._id }, { status: 'completed' });

// Aggregation pipeline
const orderStats = await orderRepo.aggregate([
  { $match: { status: 'completed' } },
  {
    $group: {
      _id: '$userId',
      totalOrders: { $sum: 1 },
      totalSpent: { $sum: '$total' }
    }
  },
  { $sort: { totalSpent: -1 } },
  { $limit: 10 }
]);

SQLRawRepository

  • @hemia/db-connector instalado y configurado
  • Base de datos MongoDB o MySQL/MSSQL activa

Tipos comunes utilizados

interface CredentialsConnection {
  host: string;
  port?: number;
  user?: string;
  password?: string;
  database: string;
  dialect?: 'mysql' | 'mssql' | 'postgresql';
}

Características Destacadas

🚀 DrizzleRepository - Lo Mejor de Dos Mundos

  • API Abstracta con Inferencia: Sintaxis familiar + autocompletado de columnas
  • withDb(): Manejo automático de conexión para queries complejas ✨ NUEVO
  • API Nativa: Acceso completo a Drizzle con tipado 100%
  • JOINs Tipados: INNER, LEFT, RIGHT, FULL con tipado completo
  • Agregaciones: COUNT, SUM, AVG, MAX, MIN nativos
  • Performance: 3-4x más rápido que Sequelize
  • Type Safety: Inferencia automática de columnas del modelo
  • Bundle Size: 50% más pequeño que alternativas
  • Zero Overhead: Todo optimizado en compile-time
  • Operadores Avanzados: $gt, $gte, $like, $ilike, $in, $isNull, etc.

🔧 Query Manager

Todos los repositories incluyen:

  • ✅ Manejo automático de conexiones
  • ✅ Gestión centralizada de errores
  • ✅ Soporte para transacciones
  • ✅ Validaciones integradas
  • ✅ Logging y debugging
  • ✅ Tipado TypeScript completo

📚 Documentación Adicional


Roadmap

  • [x] NoSQLRepository para MongoDB
  • [x] SQLRepository para MySQL/MSSQL
  • [x] DrizzleRepository para PostgreSQL
  • [x] Soporte para API nativa de Drizzle
  • [x] Inferencia automática de columnas en filtros ✨ NUEVO
  • [x] withDb() para manejo automático de conexión ✨ NUEVO
  • [x] Soporte completo para JOINs (INNER, LEFT, RIGHT, FULL) ✨ NUEVO
  • [x] Agregaciones nativas (COUNT, SUM, AVG) ✨ NUEVO
  • [ ] DrizzleRepository para MySQL
  • [ ] DrizzleRepository para SQLite
  • [ ] Cache layer integrado
  • [ ] Métricas y observabilidad

❓ Preguntas Frecuentes

¿Cuándo usar DrizzleRepository vs SQLRepository?

  • DrizzleRepository: PostgreSQL moderno, máximo performance, tipado completo
  • SQLRepository: MySQL/MSSQL, compatibilidad con Sequelize

¿Puedo hacer JOINs con find() o findOne()?

No. La API abstracta (find, findOne) no soporta JOINs. Usa withDb():

const result = await repository.withDb(async (db, users) => {
  return await db
    .select()
    .from(users)
    .leftJoin(posts, eq(users.id, posts.userId));
});

¿Por qué obtengo "Base de datos no conectada" al usar getDb()?

getDb() no maneja la conexión automáticamente. Usa withDb() en su lugar:

// ❌ Error
const db = repository.getDb();
const result = await db.select().from(users);

// ✅ Correcto
const result = await repository.withDb(async (db, users) => {
  return await db.select().from(users);
});

¿Cómo hago un LEFT JOIN?

const result = await repository.withDb(async (db, users) => {
  return await db
    .select()
    .from(users)
    .leftJoin(posts, eq(users.id, posts.userId));
});

¿Puedo seleccionar columnas específicas?

Sí, con withDb():

const result = await repository.withDb(async (db, users) => {
  return await db
    .select({
      id: users.id,
      name: users.name
    })
    .from(users);
});

¿Puedo mezclar API Abstracta y withDb()?

Sí! Esa es la mejor práctica:

// Queries simples con API Abstracta
const user = await repository.findById(123);

// Queries complejas con withDb()
const stats = await repository.withDb(async (db, users) => {
  return await db.select({ count: count(users.id) }).from(users);
});

📚 Más información: DRIZZLE_README.md


Contribuir

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

Soporte


Changelog

v2.1.0 (2025-12-18) 🆕 NUEVO

  • Inferencia automática de columnas en filtros con Filter<T>
  • Nuevo método withDb() - Maneja conexión automáticamente
  • JOINs tipados: INNER, LEFT, RIGHT, FULL con tipado completo
  • Agregaciones: COUNT, SUM, AVG, MAX, MIN
  • Selección de columnas específicas con tipado
  • 📖 Documentación completa de JOINs y queries complejas
  • 🔧 Solución al error "Base de datos no conectada"
  • 💻 Ejemplos actualizados y nuevas guías

v2.0.0 (2025-12-13)

  • ✨ Nuevo DrizzleRepository para PostgreSQL
  • ✨ Soporte para API nativa de Drizzle ORM
  • ✨ Operadores personalizados ($gte, $like, etc.)
  • 📖 Documentación completa y ejemplos
  • 🎯 Tipado TypeScript mejorado

v1.x.x

  • NoSQLRepository para MongoDB
  • SQLRepository para MySQL/MSSQL
  • SQLRawRepository para queries crudas
  • OLAPRepository para análisis

Licencia

MIT


Autor

Hemia Technologies