This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MikroORM Getting Started guide — a blog API built with Fastify, MikroORM v7 (SQLite driver), and TypeScript. ESM-only ("type": "module" in package.json).
- Build:
npm run build(runstsc) - Bundle:
npm run bundle(Vite SSR build →dist/) - Dev server:
npm run start(runstsx src/server.tson port 3001) - Run all tests:
npm test(runsvitest) - Run a single test:
npx vitest run test/user.test.ts - MikroORM CLI:
npx mikro-orm(config atsrc/mikro-orm.config.ts) - Create migration:
npx mikro-orm migration:create
Entities use MikroORM v7's defineEntity() + p.* property builder pattern, not decorators:
export const ArticleSchema = defineEntity({
name: 'Article',
extends: BaseSchema,
properties: {
title: p.string().index(),
author: () => p.manyToOne(UserSchema).ref(), // arrow function for circular refs
},
});
export type IArticle = InferEntity<typeof ArticleSchema>;Key conventions:
- Schema objects are exported (e.g.,
ArticleSchema) and registered inmikro-orm.config.ts - Types are inferred with
InferEntity<typeof Schema>(e.g.,IArticle,ITag,IComment) - Only
Userhas a custom class (viasetClass) forverifyPassword()method BaseSchema(insrc/modules/common/base.entity.ts) providesid,createdAt,updatedAt- Relations use arrow-function property definitions to handle circular imports
- Entity imports use
@mikro-orm/core; driver-specific imports (@mikro-orm/sqlite) are used only indb.tsand custom repositories
Code is organized in src/modules/{module}/:
- user/ —
Userentity (with custom class),UserRepository, routes (sign-up, sign-in, profile) - article/ —
Article,Comment,Tagschemas +ArticleListingvirtual entity,ArticleRepository, routes - common/ —
BaseSchema,AuthError, helper functions,SoftDeleteSubscriber
initORM() synchronously initializes MikroORM (via new MikroORM()) and returns a cached Services object with typed repository accessors (db.user, db.article, db.comment, db.tag). Both app code and tests call this — tests pass overrides (:memory: DB, debug off).
bootstrap() creates the Fastify app with:
- JWT plugin for auth
RequestContexthook (per-request MikroORM identity map)- Auth hook (decodes JWT, loads
request.user) - Error handler mapping
NotFoundError→ 404,AuthError→ 401 - Route registration with
/userand/articleprefixes
Tests use in-memory SQLite (:memory:) with schema created fresh + TestSeeder for fixtures. Each test file uses a different port for parallel execution. Tests use Fastify's app.inject() for HTTP assertions without a real server.
- Soft delete:
SoftDeleteSubscriberintercepts DELETE change sets and converts them to UPDATE withdeletedAttimestamp.Commententity has asoftDeletefilter enabled by default. - Virtual entity:
ArticleListinguses an expression callback (returns QueryBuilder) instead of a database table. - Lazy properties:
User.passwordandArticle.textare lazy-loaded (not fetched unless explicitly populated). - Ref wrapper: Foreign keys use
.ref()for type-safe references without loading the full entity. - Zod validation: Route handlers use Zod schemas for request body validation.