The routes of this page
The routes of this page in the Routing and data guide.
The rest of the page uses one small project. The data comes from server functions, as in
Dusk; src/schema.ts is a Standard Schema validator.
src/schema.tsts
import type { StandardSchemaV1 } from "@shadow-js/dusk/server";
/** Wraps a parse function that throws on bad input as a Standard Schema. */
export function schema<T>(parse: (value: unknown) => T): StandardSchemaV1<T, T> {
return {
"~standard": {
version: 1,
vendor: "app",
validate(value) {
try {
return { value: parse(value) };
} catch (error) {
return { issues: [{ message: error instanceof Error ? error.message : "invalid" }] };
}
},
},
};
}
src/server/posts.tsts
import { getRequestEvent, HttpError, serverFn } from "@shadow-js/dusk/server";
import { schema } from "../schema";
export interface Post {
id: string;
title: string;
}
const posts = new Map<string, Post>([
["1", { id: "1", title: "First post" }],
["2", { id: "2", title: "Second post" }],
]);
const postId = schema((value) => {
if (typeof value === "object" && value !== null && "id" in value && typeof value.id === "string") {
return { id: value.id };
}
throw new Error("id must be a string");
});
export const getPost = serverFn({ method: "GET", input: postId }, async ({ id }): Promise<Post> => {
const post = posts.get(id);
if (post === undefined) throw new HttpError(404, "No such post");
return post;
});
export const listPosts = serverFn({ method: "GET" }, async (): Promise<Post[]> => [
...posts.values(),
]);
/** Who is signed in: the `viewer` cookie of the request, or null. */
export const whoami = serverFn({ method: "GET" }, async (): Promise<string | null> => {
return getRequestEvent().cookies.get("viewer") ?? null;
});
src/entry-server.tsts
import type { HandlerOptions } from "@shadow-js/dusk/server";
const options: Omit<HandlerOptions, "manifest"> = {
security: { allowedHosts: ["localhost"] },
};
export default options;