GuidesNext.js server actions

Next.js server actions

Server actions are a perfect place for Eda: they’re where your UI triggers real side effects. The SDK is zero-dependency and edge-safe, so it works in both the Node and Edge runtimes.

Construct the client once

lib/eda.ts
import { Eda } from "@eda-holding-inc/sdk";
export const eda = new Eda({ apiKey: process.env.EDA_API_KEY! });

Gate the action

app/actions/refund.ts
"use server";
import { eda } from "@/lib/eda";
import { auth } from "@/lib/auth";
 
export async function refundAction(customerId: string, amount: number) {
  const { userId } = await auth();
 
  const decision = await eda.check({
    agent: "dashboard-user",
    action: "refund_customer",
    params: { amount, customerId },
    metadata: { userId }, // who triggered it, for the audit log
  });
 
  if (decision.status !== "approved") {
    return { ok: false, reason: decision.reason, status: decision.status };
  }
 
  await stripe.refunds.create({ customer: customerId, amount });
  return { ok: true };
}

Handle the pending case in the UI

app/refund-button.tsx
"use client";
import { refundAction } from "@/app/actions/refund";
 
export function RefundButton({ customerId, amount }: { customerId: string; amount: number }) {
  async function onClick() {
    const res = await refundAction(customerId, amount);
    if (res.status === "pending") toast("Sent for approval — you'll be notified.");
    else if (!res.ok) toast.error(res.reason);
    else toast.success("Refunded.");
  }
  return <button onClick={onClick}>Refund ${amount}</button>;
}

Edge runtime

The SDK uses only the standard fetch, so it runs unchanged on the edge:

export const runtime = "edge";

If you run in an environment with a non-global fetch, inject one:

const eda = new Eda({ apiKey: process.env.EDA_API_KEY!, fetch: myFetch });

Set EDA_API_KEY in your hosting provider’s environment (Vercel/Amplify/…), not in the client bundle. The SDK reads it server-side only.

Fail posture

For a dashboard action, failMode: "closed" (the default) is usually right — if Eda is unreachable, the refund simply doesn’t run and the user can retry. Flip to "open" only for actions where a missed gate is acceptable.