express-lite-nosql-session
v1.0.0
Published
Persistent session store for express-session using lite-nosql (file-based, no external DB required)
Maintainers
Readme
express-lite-nosql-session
Persistent session store for express-session backed by lite-nosql.
No MySQL. No Redis. No Postgres. No native binaries.
Sessions are persisted to disk as plain files — ideal for shared hosting, small VPSs, or any environment without an external database.
Features
- ✅ Fully compatible with
express-sessionStore interface - ✅
store instanceof session.Storeis true at runtime (extends EventEmitter) - ✅ Persistent across server restarts
- ✅ Automatic TTL expiry + background garbage collection
- ✅ Optional AES-256-GCM encryption at rest
- ✅ WAL + snapshot compaction via
lite-nosql - ✅ Zero native dependencies
- ✅ TypeScript types — aligned with runtime
- ✅ ESM (Node.js 18+)
Installation
npm install express-lite-nosql-session lite-nosql express-sessionQuick Start
import express from "express";
import session from "express-session";
import LiteNoSQLStore from "express-lite-nosql-session";
const app = express();
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: new LiteNoSQLStore({ pasta: "./dados" }),
cookie: { maxAge: 86_400_000 }, // 24 h
}));Full Example with All Options
import session from "express-session";
import LiteNoSQLStore from "express-lite-nosql-session";
const store = new LiteNoSQLStore({
pasta: "./dados", // directory for db files (outside public web root)
colecao: "sessions", // collection name
modoDuravel: true, // fsync on every write
limiteWAL: 1_048_576, // compact WAL at 1 MB
ttlPadraoMs: 86_400_000, // 24 h default TTL
intervaloLimpezaMs: 300_000, // GC every 5 minutes
compactarAposLimpeza: false, // compact after each GC pass
debug: false,
chaveEncriptacao: process.env.SESS_DB_KEY, // optional encryption
});
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store,
cookie: { maxAge: 86_400_000 },
}));Options Reference
| Option | Type | Default | Description |
|---|---|---|---|
| pasta | string | required | Directory where session files are stored |
| colecao | string | "sessions" | lite-nosql collection name |
| modoDuravel | boolean | true | fsync on every write (safer, slightly slower) |
| limiteWAL | number | 1_048_576 | WAL size (bytes) before automatic compaction |
| ttlPadraoMs | number | 86_400_000 | Default TTL (ms) when no cookie expiry is set |
| intervaloLimpezaMs | number | 300_000 | GC interval in ms |
| compactarAposLimpeza | boolean | false | Compact after each GC pass |
| debug | boolean | false | Verbose internal logging |
| chaveEncriptacao | string | — | AES-256-GCM key to encrypt WAL + snapshot |
express-session Compatibility
LiteNoSQLStore extends session.Store (which itself extends EventEmitter) at runtime via createRequire, because express-session is a CommonJS module.
This means:
import session from "express-session";
import LiteNoSQLStore from "express-lite-nosql-session";
const store = new LiteNoSQLStore({ pasta: "./dados" });
console.log(store instanceof session.Store); // true
console.log(typeof store.emit); // "function"The TypeScript declaration (extends Store) matches this runtime behaviour exactly.
TTL and Session Expiry
expiresAt is calculated with this priority:
session.cookie.expires(absolute Date)session.cookie.maxAge→Date.now() + maxAge- Fallback:
Date.now() + ttlPadraoMs
On get(), expired sessions return null and are removed lazily (non-blocking).
touch() only writes expiresAt and sess.cookie — not the full session — to minimise WAL write amplification.
Garbage Collection
The background GC runs every intervaloLimpezaMs and:
- Queries sessions where
expiresAt <= Date.now() - Removes them in batches of 50 (avoids blocking the event loop)
- Optionally calls
compactar()ifcompactarAposLimpeza: true
The timer is created with .unref() so it never prevents the process from exiting.
WAL Compaction
lite-nosql writes all mutations to an append-only WAL, which grows over time. Compaction merges the WAL into a snapshot and resets it.
- Automatic: triggers when WAL exceeds
limiteWAL(default 1 MB). - After GC: set
compactarAposLimpeza: truefor session-heavy applications. - Before backup: call
await store._col.compactar()for a clean snapshot.
Encryption at Rest
new LiteNoSQLStore({
pasta: "./dados",
chaveEncriptacao: process.env.SESS_DB_KEY, // min 8 characters
});⚠️ Never delete .db.salt or other auxiliary files from the data folder — they are required to decrypt existing data.
⚠️ Store the key in an environment variable, never hardcode it.
Graceful Shutdown
Always call store.close() before exiting to flush pending writes and cancel the GC timer:
async function shutdown(signal) {
console.log(`${signal} received — shutting down`);
await store.close();
process.exit(0);
}
process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));Validating the Package Before Publishing
Use npm pack to simulate exactly what gets published to npm:
# Shows the list of files that would be included
npm pack --dry-run
# Creates the real tarball for inspection
npm pack
tar -tf express-lite-nosql-session-1.0.0.tgzOnly files listed in "files" in package.json are included:
src/index.jssrc/index.d.tsREADME.md
Single Process Only
⚠️
lite-nosqluses file-level locking and an in-memory cache. Running multiple Node.js processes pointing at the samepastadirectory (e.g. PM2 cluster mode) is not supported and will cause data corruption.
For multi-process deployments, use a network-capable store (Redis, Postgres, etc.).
Files on Disk
dados/
sessions.snapshot.json ← compact state
sessions.wal.log ← append-only journal
sessions.lock ← temporary lock during compactionPlace pasta outside your public web root so these files are not accessible over HTTP.
Limitations
- All sessions in memory:
lite-nosqlcaches everything in RAM. Not suitable for very large numbers of concurrent long-lived sessions. - Single process only: see above.
- No cross-collection transactions.
- ESM only:
lite-nosqlis ESM. CommonJS projects must useawait import(...).
CommonJS Usage
Because lite-nosql is ESM-only, CommonJS projects must use dynamic import:
// CommonJS
const { LiteNoSQLStore } = await import("express-lite-nosql-session");Or migrate to ESM by adding "type": "module" to your package.json.
Running the Example
cd examples/express-basic
npm install
SESSION_SECRET=mysecret node server.js# Login
curl -c cookies.txt -X POST http://localhost:3000/login \
-H "Content-Type: application/json" -d '{"userId":"123"}'
# Read session
curl -b cookies.txt http://localhost:3000/me
# Logout
curl -b cookies.txt -X POST http://localhost:3000/logoutLicense
MIT
