HyperSaaS
FrontendData Fetching

Type System

Zod schemas and inferred TypeScript types.

HyperSaaS uses Zod schemas as the single source of truth for data types. TypeScript types are inferred from schemas, ensuring zero drift between validation and type definitions.

Schema Organization

src/lib/validations/
├── common.ts       # Base schema, enums (roles, statuses, providers)
├── auth.ts         # Login, register, password reset schemas
├── workspaces.ts   # Workspace, team, membership, invitation schemas
├── chat.ts         # Chat session, message, AI settings schemas
├── documents.ts    # Document, knowledge base schemas
├── billing.ts      # Subscription, plan, price schemas
└── contact.ts      # Contact form schema

Pattern: Schema → Type

// lib/validations/chat.ts
import { z } from "zod";

export const chatSessionSchema = z.object({
  id: z.string().uuid(),
  name: z.string().nullable(),
  status: z.enum(["active", "completed", "archived"]),
  ai_provider: z.enum(["openai", "anthropic", "google", "groq", "deepseek"]),
  ai_model: z.string(),
  agent_framework: z.enum(["none", "langgraph", "pydantic_ai"]),
  system_prompt: z.string(),
  temperature: z.number().nullable(),
  max_tokens: z.number().nullable(),
  top_p: z.number().nullable(),
  frequency_penalty: z.number().nullable(),
  presence_penalty: z.number().nullable(),
  stream: z.boolean(),
  model_parameters: z.record(z.any()),
  is_shared: z.boolean(),
  is_public: z.boolean(),
  shareable_link_id: z.string().uuid(),
  created_at: z.string(),
  updated_at: z.string(),
});

// Type inferred from schema
export type ChatSession = z.infer<typeof chatSessionSchema>;

Common Schemas

Base Schema

// lib/validations/common.ts
export const baseSchema = z.object({
  id: z.string().uuid(),
  created_at: z.string(),
  updated_at: z.string(),
});

Enum Schemas

export const aiProviderSchema = z.enum([
  "openai", "anthropic", "google", "groq", "deepseek"
]);

export const agentFrameworkSchema = z.enum([
  "none", "langgraph", "pydantic_ai"
]);

export const messageRoleSchema = z.enum([
  "user", "assistant", "system", "tool"
]);

export const processingStatusSchema = z.enum([
  "pending", "processing", "completed", "failed"
]);

Key Types

Auth Types

// lib/types/auth.ts
interface UserDetails {
  id: string;
  email: string;
  name: string;
  username: string;
  image?: string;
  subscription_status?: string;
  has_active_subscription?: boolean;
}

interface LoginFormData {
  email: string;
  password: string;
}

interface RegisterFormData {
  email: string;
  name: string;
  password: string;
  re_password: string;
}

Workspace Types

// lib/types/workspaces.ts
interface Workspace {
  id: string;
  name: string;
  slug: string;
  owner: string;
  status: "active" | "archived";
  memberships: WorkspaceMembership[];
  teams: Team[];
  created_at: string;
  updated_at: string;
}

interface WorkspaceMembership {
  id: string;
  user: string;
  workspace: string;
  role: "admin" | "member";
}

Chat Types

// lib/types/chat.ts
interface Message {
  id: string;
  session: string;
  role: "user" | "assistant" | "system" | "tool";
  content: string;
  parent_message: string | null;
  sequence_number: number;
  attachments: any[];
  map_data: Record<string, any> | null;
  created_at: string;
}

Document Types

// lib/types/documents.ts
interface KnowledgeBase {
  id: string;
  name: string;
  description: string;
  is_shared_with_workspace: boolean;
  document_count: number;
  created_at: string;
}

interface Document {
  id: string;
  name: string;
  source_type: "file" | "web_url" | "youtube";
  source_url: string;
  file_type: string;
  processing_status: "pending" | "processing" | "completed" | "failed";
  chunk_count: number;
  created_at: string;
}

Billing Types

// lib/types/billing.ts
interface Subscription {
  subscription_id: string;
  status: "active" | "trialing" | "past_due" | "canceled" | "incomplete" | "ended";
  period_start: string;
  period_end: string;
  cancel_at_period_end: boolean;
  trial_start: string | null;
  trial_end: string | null;
}

Input Validation Schemas

Schemas for form inputs use stricter validation:

// lib/validations/chat.ts
export const chatSessionCreateSchema = z.object({
  name: z.string().min(1).max(255),
  ai_provider: aiProviderSchema,
  ai_model: z.string().min(1),
  agent_framework: agentFrameworkSchema.default("langgraph"),
  system_prompt: z.string().default(""),
  temperature: z.number().min(0).max(2).nullable().default(null),
  max_tokens: z.number().positive().nullable().default(null),
  top_p: z.number().min(0).max(1).nullable().default(null),
  model_parameters: z.string().max(10240).default("{}"),  // JSON string, max 10KB
});

Runtime Validation

Schemas validate API responses at runtime:

const { data } = await fetchApiData(
  `/api/workspaces/${id}/chats/`,
  z.array(chatSessionSchema)  // Validates response shape
);
// data is ChatSession[] | null — guaranteed to match the schema

This catches backend API changes at the data boundary rather than deep in component logic.

On this page