Skip to content

Commit

Permalink
move to separate pages
Browse files Browse the repository at this point in the history
  • Loading branch information
janglad committed Sep 6, 2024
1 parent 80ddb21 commit b00e8ae
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 112 deletions.
89 changes: 89 additions & 0 deletions apps/docs/content/docs/create/handler.mdx
Original file line number Diff line number Diff line change
@@ -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<TData,TError>` or `Promise<Result<TData,TError>>`.
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<string[], { code: "DB_ERROR" }>;
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<string[], { code: "DB_ERROR" }>;
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);
});
```
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -169,98 +154,3 @@ ActionResult<
like `coerce` or passing functions to `transform()`. These will be caught and
passed to your error handler.
</Callout>

## Handler functions

### Handler

`handler()` takes in a function that will be called when your SafeFn is executed. This function should return a `Result<TData,TError>` or `Promise<Result<TData,TError>>`.
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<string[], { code: "DB_ERROR" }>;
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<string[], { code: "DB_ERROR" }>;
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

<Cards>
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
<Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
</Cards>
15 changes: 15 additions & 0 deletions apps/docs/content/docs/create/instantiate.mdx
Original file line number Diff line number Diff line change
@@ -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);
```
4 changes: 4 additions & 0 deletions apps/docs/content/docs/create/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Creating a SafeFn",
"pages": ["instantiate", "input-output", "handler"]
}
2 changes: 1 addition & 1 deletion apps/docs/content/docs/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Hello World
title: Hello World!
description: Your first document
---

Expand Down

0 comments on commit b00e8ae

Please sign in to comment.