@izri/trpc
Type-safe API layer using tRPC for the Izri platform
Overview
The tRPC package provides a complete type-safe API layer that connects the frontend and backend. It uses tRPC for end-to-end typing, Zod for validation, and Drizzle for database operations. Authentication integrates Better Auth via the Hono adapter.
Package Structure
packages/trpc/
├── src/
│ ├── index.ts # Main package exports
│ ├── trpc.ts # tRPC instance and procedures
│ ├── context.ts # Context creation (Hono + Better Auth)
│ └── routers/
│ ├── index.ts # Root router (AppRouter)
│ ├── auth.ts # Authentication & session management
│ ├── projects.ts # Project management
│ ├── organizations.ts # Organization operations
│ └── analysis.ts # Repository analysis
└── package.json
Context
Context exposes database and auth/session data and raw headers:
interface Context {
db: DrizzleDB
user: AuthenticatedUser | null
session: Session | null
isAuthenticated: boolean
headers: Headers
}
Created in src/context.ts using Better Auth:
const sessionData = await auth.api.getSession({ headers })
// user, session, isAuthenticated derived from sessionData
Note: Permissions are organization-scoped; there are no global user roles.
Base procedures
export const publicProcedure = t.procedure
export const protectedProcedure = t.procedure.use(/* checks ctx.isAuthenticated */)
export const router = t.router
Routers and procedures (highlights)
- Root: auth, organizations, projects, analysis, and a public health query
Auth
- getSession (public): returns { isAuthenticated, session, user }
- listSessions (protected)
- invalidateSession (protected)
- revokeSession (protected)
- revokeOtherSessions (protected)
- getProfile (protected)
- updateProfile (protected)
- getAuditLogs (protected)
- initializeNewUser (public): post-signup helper that creates a default organization for the new user
API tokens: functionality is temporarily removed during Better Auth migration; see docs/features/api-keys.md
Organizations
- create (protected): create org; creator becomes OWNER
- update (protected): update basic fields; requires OWNER/ADMIN membership
- list (protected): list orgs with the caller’s role
- getBySlug (protected): fetch details by slug
- delete (protected): delete org (owner only)
- createDefaultOrganization helper is exported from this router and used by auth.initializeNewUser
Internals: slugs validated (lowercase letters, numbers, hyphens). IDs generated with ulid() from the ulid package.
Projects
- getProjects (public): list caller’s projects; optional organizationId filter; includes latest test run per project
- getProject (protected): fetch by id; returns settings and all runs
- createProject (protected): checks for repository duplication per user; creates default project_settings automatically
- updateProject (protected)
- deleteProject (protected)
- getProjectAnalyses (protected)
- getLatestProjectAnalysis (protected)
IDs for projects and project_settings created with ulid() from the ulid package.
Analysis
- analyzeRepository (protected): verifies project ownership, runs repository analysis, and stores results in project_analyses using an upsert on (projectId, commitSha)
Current analyzer used: RepositoryAnalyzer (legacy). EnhancedRepositoryAnalyzer is available in @izri/repo-analyzer but not yet wired here. See repo-analyzer docs for migration guidance.
Examples
// Client example
const { project } = await trpc.projects.createProject.mutate({
name: 'My Project',
repository: 'https://github.com/user/repo',
branch: 'main',
language: 'typescript',
framework: 'nextjs',
organizationId: 'org_123',
userEmail: 'user@example.com',
})
// Health check
await trpc.health.query() // { ok: true }
Notes
- Use env via @izri/shared in any server using this router.
- ID strategy: All tables (auth and application) use ULID generated via
ulid()from theulidpackage; all stored as varchar(191). - Org-scoped permission model applies to all protected procedures.