# Type Safety

TableCraft is built on Drizzle ORM, which means you get excellent type safety out of the box. However, raw SQL strings can be a weak point. We provide a set of helpers to maintain type safety even when doing complex SQL operations.

{% hint style="warning" %}
**The Rule of Thumb:**

* **Good**: Using `${schema.table.column}` inside `sql` tags.
* **Bad**: Writing column names as plain strings.
  {% endhint %}

## Available Helpers

Import these from `@tablecraft/engine`.

{% tabs %}
{% tab title="caseWhen" %}
Generates a `CASE WHEN` statement safely.

```typescript
import { caseWhen } from '@tablecraft/engine';
import { sql } from 'drizzle-orm';

// ❌ Risky: Raw SQL string
.computed('status', sql`CASE WHEN ${s.users.role} = 'admin' THEN 1 ELSE 0 END`)

// ✅ Type-Safe: Compiler checks column and types
.computed('status', caseWhen(s.users.role, {
  'admin': 1,
  'user': 0
}, 0)) // Fallback to 0
```

{% endtab %}

{% tab title="column" %}
Getting a column reference dynamically? Use this helper to ensure the column name exists on the table.

```typescript
import { column } from '@tablecraft/engine';

const col = column(s.users, 'email'); // ✅ Works
const bad = column(s.users, 'emial'); // ❌ TypeScript Error: Property 'emial' does not exist
```

{% endtab %}

{% tab title="coalesce" %}
Type-safe `COALESCE` (returns the first non-null value).

```typescript
import { coalesce } from '@tablecraft/engine';

// Returns nickname, or name if nickname is null, or 'Anonymous'
.computed('displayName', coalesce(s.users.nickname, s.users.name, 'Anonymous'))
```

{% endtab %}

{% tab title="concat" %}
Type-safe string concatenation.

```typescript
import { concat } from '@tablecraft/engine';

.computed('fullName', concat(s.users.firstName, ' ', s.users.lastName))
```

{% endtab %}

{% tab title="dateTrunc" %}
Truncates a timestamp to a specific precision (year, month, day, etc.). Useful for grouping.

```typescript
import { dateTrunc } from '@tablecraft/engine';

// Group sales by month
.groupBy(dateTrunc('month', s.orders.createdAt))
```

{% endtab %}

{% tab title="ago" %}
Shorthand for `NOW() - INTERVAL`. Useful for filters.

```typescript
import { ago } from '@tablecraft/engine';

// Users created in the last 7 days
.where({ field: 'createdAt', op: 'gt', value: ago(7, 'days') })
```

{% endtab %}
{% endtabs %}

## Best Practices

1. **Prefer Helpers**: Always check if a helper exists before writing raw SQL.
2. **Use Drizzle Columns**: Inside `sql` tags, always interpolate Drizzle column objects (`${s.users.name}`) instead of writing string names (`"name"`).
3. **Validate Configs**: If you are dynamically generating configs, use `validateConfig` to catch errors early.

## Frontend Type Safety

### Generated Types

The `@tablecraft/codegen` package generates fully-typed interfaces from your API metadata:

```typescript
// Generated from API metadata
export interface ProductsRow extends Record<string, unknown> {
  id: number;
  name: string;
  price: number;
  metadata: Record<string, unknown>;
}

export type ProductsColumn = 'id' | 'name' | 'price' | 'metadata';
```

### Type-Safe Hidden Columns

Use the `hiddenColumns` helper with generated `*Column` types for compile-time safety:

```typescript
import { DataTable, hiddenColumns } from '@tablecraft/table';
import type { ProductsRow, ProductsColumn } from './generated';

<DataTable<ProductsRow>
  adapter={adapter}
  hiddenColumns={hiddenColumns<ProductsColumn>(['id', 'metadata'])}
/>
```

{% hint style="info" %}
The `hiddenColumns` helper catches typos and invalid column names at compile time, not runtime.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jacksonkasi.gitbook.io/tablecraft/guides/type-safety.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
