Schema Types Reference
This guide documents all TypeScript types available in the @repo/database package. Understanding these types is essential for working with the database layer and ensuring type safety across the codebase.
Type Categories
Base
Direct table rowsExtended
With relationsUtility
Reusable fragments// Importing types from @repo/databaseimport type { userType, // Base types TaskWithLabels, // Extended types UserSummary, // Utility types} from "@repo/database";User Types
Section titled “User Types”Types for users, authentication, and sessions.
userType Base
Full user record from the user table. Contains all user profile data including authentication status and ban information.
type userType = { id: string; name: string; displayName: string | null; email: string; emailVerified: boolean; image: string | null; createdAt: Date; updatedAt: Date; role: string | null; banned: boolean | null; banReason: string | null; banExpires: Date | null;};UserSummary Utility
Lightweight user data for display purposes. Used whenever you need to show user information (avatars, names) without loading the full user record. Derived from userType.
Used in
TaskWithLabels.createdBy TaskWithLabels.assignees CommentsWithAuthor.createdBy taskTimelineWithActor.actor ReleaseWithTasks.createdBy type UserSummary = { id: string; name: string; image: string | null; displayName?: string | null;};
// Related constants for queries:import { userSummaryColumns } from "@repo/database";
const task = await db.query.task.findFirst({ with: { createdBy: { columns: userSummaryColumns }, },});sessionType Base
User session record for authentication. Tracks login sessions with device info.
type sessionType = { id: string; expiresAt: Date; token: string; createdAt: Date; updatedAt: Date; ipAddress: string | null; userAgent: string | null; userId: string; impersonatedBy: string | null;};accountType Base
OAuth account linkage (GitHub, Doras, etc.). Links external auth providers to users.
type accountType = { id: string; accountId: string; providerId: string; userId: string; accessToken: string | null; refreshToken: string | null; idToken: string | null; accessTokenExpiresAt: Date | null; refreshTokenExpiresAt: Date | null; scope: string | null; password: string | null; createdAt: Date; updatedAt: Date;};Task Types
Section titled “Task Types”Types for tasks and related entities - the core of Sayr’s functionality.
taskType Base
Base task record from the database. Note: createdBy is a string (user ID) in this type, not a UserSummary.
type taskType = { id: string; organizationId: string; shortId: number | null; visible: "public" | "private"; createdAt: Date | null; updatedAt: Date | null; title: string | null; description: NodeJSON | null; status: "backlog" | "todo" | "in-progress" | "done" | "canceled"; priority: "none" | "low" | "medium" | "high" | "urgent"; createdBy: string | null; // User ID category: string | null; releaseId: string | null; voteCount: number;};TaskWithLabels Extended
The primary task type used throughout the application. Includes all related data needed for display in task lists, detail pages, and API responses.
Used in
Task list views Task detail pages WebSocket updates API responses type TaskWithLabels = Omit<taskType, "createdBy"> & { labels: labelType[]; assignees: UserSummary[]; createdBy?: UserSummary | null; organization?: { id: string; name: string; slug: string; logo: string | null; }; comments?: CommentsWithAuthor; githubIssue?: githubIssueType; description: NodeJSON;};
// Uses Omit because base type has createdBy: string// but we need createdBy: UserSummarytaskAssigneeType Base
Join table for task-user assignments. Links users to tasks they're assigned to.
type taskAssigneeType = { taskId: string; organizationId: string; userId: string;};taskVoteType Base
User vote on a task. Supports both authenticated and anonymous voting via fingerprinting.
type taskVoteType = { id: string; organizationId: string; taskId: string; userId: string | null; // null for anon anonHash: string | null; // fingerprint createdAt: Date | null;};Comment Types
Section titled “Comment Types”Types for task comments, reactions, and edit history.
taskCommentType Base
Base comment record. Supports public and internal visibility for team-only comments.
type taskCommentType = { id: string; organizationId: string; taskId: string | null; createdAt: Date | null; updatedAt: Date | null; content: NodeJSON | null; createdBy: string | null; // User ID visibility: "public" | "internal";};CommentsWithAuthor Extended
Comments array with resolved author information. Used when displaying comments in task detail views.
Used in
Task detail views Comment lists TaskWithLabels.comments type CommentsWithAuthor = Array< Omit<taskCommentType, "createdBy"> & { createdBy: UserSummary | null; content: NodeJSON; }>;taskCommentReactionType Base
Emoji reaction on a comment. Each user can react once per emoji per comment.
type taskCommentReactionType = { id: string; organizationId: string; taskId: string; commentId: string; userId: string; emoji: string; // Raw emoji character createdAt: Date | null;};taskCommentHistoryType Base
Edit history for a comment. Stores previous versions when comments are edited.
type taskCommentHistoryType = { id: string; organizationId: string; taskId: string | null; commentId: string; editedAt: Date | null; editedBy: string | null; content: NodeJSON | null;};Timeline Types
Section titled “Timeline Types”Types for task activity timeline and event tracking.
taskTimelineType Base
Base timeline event record. Tracks all changes made to a task including status changes, assignments, and comments.
type taskTimelineType = { id: string; taskId: string; organizationId: string; actorId: string | null; eventType: TimelineEventType; fromValue: unknown; // Previous value toValue: unknown; // New value content: NodeJSON | null; createdAt: Date | null;};
type TimelineEventType = | "status_change" | "priority_change" | "comment" | "label_added" | "label_removed" | "assignee_added" | "assignee_removed" | "created" | "updated" | "category_change" | "release_change";taskTimelineWithActor Extended
Timeline event with resolved actor and visibility. Used when displaying task activity feeds.
Used in
Task activity feeds Timeline components type taskTimelineWithActor = taskTimelineType & { visibility: "public" | "internal"; actor?: UserSummary; updatedAt?: Date | null; reactions?: { total: number; reactions: Record<string, { count: number; users: string[]; }>; };};Organization Types
Section titled “Organization Types”Types for organizations, membership, and teams.
organizationType Base
Base organization record. Contains org profile and settings.
type organizationType = { id: string; name: string; slug: string; logo: string | null; bannerImg: string | null; description: string; createdAt: Date | null; updatedAt: Date | null; privateId: string;};OrganizationWithMembers Extended
Organization with its member list loaded. Used for org details with team members.
Used in
Organization settings Member management interface OrganizationWithMembers extends organizationType { members: (OrganizationMemberType & { user: userType })[];}OrganizationMemberType Base
A user's membership in an organization. Links users to orgs.
type OrganizationMemberType = { id: string; userId: string; organizationId: string; createdAt: Date | null;};OrganizationTeamType Base
A team within an organization with granular permissions.
type OrganizationTeamType = { id: string; organizationId: string; name: string; description: string | null; permissions: TeamPermissions; createdAt: Date | null; updatedAt: Date;};TeamPermissions Utility
Granular permission structure for teams. Uses Discord-style inheritance: if ANY team grants a permission, the user has it.
interface TeamPermissions { admin: { administrator: boolean; manageMembers: boolean; manageTeams: boolean; }; content: { manageCategories: boolean; manageLabels: boolean; manageViews: boolean; }; tasks: { create: boolean; editAny: boolean; deleteAny: boolean; assign: boolean; changeStatus: boolean; changePriority: boolean; }; moderation: { manageComments: boolean; approveSubmissions: boolean; manageVotes: boolean; };}Release Types
Section titled “Release Types”Types for releases and milestones.
releaseType Base
Base release record for tracking milestones and versioned releases.
type releaseType = { id: string; organizationId: string; name: string; slug: string; description: NodeJSON | null; status: "planned" | "in-progress" | "released" | "archived"; targetDate: Date | null; releasedAt: Date | null; color: string | null; icon: string | null; createdBy: string | null; createdAt: Date | null; updatedAt: Date | null;};ReleaseWithTasks Extended
Release with its associated tasks. Used for release detail pages and roadmap views.
Used in
Release pages Roadmap views type ReleaseWithTasks = Omit<releaseType, "createdBy"> & { tasks: TaskWithLabels[]; createdBy?: UserSummary | null;};Label & Category Types
Section titled “Label & Category Types”Types for organizing tasks with labels and categories.
labelType Base
A reusable label within an organization. Supports visibility settings for public/private labels.
type labelType = { id: string; organizationId: string; name: string; color: string | null; // Hex (#RRGGBB) visible: "public" | "private"; createdAt: Date | null;};taskLabelType Base
Join table for task-label assignments. Links labels to tasks.
type taskLabelType = { id: string; taskId: string; organizationId: string; labelId: string;};categoryType Base
A category/project within an organization. Used for grouping related tasks.
type categoryType = { id: string; organizationId: string; name: string; color: string | null; // HSLA icon: string | null; createdAt: Date | null;};Template Types
Section titled “Template Types”Types for task templates that pre-fill values when creating new tasks.
issueTemplateType Base
Base template record for creating tasks with preset values.
type issueTemplateType = { id: string; organizationId: string; name: string; titlePrefix: string | null; description: NodeJSON | null; status: string | null; priority: string | null; categoryId: string | null; createdAt: Date | null; updatedAt: Date | null;};issueTemplateWithRelations Extended
Template with labels, assignees, and category loaded. Used when displaying and applying templates.
Used in
Template selection UI Task creation forms type issueTemplateWithRelations = issueTemplateType & { labels: labelType[]; assignees: UserSummary[]; category?: { id: string; name: string; color: string | null; icon: string | null; } | null;};GitHub Integration Types
Section titled “GitHub Integration Types”Types for GitHub sync features and issue linking.
githubInstallationType Base
GitHub App installation record. Tracks which orgs have installed the GitHub app.
type githubInstallationType = { id: string; organizationId: string | null; installationId: number; userId: string | null; createdAt: Date; updatedAt: Date;};githubRepositoryType Base
A repository linked to an installation. Maps GitHub repos to Sayr categories.
type githubRepositoryType = { id: string; installationId: number; repoId: number; repoName: string; organizationId: string | null; categoryId: string | null; userId: string | null; createdAt: Date; updatedAt: Date;};githubIssueType Base
A GitHub issue synced to a Sayr task. Links external issues to internal tasks.
type githubIssueType = { id: string; repositoryId: string; organizationId: string; issueNumber: number; issueUrl: string; taskId: string; createdAt: Date; updatedAt: Date;};Other Types
Section titled “Other Types”Additional utility and configuration types.
NodeJSON Utility
Represents TipTap/ProseMirror rich text content stored as JSONB in the database.
Used in
task.description taskComment.content taskTimeline.content release.description interface NodeJSON { type: string; marks?: Array<{ type: string; attrs?: Record<string, any>; }>; text?: string; content?: NodeJSON[]; attrs?: Record<string, any>;}savedViewType Base
A saved filter/view configuration for tasks. Stores user-created views with filters and display settings.
type savedViewType = { id: string; organizationId: string; createdById: string | null; name: string; slug: string | null; logo: string | null; filterParams: string; viewConfig: { mode: "list" | "kanban"; groupBy: "status" | "priority" | "assignee" | "category"; subGroupBy?: "status" | "priority" | "assignee" | "category" | "none"; showCompletedTasks: boolean; color?: string; icon?: string; }; createdAt: Date | null; updatedAt: Date | null;};inviteType Base
Organization invitation record. Tracks pending and accepted invites.
type inviteType = { id: string; organizationId: string; email: string; userId: string | null; invitedById: string; role: string; inviteCode: string; status: "pending" | "accepted" | "declined" | "expired"; createdAt: Date | null; expiresAt: Date | null;};ApikeyType Base
API key record with rate limiting support. Used for external API access.
type ApikeyType = { id: string; name: string | null; start: string | null; prefix: string | null; key: string; // Hashed userId: string; enabled: boolean; rateLimitEnabled: boolean; rateLimitTimeWindow: number | null; rateLimitMax: number | null; requestCount: number; remaining: number | null; lastRequest: Date | null; expiresAt: Date | null; permissions: string | null; metadata: unknown; createdAt: Date; updatedAt: Date;};Type Inference Patterns
Section titled “Type Inference Patterns”Inferring Types from Schema
Section titled “Inferring Types from Schema”Drizzle provides automatic type inference from your schema definitions:
import { schema } from "@repo/database";
// SELECT result type (what you get from queries)type Task = typeof schema.task.$inferSelect;
// INSERT input type (what you pass to inserts)type NewTask = typeof schema.task.$inferInsert;Creating Extended Types
Section titled “Creating Extended Types”When queries include relations, define extended types in packages/database/schema/index.ts:
// Good: Use Omit when overriding a field typeexport type TaskWithLabels = Omit<taskType, "createdBy"> & { labels: labelType[]; createdBy?: UserSummary | null; // Changed from string to UserSummary};
// Bad: Intersection creates impossible type (string & UserSummary = never)export type TaskWithLabels = taskType & { createdBy: UserSummary;};Using UserSummary Consistently
Section titled “Using UserSummary Consistently”Always use userSummaryColumns when fetching user data for display:
import { userSummaryColumns } from "@repo/database";
const task = await db.query.task.findFirst({ where: eq(task.id, taskId), with: { createdBy: { columns: userSummaryColumns }, assignees: { with: { user: { columns: userSummaryColumns } }, }, },});Related Documentation
Section titled “Related Documentation”- Database Guide - Working with Drizzle ORM
- Architecture - How the database fits into the system
- Adding Features - Complete feature implementation guide