@analog-tools/session
✨ Simplified Session Management ✨
Completely redesigned with a clean, functional API. No more over-engineered abstractions!
A simple, performant session management library for H3-based applications (Nuxt, Nitro, AnalogJS). Designed for simplicity and efficiency with a single API pattern.

Table of Contents
Features
- 🎯 Simple: Single functional API, no dual patterns or classes
- ⚡ Performance: ~4KB gzipped, optimized for modern applications
- 🔒 Secure: Essential crypto with timing attack resistance
- 🔄 Direct: Uses unstorage directly, no wrapper abstractions
- 🔄 Rotation: Secret key rotation support
- 🧩 TypeScript: Full type safety with minimal generics
- ⚡ Modern: Built for AnalogJS
Breaking Changes in v0.0.6
This version introduces a complete API redesign that simplifies the previous over-engineered approach:
- ❌ Removed:
Session
class and SessionHandler
interface
- ❌ Removed:
UnstorageSessionStore
wrapper and registerStorage
factory
- ❌ Removed: Complex crypto module (309 lines → 96 lines)
- ❌ Removed: Dual API patterns and unnecessary abstractions
- ✅ Added: Simple functional API with direct storage integration
- ✅ Added: Essential crypto functions only
- ✅ Added: Storage factory functions
Migration Guide: See Migration from v0.0.5 section below.
Installation
npm install @analog-tools/session
Quick Start
Basic Usage with Memory Storage
import { defineEventHandler } from 'h3';
import { useSession, getSession, updateSession, createMemoryStore } from '@analog-tools/session';
const store = createMemoryStore();
export default defineEventHandler(async (event) => {
await useSession(event, {
store,
secret: 'your-secret-key',
maxAge: 86400,
});
const session = getSession(event);
console.log('Current session:', session);
await updateSession(event, (data) => ({
visits: (data.visits || 0) + 1,
lastAccess: Date.now(),
}));
return {
visits: getSession(event)?.visits || 0,
};
});
With Redis Storage
import { createRedisStore } from '@analog-tools/session';
const store = createRedisStore({
host: 'localhost',
port: 6379,
});
export default defineEventHandler(async (event) => {
await useSession(event, {
store,
secret: ['new-secret', 'old-secret'],
name: 'my-app-session',
maxAge: 3600,
cookie: {
secure: true,
httpOnly: true,
sameSite: 'strict',
},
});
});
API Reference
Core Functions
useSession(event, config)
Initialize session middleware for an H3 event. Must be called before other session operations.
await useSession(event, {
store: Storage<T>,
secret: string | string[],
name?: string,
maxAge?: number,
cookie?: CookieOptions,
generate?: () => T,
});
getSession<T>(event): T | null
Get current session data from the event context.
const session = getSession<{ userId?: string }>(event);
if (session?.userId) {
console.log('User ID:', session.userId);
}
updateSession<T>(event, updater)
Update session data immutably and persist to storage.
await updateSession(event, (currentData) => ({
lastLogin: new Date().toISOString(),
loginCount: (currentData.loginCount || 0) + 1,
}));
destroySession(event)
Destroy the current session, clear storage and cookies.
await destroySession(event);
regenerateSession<T>(event)
Regenerate session ID while preserving data (useful after login).
await regenerateSession(event);
Storage Factories
createMemoryStore<T>(options?)
Create in-memory storage for development and testing.
const store = createMemoryStore();
createRedisStore<T>(options)
Create Redis-backed storage for production.
const store = createRedisStore({
url: 'redis://localhost:6379',
host: 'localhost',
port: 6379,
password: 'optional',
db: 0,
});
Crypto Functions
signCookie(value, secret): Promise<string>
Sign a cookie value with HMAC-SHA256.
unsignCookie(signedValue, secrets): Promise<string | null>
Verify and unsign a cookie value, supports multiple secrets for rotation.
Usage Examples
Authentication Flow
import { defineEventHandler, readBody, createError } from 'h3';
import {
useSession,
getSession,
updateSession,
destroySession,
regenerateSession,
createRedisStore
} from '@analog-tools/session';
const sessionConfig = {
store: createRedisStore({ url: process.env.REDIS_URL }),
secret: process.env.SESSION_SECRET!,
maxAge: 3600,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
sameSite: 'strict' as const,
},
};
export default defineEventHandler(async (event) => {
await useSession(event, sessionConfig);
const { username, password } = await readBody(event);
const user = await validateUser(username, password);
if (user) {
await regenerateSession(event);
await updateSession(event, () => ({
userId: user.id,
username: user.username,
loginTime: Date.now(),
}));
return { success: true };
}
return { success: false };
});
export default defineEventHandler(async (event) => {
await useSession(event, sessionConfig);
const session = getSession(event);
if (!session?.userId) {
throw createError({
statusCode: 401,
statusMessage: 'Not authenticated',
});
}
return { user: session };
});
export default defineEventHandler(async (event) => {
await useSession(event, sessionConfig);
await destroySession(event);
return { success: true };
});
TypeScript Interface
interface UserSession {
userId?: string;
username?: string;
roles?: string[];
preferences?: Record<string, unknown>;
lastActivity?: number;
}
export default defineEventHandler(async (event) => {
await useSession<UserSession>(event, {
store: createRedisStore({ url: process.env.REDIS_URL }),
secret: process.env.SESSION_SECRET!,
generate: () => ({ lastActivity: Date.now() }),
});
const session = getSession<UserSession>(event);
});
Configuration
Session Config
interface SessionConfig<T> {
store: Storage<T>;
secret: string | string[];
name?: string;
maxAge?: number;
cookie?: CookieOptions;
generate?: () => T;
}
Cookie Options
interface CookieOptions {
domain?: string;
path?: string;
secure?: boolean;
httpOnly?: boolean;
sameSite?: boolean | 'lax' | 'strict' | 'none';
}
Migration from v0.0.5
If you're upgrading from v0.0.5 or earlier, here's how to migrate your code:
Before (v0.0.5 and earlier)
After (v0.0.6 - Current)
import { useSession, getSession, updateSession, createRedisStore } from '@analog-tools/session';
const store = createRedisStore({ host: 'localhost', port: 6379 });
export default defineEventHandler(async (event) => {
await useSession(event, { store, secret: 'key' });
await updateSession(event, (data) => ({
visits: (data.visits || 0) + 1
}));
});
Key Migration Points:
- Use
createRedisStore()
or createMemoryStore()
for storage
- Pass configuration directly to
useSession(event, config)
- Use
getSession(event)
to get current session data
- Use
updateSession(event, updater)
to modify session data (auto-saves)
- All operations are functional - no class instantiation needed
Performance
- Bundle Size: ~4KB gzipped (significant reduction from previous versions)
- Memory Usage: Reduced through simplified architecture and direct storage integration
- CPU: Essential HMAC-SHA256 operations only, ~96 lines of crypto code
- Tree Shaking: Better dead code elimination with modern ESM build
Security
- HMAC-SHA256 for cookie signing
- Timing attack resistant comparisons
- Secure cookie defaults
- Secret rotation support
- No over-engineered crypto that creates attack surfaces
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT © Gregor Speck