API Reference

Complete reference for every utility module in src/lib/. Function signatures, parameters, return types, and usage examples.

# Auth lib/auth.ts

Server-side authentication helpers built on Clerk and Prisma. Use these in Server Components, Server Actions, and API routes.

# getCurrentUser()

Returns the current authenticated user from the database, or null if not signed in.

// Signature
async function getCurrentUser(): Promise<User | null>
// Usage in a Server Component
import { getCurrentUser } from "@/lib/auth";

export default async function DashboardPage() {
  const user = await getCurrentUser();
  if (!user) redirect("/sign-in");
  return <p>Hello {user.name}</p>;
}

# requireUser()

Returns the current user or throws "Unauthorized". Use in protected pages and API routes.

// Signature
async function requireUser(): Promise<User>
// Usage in an API route
import { requireUser } from "@/lib/auth";

export async function GET() {
  const user = await requireUser(); // throws if not authenticated
  return NextResponse.json({ user });
}

# requireAdmin()

Returns the current user if they have the ADMIN role, otherwise throws "Forbidden".

// Signature
async function requireAdmin(): Promise<User>
// Usage
import { requireAdmin } from "@/lib/auth";

export async function DELETE(req: Request) {
  await requireAdmin(); // throws "Forbidden" if not admin
  // ... admin-only logic
}

# syncUser()

Upserts the current Clerk user into the database. Called automatically in the dashboard layout as a fallback for webhook sync.

// Signature
async function syncUser(): Promise<User | null>
// Usage in dashboard layout
import { syncUser } from "@/lib/auth";

export default async function DashboardLayout({ children }) {
  await syncUser(); // ensures user exists in DB
  return <div>{children}</div>;
}

# getUserPlan()

Returns the user's current plan enum value (FREE, PRO, or BUSINESS).

// Signature
function getUserPlan(user: { plan: Plan }): Plan
// Usage
const plan = getUserPlan(user); // "FREE" | "PRO" | "BUSINESS"

# isAdmin()

Returns true if the user has the ADMIN role.

// Signature
function isAdmin(user: { role: Role }): boolean
// Usage
if (isAdmin(user)) {
  // show admin controls
}

# hasActiveSubscription()

Returns true if the user has a paid plan with a valid (non-expired) subscription period.

// Signature
function hasActiveSubscription(user: {
  stripeCurrentPeriodEnd: Date | null;
  plan: Plan;
}): boolean
// Usage
if (!hasActiveSubscription(user)) {
  redirect("/pricing"); // prompt upgrade
}

# Stripe lib/stripe.ts

Stripe client singleton and plan configuration. The Stripe instance is lazily initialized to avoid build-time errors when environment variables are missing.

# getStripe()

Returns the Stripe client instance. Initializes on first call using STRIPE_SECRET_KEY.

// Signature
function getStripe(): Stripe
// Usage
import { getStripe } from "@/lib/stripe";

const stripe = getStripe();
const session = await stripe.checkout.sessions.create({ ... });

# PLANS

The plan configuration object. Three tiers: free, pro, and business. Each plan has name, description, pricing, Stripe Price IDs, limits, and feature lists.

// Structure
const PLANS = {
  free: {
    name: "Free",
    description: "To get started",
    price: { monthly: 0, yearly: 0 },
    stripePriceId: { monthly: null, yearly: null },
    limits: { projects: 1, apiCalls: 100 },
    features: ["1 project", "100 API calls/month", "Community support"],
  },
  pro: {
    name: "Pro",
    description: "For professionals",
    price: { monthly: 29, yearly: 290 },
    stripePriceId: { monthly: "price_...", yearly: "price_..." },
    limits: { projects: 10, apiCalls: 10000 },
    features: ["10 projects", "10,000 API calls/month", "Priority support", "API access", "CSV exports"],
  },
  business: {
    name: "Business",
    description: "For teams",
    price: { monthly: 99, yearly: 990 },
    stripePriceId: { monthly: "price_...", yearly: "price_..." },
    limits: { projects: -1, apiCalls: -1 }, // -1 = unlimited
    features: ["Unlimited projects", "Unlimited API calls", "Dedicated support", "API access", "CSV exports", "Webhooks", "SSO (coming soon)"],
  },
} as const;
// Usage
import { PLANS, type PlanKey } from "@/lib/stripe";

const proLimits = PLANS.pro.limits; // { projects: 10, apiCalls: 10000 }
const proPrice = PLANS.pro.price.monthly; // 29

# PlanKey

Type alias for the keys of the PLANS object.

type PlanKey = "free" | "pro" | "business";

# Email lib/email.ts

Transactional emails via Resend. The Resend client is lazily initialized using RESEND_API_KEY.

# sendWelcomeEmail()

Sends a welcome email to a newly registered user.

// Signature
async function sendWelcomeEmail(email: string, name: string): Promise<void>
Param Type Description
emailstringRecipient email address
namestringUser's display name
// Usage
import { sendWelcomeEmail } from "@/lib/email";

await sendWelcomeEmail("user@example.com", "John");

# sendSubscriptionEmail()

Sends a confirmation email when a user subscribes or upgrades their plan.

// Signature
async function sendSubscriptionEmail(email: string, plan: string): Promise<void>
Param Type Description
emailstringRecipient email address
planstringPlan name (e.g. "Pro", "Business")
// Usage
import { sendSubscriptionEmail } from "@/lib/email";

await sendSubscriptionEmail("user@example.com", "Pro");

# Blog lib/blog.ts

MDX blog post reader. Reads .mdx files from content/blog/ and parses frontmatter with gray-matter.

# BlogPost Interface

interface BlogPost {
  slug: string;        // filename without .mdx extension
  title: string;       // from frontmatter
  description: string; // short description for SEO/cards
  date: string;        // ISO date string
  author: string;      // author name, defaults to "Admin"
  image?: string;      // optional cover image URL
  tags: string[];      // array of tag strings
  content: string;     // raw MDX content (without frontmatter)
  published: boolean;  // false to hide from listing
}

# getAllPosts()

Returns all published blog posts, sorted by date (newest first). Returns an empty array if the content/blog/ directory does not exist.

// Signature
function getAllPosts(): BlogPost[]
// Usage
import { getAllPosts } from "@/lib/blog";

const posts = getAllPosts();
// [{ slug: "launch-announcement", title: "We launched!", ... }, ...]

# getPostBySlug()

Returns a single blog post by its slug, or null if not found.

// Signature
function getPostBySlug(slug: string): BlogPost | null
// Usage
import { getPostBySlug } from "@/lib/blog";

const post = getPostBySlug("launch-announcement");
if (!post) notFound();

# Utils lib/utils.ts

General-purpose utility functions for class merging, formatting, and string manipulation.

# cn()

Merges Tailwind CSS class names with conflict resolution. Combines clsx and tailwind-merge.

// Signature
function cn(...inputs: ClassValue[]): string
// Usage
import { cn } from "@/lib/utils";

<div className={cn("px-4 py-2", isActive && "bg-blue-500", className)} />

# absoluteUrl()

Prepends NEXT_PUBLIC_APP_URL to a path to create an absolute URL.

// Signature
function absoluteUrl(path: string): string
// Usage
absoluteUrl("/dashboard") // "https://yourdomain.com/dashboard"

# formatDate()

Formats a date as a human-readable string using Intl.DateTimeFormat (en-US, long month).

// Signature
function formatDate(date: Date | string): string
// Usage
formatDate("2024-01-15") // "January 15, 2024"
formatDate(new Date())   // "March 20, 2026"

# formatPrice()

Formats a number as a currency string using Intl.NumberFormat. Defaults to USD.

// Signature
function formatPrice(price: number, currency?: string): string
// Usage
formatPrice(29)          // "$29.00"
formatPrice(99, "EUR")   // "€99.00"

# truncate()

Truncates a string to a maximum length, appending ... if truncated.

// Signature
function truncate(str: string, length: number): string
// Usage
truncate("Hello World", 5) // "Hello..."
truncate("Hi", 5)          // "Hi"

# slugify()

Converts a string into a URL-friendly slug. Handles French accented characters.

// Signature
function slugify(str: string): string
// Usage
slugify("Hello World!")      // "hello-world"
slugify("Créer un projet")   // "creer-un-projet"

# Rate Limiting lib/rate-limit.ts

In-memory rate limiter using a Map. Automatically cleans up stale entries every 60 seconds. Suitable for single-instance deployments; use Redis for multi-instance.

# rateLimit()

// Signature
function rateLimit(
  key: string,
  limit?: number,    // default: 10
  windowMs?: number  // default: 60000 (1 minute)
): { success: boolean; remaining: number }
Param Type Default Description
keystringUnique identifier (e.g. IP address, user ID)
limitnumber10Max requests per window
windowMsnumber60000Time window in milliseconds
// Usage in an API route
import { rateLimit } from "@/lib/rate-limit";

export async function POST(req: Request) {
  const ip = req.headers.get("x-forwarded-for") ?? "unknown";
  const { success, remaining } = rateLimit(ip, 5, 3600000); // 5 per hour

  if (!success) {
    return NextResponse.json(
      { error: "Too many requests" },
      { status: 429 }
    );
  }

  // ... handle request
}

# Config lib/config.ts

Central site configuration object used across the application for metadata, SEO, and social links.

# siteConfig

// Structure
const siteConfig = {
  name: string,        // from NEXT_PUBLIC_APP_NAME or "SaaS Starter"
  url: string,         // from NEXT_PUBLIC_APP_URL or "http://localhost:3000"
  description: string, // "The ultimate SaaS starter kit to launch your product fast."
  links: {
    twitter: string,   // Twitter/X profile URL
    github: string,    // GitHub repository URL
  },
  creator: string,     // Your name
  ogImage: string,     // "/og.png"
};
// Usage
import { siteConfig } from "@/lib/config";

export const metadata = {
  title: siteConfig.name,
  description: siteConfig.description,
  openGraph: { images: [siteConfig.ogImage] },
};