# timestamps

> createdAt and updatedAt without remembering them.

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

```ts
import { timestamps } from "better-supabase/plugins/timestamps";

defineSupabase(schema).use(timestamps());
```

* Inserts get `createdAt` and `updatedAt`.
* Updates (including soft deletes) get `updatedAt`.
* Upserts that may update an existing row only get `updatedAt`, so the
  original `createdAt` survives. With `ignoreDuplicates: true` both are set.
* Values you pass yourself are refused with `invalid_request`, so a client
  cannot backdate a row. Imports and backfills pass `{ override: true }` to
  keep them:

```ts
await db.customers.create(
  { ...row, createdAt: imported.createdAt },
  { override: true },
);
```

Generated JSON Schema and OpenAPI documents mark these columns `readOnly`.
In a [`defineApi`](/docs/specs) document they are also left out of the
required fields of request bodies, and a column without a comment gets a
description: "Set by the server when the row is created." for `createdAt`
and "Set by the server on every write." for `updatedAt`.

The time comes from `defineSupabase(schema, { now })`, a function that returns
a `Temporal.Instant`, which makes tests deterministic. See
[Temporal](/docs/concepts/temporal).

> **Writes outside the app**
>
> SQL functions, dashboards and other services bypass the plugin. Add the
> `updated-at` trigger from the SQL modules (`better-supabase sql add
>   updated-at`) to cover them. The plugin and the trigger work together.