diff --git a/apps/docs/content/docs/create/handler.mdx b/apps/docs/content/docs/create/handler.mdx new file mode 100644 index 0000000..230e84d --- /dev/null +++ b/apps/docs/content/docs/create/handler.mdx @@ -0,0 +1,89 @@ +--- +title: Handler functions +--- + +### Handler + +`handler()` takes in a function that will be called when your SafeFn is executed. This function should return a `Result` or `Promise>`. +If you set an output schema, the `TData` type is constrained to the parsed output type of your schema. Otherwise, you can return any `Result` type. + +The returned types from `run()` and `createAction()()` are updated with the inferred return type of your handler function. + +```ts +const mySafeFunction = SafeFn.new() + .unparsedInput<{ firstName: string; lastName: string }>() + .output(z.object({ fullName: z.string() })) + .handler((args) => { + // Error: Type Ok<{ name: string; }> is not assignable to type Ok<{ fullName: string; }> // [!code highlight] + return ok({ + fullName: `${args.unparsedInput.firstName} ${args.unparsedInput.lastName}`, + }); + }); +``` + +the following arguments are passed to the handler function: + +- `args.parsedInput`: The result of parsing the input schema. +- `args.unparsedInput`: The unparsed input of your function. +- `args.ctx`: The `Ok` return value of the parent handler function or undefined if no parent handler function is set. + +### Safe Handler + +Instead of using `handler()`, you can also use `safeHandler()`. This offers the same functionality as NeverThrow's `safeTry()` and requires an async generation function. +This is probably the route you want to take if the rest of your codebase is built with NeverThrow, as it allows ergonomic "return if error" handling. + +Consider the following example: + +```ts +// Fake function declarations +declare function getUserTodoList(): ResultAsync; +declare function getTodoById( + id: string, +): ResultAsync< + { id: string; title: string; description: string }, + { code: "DB_ERROR" } +>; + +const getLastUserTodoTitle = SafeFn.new().handler(async () => { + const todoList = await getUserTodoList(); + if (todoList.isErr()) { + return todoList; + } + if (!todoList.value.length) { + return err({ + code: "NO_TODOS", + }); + } + const lastTodoId = todoList.value[todoList.value.length - 1]; + const todo = await getTodoById(lastTodoId); + if (todo.isErr()) { + return todo; + } + return ok(todo.value.title); +}); +``` + +This can be rewritten using `safeHandler()` as follows: + +```ts +// Fake function declarations +declare function getUserTodoList(): ResultAsync; +declare function getTodoById( + id: string, +): ResultAsync< + { id: string; title: string; description: string }, + { code: "DB_ERROR" } +>; + +const getLastUserTodoTitle = SafeFn.new().handler(async function* () { + const todoList = yield* getUserTodoList().safeUnwrap(); + if (!todoList.length) { + return err({ + code: "NO_TODOS", + }); + } + const lastTodoId = todoList[todoList.length - 1]; + const todo = yield* getTodoById(lastTodoId).safeUnwrap(); + return ok(todo.title); +}); +``` diff --git a/apps/docs/content/docs/create.mdx b/apps/docs/content/docs/create/input-output.mdx similarity index 56% rename from apps/docs/content/docs/create.mdx rename to apps/docs/content/docs/create/input-output.mdx index a151587..2322344 100644 --- a/apps/docs/content/docs/create.mdx +++ b/apps/docs/content/docs/create/input-output.mdx @@ -1,22 +1,7 @@ --- -title: Creating a SafeFn -description: Components +title: Input and Output --- -## Instantiating - -{/* TODO: chaining page */} -You can create a SafeFn by using the `.new()` method. Here you can optionally pass in a parent handler (see chaining) - -```ts -import { parentFunction } from "./some-file"; - -const myySafeFunction = SafeFn.new(); -const mySafeFunction2 = SafeFn.new(parentFunction); -``` - -## Input and output - ### Input schema You can set an input schema for your SafeFn by using `input()`. This takes in a Zod schema and will type the arguments for your handler function as well as the required input to execute the SafeFn. @@ -169,98 +154,3 @@ ActionResult< like `coerce` or passing functions to `transform()`. These will be caught and passed to your error handler. - -## Handler functions - -### Handler - -`handler()` takes in a function that will be called when your SafeFn is executed. This function should return a `Result` or `Promise>`. -If you set an output schema, the `TData` type is constrained to the parsed output type of your schema. Otherwise, you can return any `Result` type. - -The returned types from `run()` and `createAction()()` are updated with the inferred return type of your handler function. - -```ts -const mySafeFunction = SafeFn.new() - .unparsedInput<{ firstName: string; lastName: string }>() - .output(z.object({ fullName: z.string() })) - .handler((args) => { - // Error: Type Ok<{ name: string; }> is not assignable to type Ok<{ fullName: string; }> // [!code highlight] - return ok({ - fullName: `${args.unparsedInput.firstName} ${args.unparsedInput.lastName}`, - }); - }); -``` - -the following arguments are passed to the handler function: - -- `args.parsedInput`: The result of parsing the input schema. -- `args.unparsedInput`: The unparsed input of your function. -- `args.ctx`: The `Ok` return value of the parent handler function or undefined if no parent handler function is set. - -### Safe Handler - -Instead of using `handler()`, you can also use `safeHandler()`. This offers the same functionality as NeverThrow's `safeTry()` and requires an async generation function. -This is probably the route you want to take if the rest of your codebase is built with NeverThrow, as it allows ergonomic "return if error" handling. - -Consider the following example: - -```ts -// Fake function declarations -declare function getUserTodoList(): ResultAsync; -declare function getTodoById( - id: string, -): ResultAsync< - { id: string; title: string; description: string }, - { code: "DB_ERROR" } ->; - -const getLastUserTodoTitle = SafeFn.new().handler(async () => { - const todoList = await getUserTodoList(); - if (todoList.isErr()) { - return todoList; - } - if (!todoList.value.length) { - return err({ - code: "NO_TODOS", - }); - } - const lastTodoId = todoList.value[todoList.value.length - 1]; - const todo = await getTodoById(lastTodoId); - if (todo.isErr()) { - return todo; - } - return ok(todo.value.title); -}); -``` - -This can be rewritten using `safeHandler()` as follows: - -```ts -// Fake function declarations -declare function getUserTodoList(): ResultAsync; -declare function getTodoById( - id: string, -): ResultAsync< - { id: string; title: string; description: string }, - { code: "DB_ERROR" } ->; - -const getLastUserTodoTitle = SafeFn.new().handler(async function* () { - const todoList = yield* getUserTodoList().safeUnwrap(); - if (!todoList.length) { - return err({ - code: "NO_TODOS", - }); - } - const lastTodoId = todoList[todoList.length - 1]; - const todo = yield* getTodoById(lastTodoId).safeUnwrap(); - return ok(todo.title); -}); -``` - -## Cards - - - - - diff --git a/apps/docs/content/docs/create/instantiate.mdx b/apps/docs/content/docs/create/instantiate.mdx new file mode 100644 index 0000000..32abf83 --- /dev/null +++ b/apps/docs/content/docs/create/instantiate.mdx @@ -0,0 +1,15 @@ +--- +title: Instantiate +--- + +## Instantiating + +{/* TODO: chaining page */} +You can create a SafeFn by using the `.new()` method. Here you can optionally pass in a parent handler (see chaining) + +```ts +import { parentFunction } from "./some-file"; + +const myySafeFunction = SafeFn.new(); +const mySafeFunction2 = SafeFn.new(parentFunction); +``` diff --git a/apps/docs/content/docs/create/meta.json b/apps/docs/content/docs/create/meta.json new file mode 100644 index 0000000..0250cf5 --- /dev/null +++ b/apps/docs/content/docs/create/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Creating a SafeFn", + "pages": ["instantiate", "input-output", "handler"] +} diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index 986a7fa..f5db7ca 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -1,5 +1,5 @@ --- -title: Hello World +title: Hello World! description: Your first document ---