In-Memory Repository
Fast, simple storage for testing and prototyping
The In-Memory Repository provides a lightweight implementation of the Repository pattern backed by JavaScript Maps. It is perfect for testing, rapid prototyping, and development scenarios where persistence is not required.
Installation
The In-Memory Repository is included in @justscale/core/models:
pnpm add @justscale/core/modelsBasic Usage
The InMemoryRepository can be used directly or extended for custom entity types:
import { InMemoryRepository } from '@justscale/core/models';
import type { User } from './types';
// Direct usage
const users = new InMemoryRepository<User>();
// Save entities
const user = await users.save({
email: 'john@example.com',
name: 'John Doe',
});
console.log(user.id); // Auto-generated ID
console.log(user.createdAt); // Auto-generated timestamp
console.log(user.updatedAt); // Auto-generated timestampQuerying
The in-memory adapter supports all standard repository query operations:
import { InMemoryRepository } from '@justscale/core/models';
import type { User } from './types';
const users = new InMemoryRepository<User>();
// Find by ID
const user = await users.findById('1');
// Find one by criteria
const john = await users.findOne({ email: 'john@example.com' });
// Find all with filters
const activeUsers = await users.find({
where: { active: true },
orderBy: { createdAt: 'desc' },
limit: 10,
offset: 0,
});
// Count
const total = await users.count({ active: true });
// Check existence
const exists = await users.exists('1');Custom ID Generation
By default, the adapter generates sequential numeric IDs. You can provide a custom ID generator:
import { InMemoryRepository } from '@justscale/core/models';
import { randomUUID } from 'crypto';
import type { User } from './types';
const users = new InMemoryRepository<User>(
() => randomUUID() // Use UUIDs instead
);
const user = await users.save({ email: 'test@example.com', name: 'Test' });
console.log(user.id); // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'Testing with In-Memory Repository
The in-memory adapter is ideal for unit tests because it is fast and does not require external dependencies:
import { describe, test, expect, beforeEach } from 'vitest';
import { InMemoryRepository } from '@justscale/core/models';
import type { User } from './types';
describe('UserService', () => {
let users: InMemoryRepository<User>;
beforeEach(() => {
// Fresh repository for each test
users = new InMemoryRepository<User>();
});
test('creates user with unique email', async () => {
const user = await users.save({
email: 'unique@example.com',
name: 'Test User',
});
expect(user.id).toBeDefined();
expect(user.email).toBe('unique@example.com');
});
test('finds user by email', async () => {
await users.save({ email: 'find@example.com', name: 'Find Me' });
const found = await users.findOne({ email: 'find@example.com' });
expect(found).toBeDefined();
expect(found?.name).toBe('Find Me');
});
test('updates existing user', async () => {
const user = await users.save({ email: 'old@example.com', name: 'Old' });
user.name = 'New';
const updated = await users.save(user);
expect(updated.name).toBe('New');
expect(updated.updatedAt.getTime()).toBeGreaterThan(user.createdAt.getTime());
});
});Clear Method
The adapter provides a clear() method for resetting state during tests:
import { InMemoryRepository } from '@justscale/core/models';
import type { User } from './types';
const users = new InMemoryRepository<User>();
await users.save({ email: 'test@example.com', name: 'Test' });
console.log(await users.count()); // 1
users.clear(); // Reset repository
console.log(await users.count()); // 0Dependency Injection
Use the in-memory adapter with JustScale's DI system:
import JustScale from '@justscale/core';
import { InMemoryRepository } from '@justscale/core/models';
import { UserRepository } from './user-repository';
import { UserService } from './user-service';
// Provide in-memory implementation
const app = JustScale()
.add(UserService)
.bind(UserRepository, InMemoryRepository)
.build()Limitations
Warning
- No persistence - All data is lost when the process restarts
- Single process only - Does not share data across multiple instances or workers
- Memory constrained - Large datasets can consume significant memory
- Simple queries only - No complex joins, aggregations, or full-text search
For production applications, use the PostgreSQL adapter.
When to Use
The in-memory adapter is best suited for:
- Unit testing with fast, isolated tests
- Rapid prototyping and proof-of-concepts
- Development environments with simple data needs
- Integration tests that do not require real database state
For production use cases, use the PostgreSQL adapter.