rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
89 lines (88 loc) • 3.4 kB
JavaScript
import { requestInfo } from "./requestInfo/worker";
function createServerFunction(fns, mainFn, options) {
const wrapped = async (...args) => {
const { request, ctx } = requestInfo;
// Execute interruptors
for (const fn of fns) {
const result = await fn({ request, ctx, args });
if (result instanceof Response) {
// We can't easily return a Response from a server action function
// because the return type is expected to be TResult.
// However, if the interruptor returns a Response, it usually means "stop and return this HTTP response".
// In the RSC context, throwing a Response is a common pattern to short-circuit.
throw result;
}
}
return mainFn(...args);
};
wrapped.method = options?.method ?? "POST"; // Default to POST if not specified, though user said serverQuery defaults to GET?
// User said: "export const getProject = serverQuery(...) // Defaults to GET"
// So serverQuery defaults to GET, serverAction defaults to POST?
return wrapped;
}
/**
* Wrap a function to be used as a server query.
*
* - **Method**: Defaults to `GET`. can be changed via `options`.
* - **Behavior**: When called from the client, it returns data-only and does **not** rehydrate or re-render the React page.
* - **Location**: Must be defined in a file with `"use server"`. We recommend `queries.ts` colocated with components.
* - **Middleware**: You can pass an array of functions as the first argument to act as interruptors (e.g. for auth).
*
* @example
* ```ts
* // getters.ts
* "use server"
*
* export const getUser = serverQuery(async (id: string) => {
* return db.user.findUnique({ where: { id } })
* })
* ```
*/
export function serverQuery(fnsOrFn, options) {
let fns = [];
let mainFn;
if (Array.isArray(fnsOrFn)) {
fns = fnsOrFn.slice(0, -1);
mainFn = fnsOrFn[fnsOrFn.length - 1];
}
else {
mainFn = fnsOrFn;
}
const method = options?.method ?? "GET"; // Default to GET for query
const wrapped = createServerFunction(fns, mainFn, { ...options, method });
wrapped.method = method;
return wrapped;
}
/**
* Wrap a function to be used as a server action.
*
* - **Method**: Defaults to `POST`. can be changed via `options`.
* - **Behavior**: When called from the client, it **will** rehydrate and re-render the React page with the new server state.
* - **Location**: Must be defined in a file with `"use server"`. We recommend `actions.ts` colocated with components.
* - **Middleware**: You can pass an array of functions as the first argument to act as interruptors (e.g. for auth).
*
* @example
* ```ts
* // actions.ts
* "use server"
*
* export const updateUser = serverAction(async (id: string, data: any) => {
* return db.user.update({ where: { id }, data })
* })
* ```
*/
export function serverAction(fnsOrFn, options) {
let fns = [];
let mainFn;
if (Array.isArray(fnsOrFn)) {
fns = fnsOrFn.slice(0, -1);
mainFn = fnsOrFn[fnsOrFn.length - 1];
}
else {
mainFn = fnsOrFn;
}
const method = options?.method ?? "POST"; // Default to POST for action
const wrapped = createServerFunction(fns, mainFn, { ...options, method });
wrapped.method = method;
return wrapped;
}