Request

Move validation out of the route body so the handler only describes successful work.

Before
export async function POST(request: Request) {
  const body = await request.json();

  if (!body.email || !body.teamId) {
    return Response.json({ error: "Invalid invite" }, { status: 400 });
  }

  return createInvite(body.teamId, body.email);
}
After
const inviteInput = z.object({
  teamId: z.string().min(1),
  email: z.string().email(),
});

export const inviteMember = action(inviteInput, async (input) => {
  return createInvite(input.teamId, input.email);
});

State

Replace duplicated loading branches with a compact async state machine.

Before
let loading = false;
let error: string | null = null;
let sent = false;

async function submit() {
  loading = true;
  error = null;
  sent = false;
  // ...
}
After
type InviteState =
  | { status: "idle" }
  | { status: "sending" }
  | { status: "sent" }
  | { status: "failed"; error: string };

let state: InviteState = { status: "idle" };

Response

Return one stable shape so callers do not need to inspect transport details.

Before
const response = await fetch("/api/invite", {
  method: "POST",
  body: JSON.stringify(input),
});

if (!response.ok) {
  throw new Error("Invite failed");
}
After
const result = await inviteMember(input);

if (result.error) {
  showError(result.error.message);
}

showInvite(result.data);

Theme

My Themes
Custom
Built-in Themes
Default
Amber Minimal
Cyberpunk
Monochrome
Nature
Overclock
Soft Lavender
Twitter