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 implements the same interface as the PostgreSQL adapter, making it perfect for testing and prototyping.
Basic Usage
Define a model with defineModel, then create an in-memory model and repository:
import { defineModel, field } from '@justscale/core/models'
import { createInMemoryModel, createInMemoryRepository } from '@justscale/core/models'
// 1. Domain model (same as production)
export class User extends defineModel({
email: field.string().max(255).unique(),
name: field.string().max(100),
active: field.boolean().default(true),
}) {}
// 2. In-memory model (adapter decides storage details)
const MemUser = createInMemoryModel(User)
// 3. Repository for DI
export const UserRepository = createInMemoryRepository(MemUser)Querying
The in-memory adapter supports the full repository query interface using type-safe field expressions:
// Get by reference
const userRef = User.ref`${userId}`
const user = await users.get(userRef)
// Find one by field expression
const john = await users.findOne(User.fields.email.eq('john@example.com'))
// Find many with filters, ordering, pagination
const activeUsers = await users.find({
where: User.fields.active.eq(true),
orderBy: [User.fields.name.asc()],
limit: 10,
offset: 0,
})
// Count
const total = await users.count(User.fields.active.eq(true))
// Check existence
const exists = await users.exists(User.fields.email.eq('test@example.com'))Mutations
All mutation methods use Ref<T> for entity references — pass a persistent entity directly or use a typed reference:
// Insert
const user = await users.insert({ email: 'alice@example.com', name: 'Alice' })
// Update by passing the persistent entity directly (it IS a reference)
const updated = await users.update(user, { name: 'Alice Smith' })
// Or update by typed reference at a boundary
const userRef = User.ref`${userId}`
await users.update(userRef, { active: false })
// Delete
await users.delete(user) // Pass the entity
await users.delete(userRef) // Or a reference
// Save (smart insert/update)
const newUser = new User({ email: 'bob@example.com', name: 'Bob' })
const saved = await users.save(newUser) // Inserts (transient)
await users.save(saved) // Updates (persistent)Testing
The in-memory adapter is ideal for unit tests. Use it as a drop-in replacement for the PostgreSQL adapter:
import { test, expect, beforeEach } from 'node:test'
import { createInMemoryModel, createInMemoryRepository } from '@justscale/core/models'
import { User, UserRepository } from './user.model'
import { UserService } from './user.service'
import { TestContainer } from '@justscale/testing'
test('creates user with unique email', async () => {
const container = new TestContainer()
container.bind(UserRepository, createInMemoryRepository(createInMemoryModel(User)))
const service = container.resolve(UserService)
const user = await service.create({ email: 'test@example.com', name: 'Test' })
expect(user.email).toBe('test@example.com')
expect(user.name).toBe('Test')
})
test('finds user by email', async () => {
const container = new TestContainer()
container.bind(UserRepository, createInMemoryRepository(createInMemoryModel(User)))
const service = container.resolve(UserService)
await service.create({ email: 'find@example.com', name: 'Find Me' })
const found = await service.findByEmail('find@example.com')
expect(found?.name).toBe('Find Me')
})Dependency Injection
Wire in-memory repositories the same way as PostgreSQL — your services don't know the difference:
import JustScale, { bindRepository } from '@justscale/core'
import { ModelRepository, createInMemoryModel, createInMemoryRepository } from '@justscale/core/models'
import { User } from './user.model'
import { UserService } from './user.service'
// In-memory for development
const MemUser = createInMemoryModel(User)
const InMemUserRepo = createInMemoryRepository(MemUser)
const app = JustScale()
.add(UserService)
.add(bindRepository(ModelRepository.of(User), InMemUserRepo))
.build()Tip
ModelRepository.of(User) — the abstract token. Whether you bind PostgreSQL or in-memory at bootstrap, the service code stays identical.Limitations
Warning
- No persistence — all data is lost when the process restarts
- Single process only — does not share data across instances
- Memory constrained — large datasets consume significant memory
- Simple queries only — no complex joins, aggregations, or full-text search
For production applications, use the PostgreSQL adapter.