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 |
|---|---|---|
email | string | Recipient email address |
name | string | User'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 |
|---|---|---|
email | string | Recipient email address |
plan | string | Plan 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 |
|---|---|---|---|
key | string | — | Unique identifier (e.g. IP address, user ID) |
limit | number | 10 | Max requests per window |
windowMs | number | 60000 | Time 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] },
};