[]previous and next page

Redirects

A server function redirects by throwing redirect(to, { status }).

For agentsUpdated

A server function redirects by throwing redirect(to, { status }). The wire carries it as data, not as an HTTP redirect: the answer is 200 with { redirect: { to, status } }, because a fetch that followed a 3xx would hand the stub an unrelated page.

  • On the server, the target is checked first: a path on this site (/notes/1, not //host), or an absolute URL of this origin or one listed in trustedOrigins. Anything else is a bug or an open-redirect attempt; the handler reports REDIRECT_TARGET_BLOCKED to onError and answers 500.
  • In the browser, the stub resolves to against the current page and requires the same origin, or it throws a ServerFnProtocolError. It then throws the router's redirect() signal. A loader or beforeLoad that awaits the call lets the router follow it. A call that nobody catches, such as one in an event handler, rejects with the signal, and the unhandledrejection listener that hydrateDusk installs navigates to it.
src/server/forms.tsts
import { redirect, serverFn } from "@shadow-js/dusk/server";
import { newNote, noteId } from "../schemas";
import type { Note } from "../types";
import { requireUser, userKey } from "./auth";
import { notes } from "./db.server";

export const openNote = serverFn({ method: "GET", input: noteId }, async ({ id }): Promise<never> => {
  throw redirect(`/notes/${id}`, { status: 303 });
});

export const elsewhere = serverFn({}, async (): Promise<never> => {
  throw redirect("https://evil.example/phish");
});

const pinned = new Set<string>();

export const pinNote = serverFn({ form: true, input: noteId }, async ({ id }): Promise<void> => {
  pinned.add(id);
});

export const submitNote = serverFn(
  { form: true, input: newNote, middleware: [requireUser] },
  async ({ title }, event): Promise<never> => {
    const note: Note = { id: String(notes.size + 1), title, owner: event.get(userKey).id };
    notes.set(note.id, note);
    throw redirect(`/notes/${note.id}`);
  },
);