# Function options builder

> Build a function's options in the browser and see them as attributes and in code, the JSON the SDK sends to Inngest when the app syncs, and which attribute sets each option.

Web page: https://damikun.github.io/inngest-dotnet/explainers/function-options/

Everything Inngest controls about a function, from its triggers to flow control, goes in `FunctionOptions`, which
the function class can also carry as attributes. When Inngest syncs the app, the SDK turns those options into JSON.
Pick a preset or change the options, and the output shows the C# in both forms and that JSON. Like the other pages,
it shows attributes first unless you picked **Code first** in one of their tabs.

An interactive version is on [the web page](https://damikun.github.io/inngest-dotnet/explainers/function-options/).

## How the output is made

The builder writes the JSON the same way the SDK's `RegistrationWriter` does: the same properties in the same
order, with durations as Inngest time strings (`90` minutes becomes `"1h30m"`) and options that are not set left
out, so Inngest applies its defaults. A few details worth knowing:

- A function's id on the wire is `<app id>-<function id>`.
- `retries.attempts` holds the number of retries, not attempts. Leave `Retries` empty to get Inngest's default of
  4 retries.
- `Match` on a cancellation becomes the expression `event.<path> == async.<path>`.
- A cron trigger with a time zone is sent with a `TZ=` prefix.
- A class that implements `IFailureHandler` registers a second function, `<id>-failure`, triggered by
  `inngest/function.failed`.

The checks are the ones `FunctionOptionsValidator` makes when the app starts, and the SDK, like the builder, reports
all the problems at once.

## Attributes

As attributes, the options sit on the function class, and `AddFunction<TFunction>()` reads them into
`FunctionOptions` when the app starts, so both forms sync to the same JSON and go through the same checks. Each
attribute sets one member:

| Attribute | Sets |
| - | - |
| `[InngestFunction("id", Name, Retries, Idempotency)]` | `Id`, `Name`, `Retries`, `Idempotency` |
| `[EventTrigger("name", If)]`, `[EventTrigger<T>(If)]` | an `EventTrigger` in `Triggers` |
| `[CronTrigger("schedule", Timezone, Jitter)]` | a `CronTrigger` in `Triggers` |
| `[Concurrency(limit, Key, Scope)]` | a `ConcurrencyLimit` in `Concurrency` |
| `[Throttle(limit, "period", Burst, Key)]` | `Throttle` |
| `[RateLimit(limit, "period", Key)]` | `RateLimit` |
| `[Debounce("period", Key, Timeout)]` | `Debounce` |
| `[BatchEvents(maxSize, "timeout", Key, If)]` | `BatchEvents` |
| `[Priority("run")]` | `Priority` |
| `[Singleton(mode, Key)]` | `Singleton` |
| `[CancelOn("name", If, Match, Timeout)]`, `[CancelOn<T>(If, Match, Timeout)]` | a `CancelOn` in `CancelOn` |
| `[Timeouts(Start, Finish)]` | `Timeouts` |

The trigger, concurrency and cancellation attributes can be repeated. Durations are time strings such as `"30s"`,
`"5m"` or `"1h30m"`. `Middleware` and `Checkpointing` have no attributes; set them with
`AddFunction<TFunction>(options => options with { ... })`, which starts from the attributes. Options passed as
`AddFunction<TFunction>(new FunctionOptions { ... })` replace the attributes. An attribute of your own derives from
`FunctionOptionsAttribute`, as all of these but `[InngestFunction]` do;
[Functions and triggers](https://damikun.github.io/inngest-dotnet/guides/functions-and-triggers.md#attributes-of-your-own) shows how.

The site's build checks this page against the SDK: the snippet check compiles both forms of every preset,
registers each in a real app and compares the JSON the SDK sends with the JSON shown here.

[Flow control](https://damikun.github.io/inngest-dotnet/guides/flow-control.md) explains each option, and
[Functions and triggers](https://damikun.github.io/inngest-dotnet/guides/functions-and-triggers.md) covers ids, names and triggers.
