@agentforge-io/typeorm
v3.1.0
Published
Default TypeORM-backed persistence for AgentForge. Ships entities + repository implementations of @agentforge-io/core's repository contracts. Drop-in alternative: write your own Prisma/Drizzle/raw-SQL implementation of the same contracts.
Readme
@agentforge-io/typeorm
Default TypeORM-backed persistence for AgentForge. Implements every repository contract from @agentforge-io/core and ships the entity definitions.
This is one of several possible persistence adapters — the only thing the AgentForge core cares about is that you supply objects implementing the eight repository interfaces. Drop-in alternatives are easy to write for Prisma, Drizzle, Kysely, MikroORM, raw SQL, an in-memory mock for tests, or anything else.
Use it
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { createAgentForge } from '@agentforge-io/core';
import { mountAgentForge } from '@agentforge-io/express';
import {
AGENTFORGE_TYPEORM_ENTITIES,
createTypeOrmRepositories,
} from '@agentforge-io/typeorm';
const ds = new DataSource({
type: 'postgres',
url: process.env.DATABASE_URL!,
entities: AGENTFORGE_TYPEORM_ENTITIES,
synchronize: true, // dev only — use migrations in production
});
await ds.initialize();
const af = createAgentForge({
config: { /* ... */ },
repositories: createTypeOrmRepositories(ds),
});
const app = express();
mountAgentForge(app, af);
app.listen(3000);Entities shipped
| Entity | Table | Purpose |
|---|---|---|
| UserEntity | af_users | Account + plan + lockout state |
| RefreshTokenEntity | af_refresh_tokens | Refresh-token rotation chain |
| EmailTokenEntity | af_email_tokens | Verify-email + password-reset tokens |
| AuthIdentityEntity | af_auth_identities | OAuth links (provider, providerId → userId) |
| ConversationEntity | af_conversations | Chat thread |
| MessageEntity | af_messages | Persisted user/assistant turns |
| SubscriptionEntity | af_subscriptions | Stripe subscription state |
| UsageRecordEntity | af_usage_records | Per-message token + request usage |
If you want to extend (e.g. add a tenantId column for multi-tenant), subclass the entity or add a custom view layer in your own repository implementations.
Writing a non-TypeORM adapter
Implement these interfaces from @agentforge-io/core:
import type {
UserRepository,
RefreshTokenRepository,
EmailTokenRepository,
AuthIdentityRepository,
ConversationRepository,
MessageRepository,
SubscriptionRepository,
UsageRecordRepository,
} from '@agentforge-io/core';Prisma sketch
import { PrismaClient } from '@prisma/client';
class PrismaUserRepository implements UserRepository {
constructor(private prisma: PrismaClient) {}
findById(id: string) { return this.prisma.user.findUnique({ where: { id } }); }
findByEmail(email: string) { return this.prisma.user.findUnique({ where: { email } }); }
// ...
}
const prisma = new PrismaClient();
const repositories = {
users: new PrismaUserRepository(prisma),
// ... 7 more
};
createAgentForge({ config, repositories });Drizzle sketch
import { drizzle } from 'drizzle-orm/postgres-js';
import { eq } from 'drizzle-orm';
import { users } from './schema';
class DrizzleUserRepository implements UserRepository {
constructor(private db: ReturnType<typeof drizzle>) {}
async findById(id: string) {
const [row] = await this.db.select().from(users).where(eq(users.id, id));
return row ?? null;
}
// ...
}In-memory (testing)
@agentforge-io/core already ships createInMemoryRepositories() — use it for unit tests and ephemeral demos.
Convention
- All AgentForge tables are prefixed
af_. If you have your own users table, theUserRepositoryimpl can map between the two — the domain shape (User) is plain TS and decoupled from any storage column names. - Tokens (refresh, email-verify, password-reset) are stored hashed (sha256). Never store the raw token.
select: falsecolumns (passwordHash,failedLoginCount,lockedUntil) are not returned by default. The Auth service usesfindByEmailWithSecretswhich explicitly opts them in for the password-check path.
Migrations
For production, switch off synchronize: true and manage migrations yourself. The shipped entities are stable — schema changes will be additive whenever possible and called out in CHANGELOG.
