dbfull-library
v1.0.8
Published
Client library for dbfull-backend — simplifies communication with the DBFull Gateway API
Downloads
1,156
Maintainers
Readme
dbfull-library
Client library for the DBFull Gateway API (
dbfull-backend).
Simplifica la comunicación con el backend sin tener que escribirfetchoaxiosen cada proyecto.
Instalación
# desde npm (cuando esté publicada)
npm install dbfull-library
# o directamente desde la carpeta local
npm install ../dbfull-libraryConfiguración requerida
Necesitas:
- La URL base del backend, ej.
http://localhost:3001 - Un API Key admin válido — para operaciones CRUD
- (Opcional) Un API Key de solo lectura — se usa automáticamente en llamadas
.query()(SELECT raw)
Cómo funcionan los dos keys
El backend maneja un único header X-API-Key, pero detecta automáticamente el tipo de key:
| Key | Tipo detectado | Operaciones permitidas |
|-----|---------------|------------------------|
| apiKey (admin) | keyType: 'admin' | Todo: CRUD + query |
| queryApiKey (solo lectura) | keyType: 'query' | Solo .query() (SELECT) |
La librería selecciona automáticamente cuál enviar según la operación.
Cifrado de Extremo a Extremo (SHP Protocol)
dbfull-library integra el protocolo SHP (Secure Headers & Payload) para proporcionar cifrado E2EE transparente. Cuando está activo, todos los datos se cifran en el cliente usando AES-256-GCM y las llaves se intercambian mediante RSA-OAEP.
Configuración de SHP
Solo necesitas proporcionar la llave pública RSA del servidor en la configuración inicial:
const client = new DBFullClient({
baseUrl: 'http://localhost:3001',
apiKey: 'tu-api-key',
// Configuración de cifrado
shp: {
publicKey: '-----BEGIN PUBLIC KEY...\n...', // Llave RSA del backend
enabled: true, // Opcional (defecto: true)
}
});¿Cómo funciona?
- Transparente: No necesitas cambiar tus llamadas a
.findAll(),.create(), etc. La librería intercepta las peticiones y cifra el payload automáticamente. - Seguridad Zero-Trust: Los datos viajan cifrados por la red y solo pueden ser descifrados por el backend que posee la llave privada correspondiente.
- Protección de Headers: Además del cuerpo del mensaje, SHP protege los metadatos de la petición.
Parámetros de conexión
La ruta completa sigue este patrón:
{baseUrl}/api/v1/{projectSlug}/{syncGroupId}/{table}| Parámetro | Descripción | Ejemplo |
|-----------|-------------|---------|
| projectSlug | Slug del Project registrado en el gateway (auto-generado al crear el proyecto, ej. de nombre "Fibex Telecom" → slug "fibex-telecom") | fibex-telecom |
| syncGroupId | ID del grupo de sincronización de la Connection (campo sync_group_id en la tabla connections). Identifica el cluster/par de réplicas que el gateway debe usar. | sae-anzoategui |
| table | Nombre de la tabla en la base de datos destino | tm_contratos |
Nota: El
syncGroupIdno es el nombre de la base de datos. Es el identificador del cluster de conexiones registrado en el gateway bajo ese proyecto. Un proyecto puede tener varios sync groups (uno por cada BD o conjunto de réplicas).
Ejemplo de URL resultante:
http://localhost:3001/api/v1/fibex-telecom/sae-anzoategui/tm_contratosUso
Patrón 1 — DBFullClient (recomendado para proyectos con múltiples tablas)
import { DBFullClient } from 'dbfull-library';
const client = new DBFullClient({
baseUrl: 'http://localhost:3001',
apiKey: 'tu-api-key-admin', // CRUD (create, update, delete, findAll...)
queryApiKey: 'tu-api-key-query', // opcional — SELECT raw via .query()
});
// client.table(projectSlug, syncGroupId, table)
const contratos = client.table('fibex-telecom', 'sae-anzoategui', 'tm_contratos');
const result = await contratos.findAll({ page: 1, limit: 20 }); // usa apiKey
const one = await contratos.findById('abc-123'); // usa apiKey
const created = await contratos.create({ nombre: 'Juan', estado: 'activo' }); // usa apiKey
await contratos.update(created.data.id, { estado: 'inactivo' }); // usa apiKey
await contratos.delete(created.data.id); // usa apiKey
await contratos.restore(created.data.id); // usa apiKeyPatrón 2 — DBFullDatabase (varias tablas del mismo cluster)
// client.database(projectSlug, syncGroupId)
const db = client.database('fibex-telecom', 'sae-anzoategui');
const contratos = db.table('tm_contratos');
const pagos = db.table('tm_pagos');
const [cs, ps] = await Promise.all([
contratos.findAll({ limit: 100 }),
pagos.findAll({ limit: 100 }),
]);
// Ejecutar query SQL directamente sobre este cluster
const result = await db.query("SELECT COUNT(*) as total FROM tm_contratos");
console.log(result.data, result.count);Patrón 3 — DBFullTable standalone (ideal para inyección de dependencias / servicios)
import { DBFullTable } from 'dbfull-library';
interface Contrato {
id: string;
nombre: string;
estado: 'activo' | 'inactivo';
fecha_inicio: string;
}
export const contratosTable = new DBFullTable<Contrato>({
baseUrl: process.env.DBFULL_URL!,
apiKey: process.env.DBFULL_API_KEY!,
queryApiKey: process.env.DBFULL_QUERY_API_KEY, // opcional
org: 'fibex-telecom', // projectSlug — slug del Project en el gateway
db: 'sae-anzoategui', // syncGroupId — ID del cluster/grupo de la Connection
table: 'tm_contratos',
});
const list = await contratosTable.findAll({ sort: 'nombre:asc', limit: 50 }); // usa apiKeyFiltros por campo — searchParams
El backend detecta automáticamente cualquier query param que coincida con un campo real de la tabla y lo convierte en un filtro WHERE. La librería expone esto como la opción searchParams dentro de findAll.
// Igualdad simple (campo = valor)
const activos = await contratos.findAll({
searchParams: { estado: 'activo' },
});
// Con operador explícito
const busqueda = await contratos.findAll({
searchParams: {
nombre: { op: 'like', val: 'Juan' }, // WHERE nombre LIKE '%Juan%'
monto: { op: 'gte', val: 500 }, // WHERE monto >= 500
},
});Operadores disponibles:
| Operador | SQL generado | Ejemplo |
|----------|--------------|---------|
| eq (default) | campo = valor | { estado: 'activo' } |
| ne / neq | campo != valor | { estado: { op: 'ne', val: 'inactivo' } } |
| like | campo LIKE '%valor%' | { nombre: { op: 'like', val: 'Juan' } } |
| gt | campo > valor | { monto: { op: 'gt', val: 100 } } |
| gte | campo >= valor | { monto: { op: 'gte', val: 100 } } |
| lt | campo < valor | { monto: { op: 'lt', val: 500 } } |
| lte | campo <= valor | { monto: { op: 'lte', val: 500 } } |
| in | campo IN (...) | { id: { op: 'in', val: '1,2,3' } } |
| nin | campo NOT IN (...) | { id: { op: 'nin', val: '4,5' } } |
Raw SQL — query
Ejecuta una consulta SELECT directamente sobre la base de datos. El backend impone:
- Solo SELECT — cualquier
INSERT,UPDATE,DELETE,DROP, etc. es rechazado con 403. - Máximo 5000 filas — el backend aplica un
LIMIT 5000automáticamente si no se especifica.
⚠️ No disponible para conexiones Redis (no soporta SQL crudo).
// Vía DBFullDatabase (recomendado)
const db = client.database('fibex-telecom', 'sae-anzoategui');
const result = await db.query<{ nombre: string; estado: string }>(
"SELECT nombre, estado FROM tm_contratos WHERE estado = 'activo' LIMIT 100"
);
console.log(result.data); // { nombre: string; estado: string }[]
console.log(result.count); // número de filas retornadas
// Vía DBFullHttpClient (bajo nivel)
const raw = await client.http.query(
'fibex-telecom', // projectSlug
'sae-anzoategui', // syncGroupId
'SELECT COUNT(*) as total FROM tm_contratos'
);Health Check — verificar conectividad
Métodos disponibles en DBFullClient para verificar el estado del gateway. No requieren API Key.
client.isAlive() — ping simple
La forma más fácil: retorna true si el gateway responde healthy, false si no (incluso ante errores de red).
if (!(await client.isAlive())) {
console.error('Gateway DBFull no disponible');
process.exit(1);
}client.health() — estado general
const { data } = await client.health();
console.log(data.status); // 'healthy' | 'unhealthy'
console.log(data.uptime); // segundos desde último reinicio
console.log(data.gatewayDb); // 'connected' | 'disconnected'client.healthConnections() — estado de conexiones individuales
const { data } = await client.healthConnections();
if (data.overall === 'degraded') {
console.warn('Algunas conexiones están degradadas:');
for (const [id, status] of Object.entries(data.connections)) {
console.warn(` ${id}: ${status.status}`, status.error ?? '');
}
}Métodos de conveniencia
first(options?) — primer registro
Devuelve directamente T | null en lugar de un paginado. Usa sort para controlar cuál es "el primero".
const primero = await contratos.first();
const masAntiguo = await contratos.first({ sort: 'fecha_inicio:asc' });last(options?) — último registro
Igual que first pero invierte el sort automáticamente. Si no se pasa sort, usa id:desc.
const ultimo = await contratos.last();
const masReciente = await contratos.last({ sort: 'fecha_inicio:asc' }); // → desccount(searchParams?) — total de registros
Hace una petición mínima (limit: 1) y lee pagination.total. Ideal para dashboards y guards.
const total = await contratos.count();
const activos = await contratos.count({ estado: 'activo' });exists(id) — verificar existencia
Retorna boolean. Solo captura el 404 — cualquier otro error se propaga normalmente.
if (await contratos.exists('abc-123')) {
await contratos.update('abc-123', { estado: 'inactivo' });
}Escritura masiva — bulkCreate vs bulkUpsert
Ambos métodos escriben múltiples registros en una sola petición HTTP, pero se comportan diferente ante registros duplicados:
| | bulkCreate | bulkUpsert |
|---|---|---|
| Comportamiento | INSERT puro | INSERT ... ON CONFLICT DO UPDATE |
| Primera ejecución | ✅ OK | ✅ OK |
| Ejecuciones siguientes | ❌ Falla (clave duplicada) | ✅ Actualiza el registro existente |
| Idempotente | No | Sí |
| Uso recomendado | Inserts únicos garantizados | Sincronizaciones, ETL, reintentos |
// ❌ bulkCreate — solo para inserts que nunca van a repetirse
const res = await contratos.bulkCreate([
{ id_persona: '123', cedula: 'V-12345678', franquicia: 'Caracas' },
]);
// Segunda llamada con el mismo id_persona → Internal Server Error (PK duplicada)
// ✅ bulkUpsert — idempotente, safe para sync periódicos
const res = await contratos.bulkUpsert([
{ id_persona: '123', cedula: 'V-12345678', franquicia: 'Caracas' },
]);
// Segunda llamada → actualiza el registro existente sin error
console.log(`Upserted: ${res.count}`);Nota:
bulkUpsertusa la clave primaria de la tabla detectada por el gateway para resolver el conflicto.
API Reference
DBFullClient methods
| Método | Descripción | Retorna |
|--------|-------------|---------|
| database(projectSlug, syncGroupId) | Instancia de DBFullDatabase para un cluster concreto | DBFullDatabase |
| table<T>(projectSlug, syncGroupId, table) | Instancia de DBFullTable<T> | DBFullTable<T> |
| health() | Estado general del gateway | Promise<HealthResponse> |
| healthConnections() | Estado de cada conexión registrada | Promise<ConnectionsHealthResponse> |
| isAlive() | Ping simple — true si healthy | Promise<boolean> |
| http | Cliente HTTP de bajo nivel | DBFullHttpClient |
DBFullTable<T> methods
| Método | Descripción | Retorna |
|--------|-------------|---------|
| findAll(options?) | Lista paginada de registros | PaginatedResponse<T> |
| findById(id) | Un registro por ID | SingleResponse<T> |
| search(query, fields?, options?) | Búsqueda full-text | PaginatedResponse<T> |
| create(data) | Crea un registro | SingleResponse<T> |
| bulkCreate(records) | Inserta múltiples registros (falla con duplicados) | BulkCreateResponse<T> |
| bulkUpsert(records) | Inserta o actualiza múltiples registros — idempotente ✅ | BulkUpsertResponse<T> |
| update(id, data) | Actualiza parcialmente un registro | SingleResponse<T> |
| delete(id) | Soft-delete de un registro | DeleteResponse |
| restore(id) | Restaura un registro borrado | RestoreResponse |
| first(options?) | Primer registro o null | T \| null |
| last(options?) | Último registro o null (sort invertido) | T \| null |
| count(searchParams?) | Total de registros | number |
| exists(id) | true si existe, false si 404 | boolean |
DBFullDatabase methods
Todos los métodos de DBFullTable están disponibles directamente pasando tableName como primer argumento, más:
| Método | Descripción | Retorna |
|--------|-------------|---------|
| table<T>(tableName) | Instancia de DBFullTable<T> | DBFullTable<T> |
| query<T>(sql) | Ejecuta SELECT SQL crudo | Promise<QueryResponse<T>> |
| bulkUpsert<T>(tableName, records) | Inserta o actualiza múltiples registros | Promise<BulkUpsertResponse<T>> |
FindAllOptions
| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| page | number | Página (desde 1) |
| limit | number | Resultados por página |
| sort | string \| SortOption \| SortOption[] | Ordenamiento, ej. "nombre:asc" |
| search | string | Término de búsqueda full-text |
| includeDeleted | boolean | Incluir registros borrados |
| searchParams | SearchParams | Filtros por campo (ver sección anterior) |
Tipos exportados
import type {
DBFullClientOptions,
FindAllOptions, SortOption, SearchParams, FieldFilter, FilterOperator,
PaginatedResponse, SingleResponse, BulkCreateResponse, BulkUpsertResponse,
DeleteResponse, RestoreResponse, PaginationMeta,
QueryResponse,
HealthResponse, ConnectionHealthStatus, ConnectionsHealthResponse,
DBFullErrorResponse,
} from 'dbfull-library';Manejo de errores
Todos los errores son instancias de DBFullError:
import { DBFullError } from 'dbfull-library';
try {
await contratos.findById('no-existe');
} catch (err) {
if (err instanceof DBFullError) {
console.error(err.message); // mensaje del backend
console.error(err.statusCode); // 404
console.error(err.errorCode); // "NOT_FOUND"
}
}Build
npm install
npm run buildLos archivos compilados quedan en /dist.
