Skip to main content

Server Actions

Terra uses Next.js Server Actions for all data mutations.

Action Files

FilePurpose
airtable.tsAirtable OAuth and sync
notifications.tsEmail/SMS sending
insights.tsAnalytics queries
applicants.tsApplicant management
status.tsSubmission status
domains.tsCustom domain config
workspaces.tsFolder management
agencies.tsAgency management
form-versions.tsDraft/publish
webhooks.tsWebhook config
team.tsPermissions
settings.tsOrg settings

Common Patterns

Permission Check

"use server";

export async function updateSubmission(id: string, data: Data) {
  const user = await requireAdmin();  // Throws if unauthorized

  await supabaseAdmin
    .from("form_submissions")
    .update(data)
    .eq("id", id);

  await logAudit(user.id, "submission.updated", { id });

  return { success: true };
}

ActionResult Pattern

type ActionResult<T> =
  | { success: true; data: T }
  | { success: false; error: string };

export async function createForm(data: FormData): Promise<ActionResult<Form>> {
  try {
    const user = await requireAdmin();
    const form = await insertForm(data);
    return { success: true, data: form };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

Revalidation

import { revalidatePath } from "next/cache";

export async function publishForm(id: string) {
  await updateFormStatus(id, "published");
  revalidatePath(`/forms/${id}`);
  revalidatePath("/forms");
}