| name | guren-api |
| description | Guren framework API documentation, code patterns, and examples. Covers all subsystems — Controllers, Models, Routes, Middleware, Authentication, Authorization, Events, Jobs, Queue, Mail, Cache, Validation, Broadcasting, Notifications, Storage, Scheduling, I18n, Encryption, Health Checks, Error Handling, Container/ServiceProvider, Console Commands, and API Resources. Use when user asks "how to", "how does", "example of", "what is", or needs help understanding any Guren API — and proactively during implementation whenever you encounter an unfamiliar @guren/* API surface; check this before grepping node_modules dist files. |
Guren API Documentation Skill
You are a documentation assistant for the Guren framework.
The authoritative signature-level API reference lives in .claude/rules/*.md (auto-loaded per edited path); this skill is a subsystem tour for interactive Q&A.
Your Role
Help users understand and use Guren framework APIs by providing examples, patterns, and source file locations.
Core Subsystems
Controllers
Source: packages/server/src/mvc/Controller.ts
import { Controller } from '@guren/core'
import { pages } from '@/.guren/pages.gen'
import { PostPayloadSchema, PostIdParamSchema } from '../Validators/PostValidator.js'
export default class PostController extends Controller {
async index() {
const posts = await Post.all()
return this.inertia(pages.posts.Index, { posts })
}
async show() {
const { id } = this.validateParams(PostIdParamSchema)
const post = await Post.findOrFail(id)
return this.inertia(pages.posts.Show, { post })
}
async store() {
const data = await this.validateBody(PostPayloadSchema)
const user = await this.auth.userOrFail<UserRecord>()
const post = await Post.create({ ...data, authorId: user.id })
return this.redirect('/posts/' + post?.id)
}
}
Note: Server-side controllers use URL string literals for redirects. The route() helper is a frontend-only utility generated by codegen for React components.
Validation helpers (Zod duck-type — any schema with safeParse() works):
this.validateBody<T>(schema): Promise<T> — parse request body
this.validateQuery<T>(schema): T — parse query parameters
this.validateParams<T>(schema): T — parse route parameters
All throw ValidationException (HTTP 422) on failure.
Auth helpers:
this.auth.user<T>() — returns user or null
this.auth.userOrFail<T>() — returns user or throws AuthenticationException (401)
Models
Source: packages/orm/src/Model.ts
import { Model } from '@guren/orm'
import { posts } from '@/db/schema'
export class Post extends Model<typeof posts.$inferSelect> {
static table = posts
}
await Post.find(1)
await Post.findOrFail(1)
await Post.where('published', true).get()
await Post.create({ title: 'Hello' })
ModelNotFoundException (source: packages/orm/src/ModelNotFoundException.ts) carries statusCode: 404 and is automatically rendered as HTTP 404 by the ExceptionHandler.
Mass assignment protection — prevent injection of unintended fields via create() / update():
export class Post extends defineModel(posts) {
static fillable = ['title', 'excerpt', 'body', 'authorId']
}
export class User extends AuthenticatableModel<UserRecord> {
static override table = users
static fillable = ['name', 'email', 'password']
static guarded = ['id', 'passwordHash', 'rememberToken']
}
fillable (whitelist) — only listed fields pass through to create() / update(); unlisted input keys throw MassAssignmentException (set static strictFillable = false for silent discarding). Use forceCreate() / forceUpdate() for trusted server-side data.
guarded (blacklist, default: ['id']) — listed fields are silently stripped; ignored when fillable is set
- Both are enforced by
Model.filterFillable(), called automatically before persistence
- Always define
fillable on models that accept user input — this is the second defense layer after Zod validation
Relationships — declare once, eager-load anywhere:
export class User extends AuthenticatableModel<UserRecord> {
static override relationTypes: { posts: HasManyRecord<PostRecord> } = { posts: [] }
}
User.hasMany('posts', () => import('./Post.js').then((m) => m.Post), 'authorId', 'id')
export class Post extends defineModel(posts) {
static override relationTypes: { author: BelongsToRecord<UserRecord> } = { author: null }
}
Post.belongsTo('author', () => import('./User.js').then((m) => m.User), 'authorId', 'id')
await User.with('posts')
await User.with('posts.comments')
await User.where('active', true).with('posts').get()
await User.findWith(1, 'posts')
await User.withCount('posts')
await Post.withPaginate('author', { page: 1 })
Also available: hasOne, belongsToMany(name, related, pivotTable, foreignPivotKey, relatedPivotKey, parentKey?, relatedKey?), hasManyThrough, morphMany/morphTo. There are no attach/detach/sync pivot helpers and no firstOrCreate/updateOrCreate — manage pivot rows via a model on the pivot table (PivotModel.create(...) / PivotModel.delete(...)), and hand-roll find-or-create with Model.first(where) + Model.create(...). Full guide: docs/en/guides/database.md (Relationships section).
Routes
Source: packages/server/src/mvc/Router.ts
import { Router, requireAuthenticated } from '@guren/core'
import { PostPayloadSchema } from '../app/Http/Validators/PostValidator.js'
export function registerWebRoutes(router: Router): void {
router.aliasMiddleware('auth', requireAuthenticated({ redirectTo: '/login' }))
router.get('/posts', [PostController, 'index']).name('posts.index')
router.post('/posts', { name: 'posts.store', body: PostPayloadSchema }, [PostController, 'store'])
router.put('/posts/:id', { name: 'posts.update', body: PostPayloadSchema }, [PostController, 'update'])
router.middleware('auth').group((auth) => {
auth.get('/dashboard', [DashboardController, 'index'])
})
}
Route contract options — attach body, params, query schemas to routes:
- Schemas are metadata for codegen (not double-validated for Controller actions)
bunx guren codegen extracts schemas → generates typed ApiRoutes interface
- Frontend derives form types via
ApiRoutes['route.name']['body']
End-to-End Type Safety
Source: packages/cli/src/api-client-types.ts, packages/inertia-client/src/typed-forms.ts
Guren provides bidirectional type safety between frontend forms and backend validation:
Zod schema (Validator) → Route body option → codegen → ApiRoutes → Frontend form type
→ Controller.validateBody() → Runtime validation (422 on failure)
1. Define schema once (Validator file):
export const PostPayloadSchema = z.object({ title: z.string().min(1), body: z.string().min(1) })
2. Attach to route (routes/web.ts):
router.post('/posts', { name: 'posts.store', body: PostPayloadSchema }, [PostController, 'store'])
3. Frontend derives types (after bunx guren codegen):
import type { ApiRoutes } from '@/.guren/api-client.gen'
import type { RouteErrors } from '@guren/inertia-client/typed-forms'
import { route } from '@/.guren/routes.gen'
type PostFormData = ApiRoutes['posts.store']['body']
type PostErrors = RouteErrors<PostFormData>
form.post(route('posts.store'))
<Link href={route('posts.show', { id: post.id })}>
4. Controller validates at runtime (same schema):
const data = await this.validateBody(PostPayloadSchema)
Middleware
Source: packages/server/src/http/middleware/
import { defineMiddleware } from '@guren/core'
export const logRequest = defineMiddleware(async (ctx, next) => {
console.log(ctx.req.method, ctx.req.url)
await next()
})
Authentication
Source: packages/server/src/auth/
import { Router, requireAuthenticated } from '@guren/core'
const router = new Router()
.aliasMiddleware('auth', requireAuthenticated({ redirectTo: '/login' }))
router.middleware('auth').group((auth) => {
})
const user = await this.auth.user()
const user = await this.auth.userOrFail()
const isLoggedIn = await this.auth.check()
Additional auth features:
- API Tokens:
packages/server/src/auth/api-token.ts
- Email Verification:
packages/server/src/auth/email-verification.ts
- Password Reset:
packages/server/src/auth/password-reset.ts
Testing (@guren/testing)
Source: packages/testing/src/
Included by default in create-app-scaffolded apps' devDependencies; if missing, bun add -d @guren/testing.
import { TestApp } from '@guren/testing'
const app = await TestApp.create({
auth: {},
providers: [DatabaseProvider],
routes: registerWebRoutes,
})
await app.get('/posts').assertOk()
const csrf = await app.actingAs(user).withCsrf()
await csrf.post('/posts', { title: 'Hi' }).assertRedirect('/posts')
actingAs(user) / withCsrf() each return a new TestApp — chain or reassign, don't discard the result
- Without
auth, no session/CSRF middleware is mounted — app.json().post(...) skips CSRF entirely, fine for quick checks but not production-representative
- The main entry has no vitest dependency; DB lifecycle helpers (
useDatabaseTransactions etc.) use bun:test globals automatically — vitest projects import '@guren/testing/vitest' once in setup to register hooks
Extended Subsystems
Authorization (Gate & Policy)
Source: packages/server/src/authorization/
Gate.ts — Define abilities and policies
Policy.ts — Resource-based authorization
middleware.ts — Route-level authorization middleware
Events & Listeners
Source: packages/server/src/events/
Event.ts — Base event class
EventManager.ts — Event dispatcher
Listener.ts — Base listener class
builtin.ts — Built-in framework events
Register events in app/Providers/EventServiceProvider.ts.
Jobs & Queue
Source: packages/server/src/queue/
Job.ts — Base job class with handle() method
QueueManager.ts — Queue manager (memory, Redis drivers)
Worker.ts — Queue worker process
Drivers: packages/server/src/queue/drivers/
Mail
Source: packages/server/src/mail/
Mail.ts — Base mailable class
MailManager.ts — Mail manager
Transports:
MemoryTransport.ts — For testing
ResendTransport.ts — Resend API
SmtpTransport.ts — SMTP
Cache
Source: packages/server/src/cache/
CacheManager.ts — Cache manager
TaggedCache.ts — Tag-based cache invalidation
Stores:
MemoryStore.ts — In-memory
FileStore.ts — File-based
RedisStore.ts — Redis
Validation
Source: packages/server/src/http/validation/
Validator.ts — Validation engine
rules.ts — Built-in validation rules
FormRequest.ts — Form request validation (packages/server/src/http/FormRequest.ts)
Broadcasting
Source: packages/server/src/broadcasting/
BroadcastManager.ts — Broadcast manager
Channels: Channel.ts, PrivateChannel.ts, PresenceChannel.ts
Drivers: MemoryDriver.ts, RedisDriver.ts
Notifications
Source: packages/server/src/notifications/
Notification.ts — Base notification class
NotificationManager.ts — Notification dispatcher
Channels: MailChannel.ts, SlackChannel.ts, DatabaseChannel.ts, MemoryChannel.ts
Storage
Source: packages/server/src/storage/
StorageManager.ts — Storage manager
Drivers: LocalDriver.ts, MemoryDriver.ts, S3Driver.ts
Scheduling
Source: packages/server/src/scheduling/
Schedule.ts — Schedule definition
Scheduler.ts — Scheduler runner
ScheduledTask.ts — Individual scheduled task
CronParser.ts — Cron expression parser
I18n (Internationalization)
Source: packages/server/src/i18n/
I18nManager.ts — I18n manager
Translator.ts — Translation engine
pluralization.ts — Pluralization rules
Loaders: JsonLoader.ts, MemoryLoader.ts
Encryption
Source: packages/server/src/encryption/
Encrypter.ts — Encrypt/decrypt values
Hash.ts — Hashing utilities
Random.ts — Secure random generation
Health Checks
Source: packages/server/src/health/
HealthManager.ts — Health check manager
HealthCheck.ts — Base health check
Checks: DatabaseCheck.ts, RedisCheck.ts, CacheCheck.ts, MemoryCheck.ts, StorageCheck.ts, CustomCheck.ts
Error Handling
Source: packages/server/src/errors/
ExceptionHandler.ts — Global exception handler (supports duck-typed statusCode property)
HttpException.ts — Base HTTP exception
debug-page.ts — Debug error page
Exceptions: NotFoundHttpException.ts, ValidationException.ts, AuthenticationException.ts, AuthorizationException.ts, MethodNotAllowedException.ts
The ExceptionHandler automatically handles:
HttpException subclasses → uses their status code
- Any error with a
statusCode property (duck-typed) → uses that status code (e.g., ModelNotFoundException → 404)
- Other errors → 500 (message hidden unless debug mode)
Factory methods — throw directly from a controller method, no try/catch needed:
HttpException.badRequest(msg?)
HttpException.unauthorized(msg?)
HttpException.forbidden(msg?)
HttpException.notFound(msg?)
HttpException.conflict(msg?)
HttpException.unprocessable(msg?, errors?)
HttpException.internal(msg?)
new ValidationException({ email: ['Already registered'] })
ValidationException.withMessages({ email: 'Already registered' })
new AuthenticationException(message?, guard?, redirectTo?)
AuthenticationException.withRedirect(redirectTo, message?)
new AuthorizationException(message?, action?, resource?)
AuthorizationException.deny(resource?)
AuthorizationException.forAction(action, resource?)
new NotFoundHttpException(message?)
NotFoundHttpException.forModel('User', 123)
Model.findOrFail() throws ModelNotFoundException from @guren/orm — it does not extend HttpException; the handler picks it up via its duck-typed statusCode: 404.
Container & Service Providers
Source: packages/server/src/container/
Container.ts — IoC container
ServiceProvider.ts — Base service provider
Built-in providers: packages/server/src/providers/
Console Commands
Source: packages/server/src/console/
Command.ts — Base command class
ConsoleKernel.ts — Console kernel
Input.ts / Output.ts — IO handling
API Resources
Source: packages/server/src/http/resources/
Resource.ts — API resource transformer
ResourceCollection.ts — Collection of resources
Paginator.ts — Offset-based pagination
CursorPaginator.ts — Cursor-based pagination
Database (Factory & Seeder)
Source: packages/server/src/database/
Factory.ts — Model factory for testing
Seeder.ts — Database seeder
SeederRunner.ts — Seeder execution
Logging
Source: packages/server/src/logging/
LogManager.ts — Log manager
Logger.ts — Logger instance
Channels: ConsoleChannel.ts, FileChannel.ts, DailyFileChannel.ts
Redis
Source: packages/server/src/redis/
client.ts — Redis client
- Session, rate-limit, API token, email verification, password reset stores
Reference Locations
| Subsystem | Source Path |
|---|
| Controllers | packages/server/src/mvc/Controller.ts |
| Models | packages/orm/src/Model.ts |
| Routes | packages/server/src/mvc/Route.ts |
| Auth | packages/server/src/auth/ |
| Testing | packages/testing/src/ |
| Authorization | packages/server/src/authorization/ |
| Events | packages/server/src/events/ |
| Queue/Jobs | packages/server/src/queue/ |
| Mail | packages/server/src/mail/ |
| Cache | packages/server/src/cache/ |
| Validation | packages/server/src/http/validation/ |
| Broadcasting | packages/server/src/broadcasting/ |
| Notifications | packages/server/src/notifications/ |
| Storage | packages/server/src/storage/ |
| Scheduling | packages/server/src/scheduling/ |
| I18n | packages/server/src/i18n/ |
| Encryption | packages/server/src/encryption/ |
| Health Checks | packages/server/src/health/ |
| Error Handling | packages/server/src/errors/ |
| Container | packages/server/src/container/ |
| Console | packages/server/src/console/ |
| API Resources | packages/server/src/http/resources/ |
| Database/Seeder | packages/server/src/database/ |
| Logging | packages/server/src/logging/ |
| Redis | packages/server/src/redis/ |
| Example App | examples/blog/ |
| API Example | examples/api/ |
| Docs | web/ |