# Plugins

> First-party plugins for the columns every app repeats.

Source: https://bettersupabase.com/docs/plugins

```ts title="src/lib/supabase/index.ts"
import { defineSupabase } from "better-supabase";
import { actor } from "better-supabase/plugins/actor";
import { softDelete } from "better-supabase/plugins/soft-delete";
import { tenant } from "better-supabase/plugins/tenant";
import { timestamps } from "better-supabase/plugins/timestamps";
import { validation } from "better-supabase/plugins/validation";

import { schema } from "./generated.ts";
import { validators } from "./generated.valibot.ts";

export const betterSupabase = defineSupabase(schema)
  .use(timestamps())
  .use(softDelete())
  .use(tenant())
  .use(actor())
  .use(validation({ schemas: validators }));
```

Plugins act on tables by their generated flags. Turn the flags on in the
config and codegen marks every table that has the columns:

```ts title="better-supabase.config.ts"
plugins: {
  timestamps: true,                      // created_at, updated_at
  softDelete: { column: 'archived_at' }, // default deleted_at
  tenant: { column: 'organization_id' },
  actor: true,                           // created_by, updated_by
}
```

A table without the columns is left alone, and the types follow suit:
`restore()` and `withDeleted` only exist on soft-delete tables.

## Order [#order]

Hooks run in `use()` order, with three exceptions:

* `rules()` runs first (`enforce: 'first'`), so it checks the query as you
  wrote it.
* `softDelete()` runs next (`enforce: 'pre'`), so `timestamps()` and
  `actor()` see a soft delete as the update it becomes and stamp it.
* `validation()` runs last (`enforce: 'post'`), so it validates the row
  after `tenant()` filled `organizationId`.

| Plugin                                      | What it does                                                                            |
| ------------------------------------------- | --------------------------------------------------------------------------------------- |
| [`timestamps()`](/docs/plugins/timestamps)  | Sets `createdAt` and `updatedAt`                                                        |
| [`softDelete()`](/docs/plugins/soft-delete) | Hides deleted rows, turns `delete` into an update, adds `restore`                       |
| [`tenant()`](/docs/plugins/tenant)          | Scopes queries to the request's tenant and fills it on insert                           |
| [`actor()`](/docs/plugins/actor)            | Sets `createdBy` and `updatedBy`                                                        |
| [`validation()`](/docs/plugins/validation)  | Validates writes with any Standard Schema                                               |
| [`rules()`](/docs/plugins/rules)            | Flags unbounded reads, missing tenants, sensitive columns and admin keys in the browser |

The [lint plugin](/docs/plugins/lint) catches some of the same mistakes in
your editor.

Writing your own is covered in [Extending](/docs/extending/plugins).