Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/tests/src/server-fn-error.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { ServerFunctionErrorHandler } from "@solidjs/start/server";

/**
* Wired up through `serverFunctions.onError`. Unlike `seroval-plugins.ts` this
* module is bundled into the server only, so it may reach for server-only code.
* Wired up through `serverFunctions.onError`. Unlike `src/seroval-plugins.ts`,
* this module is bundled into the server only, so it may reach for server-only
* code.
*
* Returning `undefined` sends whatever was thrown, which is what leaves the
* other server-function error tests in this app seeing their own errors.
Expand Down
45 changes: 34 additions & 11 deletions packages/start/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,45 @@ export interface SolidStartOptions {
env?: EnvPluginOptions;

/**
* Options controlling which files are processed as server functions
* (inclusion / exclusion filters for the `"use server"` transform).
* Options for server functions: which files the `"use server"` transform
* processes (inclusion / exclusion filters), and what happens when a server
* function throws.
*/
serverFunctions?: Pick<ServerFunctionsOptions, "filter"> & {
/**
* Path to a module whose default export is called with whatever a server
* function threw, before it is serialized into the response. Return a
* value to send it in place of what was thrown, or `undefined` to send the
* original.
* Path to a module whose default export handles whatever a server function
* throws, before it reaches the client. Use it to report failures to a
* monitoring service, or to replace an error carrying internal detail with
* one that is safe to send.
*
* Naming the module here rather than registering a handler at runtime
* keeps the app in sole control of it: no dependency can reach into the
* running server and take over reporting.
* Only server function calls made over the network run through it. A
* server function called during rendering runs in process and throws
* straight to its caller, and errors from API routes never reach it
* either.
*
* The module is bundled into the server only, so it may import server-only
* code such as a monitoring SDK.
* The export is called synchronously with the thrown value, and what it
* returns decides what the client sees:
*
* - `undefined` (or `null`) sends what was thrown, unchanged.
* - A `Response` is passed through unchanged, which keeps a thrown
* `redirect()` working.
* - Any other value is sent in place of what was thrown, and the client
* call rejects with it.
*
* Control flow reaches the export the same way errors do, so a handler
* that replaces everything it sees turns redirects into errors.
*
* Whatever is sent gets serialized to the client along with its own
* properties, so an error meant to be safe to expose must not carry
* internal detail.
*
* The return value is not awaited, so make the export a plain function
* rather than an `async` one, and report failures with calls that do not
* need awaiting.
*
* Type the export as `ServerFunctionErrorHandler` from
* `@solidjs/start/server`. The module is bundled into the server only, so
* it may import server-only code such as a monitoring SDK.
*
* @example "src/server-fn-error.ts"
*/
Expand Down
32 changes: 31 additions & 1 deletion packages/start/src/fns/error-handler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
import onServerFunctionError from "solid-start:server-fn-error-handler";

/**
* Handles whatever a server function threw, before it reaches the client. Use
* it to type the default export of the module named by the
* `serverFunctions.onError` option in `vite.config.ts`.
*
* Called synchronously, and the value it returns is not awaited, so it cannot
* be an `async` function.
*
* @param thrown The value the server function threw. This is a `Response` when
* the server function threw control flow such as a `redirect()`.
* @returns `undefined` (or `null`) to send `thrown` unchanged, a `Response` to
* pass control flow through untouched, or any other value to send in place of
* `thrown`.
*
* @example
* ```ts
* // src/server-fn-error.ts
* import type { ServerFunctionErrorHandler } from "@solidjs/start/server";
* import { captureException } from "./your-monitoring-client";
*
* const onServerFunctionError: ServerFunctionErrorHandler = thrown => {
* // redirect() throws a Response, so returning it preserves that control flow.
* if (thrown instanceof Response) return thrown;
*
* captureException(thrown); // or console.error, or any reporter
* return new Error("Something went wrong");
* };
*
* export default onServerFunctionError;
* ```
*/
export type ServerFunctionErrorHandler = (thrown: unknown) => unknown;

/** @internal */
Comment thread
birkskyum marked this conversation as resolved.
export function applyServerFunctionErrorHandler(thrown: unknown): unknown {
return onServerFunctionError?.(thrown) ?? thrown;
}
Loading