mongino
v2.0.0
Published
Driver estilo MongoDB sobre un único fichero .db cifrado, con motor B-Tree en memoria. Sin servidor.
Maintainers
Readme
mongino
Driver con API estilo MongoDB/Mongoose que guarda toda la base de datos en un
único fichero .db, con motor B-Tree en memoria y persistencia binaria
cifrada. Sin servidor, sin dependencias nativas.
📖 Documentación completa de la API →
bun install
bun test
bun examples/demo.tsUso rápido
import { MongoLite, SchemaTypes, type Document } from "mongino";
interface User extends Document {
nombre: string;
email: string;
edad: number;
}
const db = new MongoLite("data/app.db", {
degree: 16, // grado del B-Tree
encryptionKey: process.env.DB_KEY, // opcional, pero recomendado
});
const User = db.model<User>("User", {
nombre: { type: SchemaTypes.String, required: true, trim: true },
email: { type: SchemaTypes.String, required: true, unique: true, lowercase: true },
edad: { type: SchemaTypes.Number, required: true, min: 0, max: 130 },
}, { timestamps: true });
await User.create({ nombre: "Ada", email: "[email protected]", edad: 36 });
const adultos = await User.find({ edad: { $gte: 18 } })
.sort("-edad")
.limit(10)
.select({ nombre: 1, edad: 1 });
db.close(); // vuelca los cambios y libera el bloqueoQué trae
- API familiar:
create,find,findOne,findById,updateOne,updateMany,deleteMany,populate,sort,skip,limit,select… - Operadores de MongoDB:
$gt $gte $lt $lte $in $nin $ne $regex $exists $type $all $size $elemMatch $mod $not $or $and $nory rutas anidadas. - Operadores de actualización:
$set $unset $inc $mul $min $max $push $addToSet $pull $pop $rename $currentDate $setOnInsert. - Schemas con validación:
required,default,unique,min/max,enum,match,immutable,trim/lowercase,cast, validadores propios (sync o async),timestampsy modostrict. - Índices en memoria para los campos
uniqueeindex: true: igualdad,$iny rangos ($gt/$lt/…) sin recorrer la colección. - Transacciones con rollback:
db.transaction(async () => { ... }). - Copias y volcados:
db.backup(ruta),db.exportJSON(),db.importJSON().
Novedades en 2.0.0
- Escrituras mucho más rápidas. En la 1.x cada escritura reescribía el
fichero entero: con una base de 33 MB, ~600 ms por escritura. En la 2.0 solo
se añade al final del fichero lo que cambió; la misma escritura cuesta
~0,5–5 ms (según el
fsyncde la plataforma). - Arreglado un caso de pérdida de datos: la librería
bsonpodía truncar en silencio un bloque de más de ~17 MB al guardar, y el fichero ya no abría. - Cambio de formato (v3). Los ficheros de la 1.x se abren sin hacer nada;
la primera escritura los convierte al formato nuevo. Un fichero convertido
no se puede abrir con la 1.x: haz una copia con
db.backup(ruta)si necesitas poder volver atrás.
El fichero .db
El contenido no es JSON ni texto legible: se serializa por bloques, se comprime con deflate y se cifra con AES-256-GCM (clave derivada con scrypt). Abrirlo con un editor solo muestra bytes opacos y cualquier manipulación se detecta al abrirlo.
Sin
encryptionKeyel fichero queda ofuscado pero no es confidencial (la clave interna está en el código). Para confidencialidad real pasa tu propiaencryptionKey; sin ella el fichero no se puede abrir.
Garantías de durabilidad (detalle):
- Escrituras confirmadas con un
fsync: cada guardado añade un registro cifrado al final del fichero; la base solo se reescribe al compactar (temporal →fsync→rename→fsyncdel directorio). - Recuperación: si el proceso muere a mitad de un guardado, al abrir se descarta el registro incompleto (guardando una copia) o se recupera el temporal.
- Nunca se descarta un fichero ilegible en silencio: se conserva una copia
.corrupt-<timestamp>y se lanzaStorageError. - Bloqueo entre procesos: dos instancias sobre el mismo fichero fallan al abrir en vez de pisarse las escrituras (los bloqueos huérfanos se reciclan).
- Validación previa: un documento no almacenable se rechaza antes de entrar en memoria, así que nunca deja la base de datos sin poder persistir.
- Reversión: si un
batcho unatransactionlanza, se revierte entero; no se persiste a medias. - Escrituras serializadas: dos operaciones simultáneas sobre el mismo
documento no se pisan, y los campos
uniquese respetan bajo concurrencia. - Aislamiento: lo almacenado es una copia propia, así que modificar un documento que te devolvió una consulta no altera la base de datos.
Rendimiento
Medido con 20.000 documentos de ~150 bytes:
| Operación | Coste |
| --- | --- |
| findById o campo indexado | < 1 ms |
| Rango selectivo sobre campo indexado | ~1 ms |
| find por campo sin índice | ~20 ms |
| create / update individual | < 0,1 ms (antes ~11 ms) |
| insertMany de 20.000 | ~310 ms |
| Abrir y cargar el fichero | ~108 ms |
Cada escritura añade solo el documento modificado al final del fichero: su
coste no depende del tamaño de la base. El fichero se compacta automáticamente
cuando ese registro ocupa más que la base. Agrupar escrituras masivas con
insertMany, batchAsync o transaction sigue ahorrando un fsync por
documento. Máximo 16 MB por documento.
Desarrollo
bun test # 164 tests
bun run typecheck
bun run build # genera dist/ (ESM + tipos)