# Functions, filters and tests

> Add your own functions, filters, tests and globals.

Add your own functions, filters and tests when you create the environment. They are plain JavaScript functions, and they can be async.

```ts
const kiln = createKiln({
	loader,
	functions: {
		document: (collection: string, query: { slug: string }) =>
			db.find(collection, query),
	},
	filters: {
		readingTime: (text: string) => Math.ceil(text.split(" ").length / 200),
	},
	tests: {
		external: (url: string) => !url.startsWith("/"),
	},
});
```

```kiln
{% set post = document("post", slug: route.slug) %}
<p>{{ post.body|readingTime }} min read</p>
{% if post.link is external %}<a href="{{ post.link }}" rel="noopener">Visit</a>{% endif %}
```

## How arguments arrive

- A filter gets the filtered value first, then its arguments.
- Named arguments arrive as one plain object at the end. `document("post", slug: "hello")` calls `document("post", { slug: "hello" })`.
- Arrays arrive as arrays and hashes as plain objects.
- Arrow functions arrive as plain functions. They return a promise if their body calls something async.
- A test should return a boolean, or a promise of one.

## Names

- Names start with a letter or underscore, followed by letters, digits or underscores. Test names can be two words, such as `divisible by`.
- Functions, filters and tests have separate names, so a filter and a function can share one.
- Your own function, filter or test replaces a built-in one with the same name. The exceptions are `parent`, `block`, `default`, `raw`, `escape` and `defined`, which Kiln reserves.

## Async functions

Any function, filter or test can return a promise. Kiln waits for it where it is called and carries on. See [Async data](https://kilnlang.com/docs/language/async-data/).

Throwing an error stops the render. The render error's `cause` is your error, so you can throw your own error types to signal a 404 or a redirect and catch them in your server.

## Request data

Some functions need data about the current request, such as the visitor's language or whether they are previewing drafts. Wrap them in `withRender`, and pass the data as `state` when you render.

```ts
import { createKiln, withRender } from "@lucidkiln/core";

type State = { locale: string; preview: boolean };

const kiln = createKiln<State>({
	loader,
	functions: {
		document: withRender((render, collection: string, query: { slug: string }) =>
			db.find(collection, {
				...query,
				locale: render.state.locale,
				drafts: render.state.preview,
			}),
		),
	},
});

await kiln.render("pages/page", context, {
	state: { locale: "en", preview: false },
});
```

The first argument describes the render:

| Property | Description |
| --- | --- |
| `state` | The `state` passed to `render` |
| `locale` | The render's locale |
| `timeZone` | The render's time zone |
| `template` | The name of the template making the call |
| `signal` | The render's abort signal, if there is one |

With a `State` type, TypeScript makes `state` a required render option, so you can't forget it.

## Globals

Globals are variables every template can read, including included templates, components and macros. Use them for data that is the same on every page, such as the site's name.

```ts
const kiln = createKiln({
	loader,
	globals: { site: { name: "Acme", url: "https://acme.example" } },
});
```

Templates can't assign to globals. Values are not awaited, so pass data that is ready, or a function.
