# Interface-Driven Entities

> Infer resources from plain TypeScript interfaces — no DSL required.

The same compile pass can infer resources with **no DSL at all**. Your app's entities are plain TypeScript interfaces, discovered from the typed hook calls that consume them.

```ts title="app/terminals.tsx"
interface Terminal {
  id: string;
  label: string;
  airport: Airport;          // a property typed as another entity → foreign key
  gates: number & Int;
}

// The typed hook call roots the entity graph:
const { data } = useEntityList<Terminal>();
```

The graph is rooted by a typed hook like `useEntityList<Terminal>()`, or opted in explicitly with a `@rastackResource` JSDoc tag. A property typed as another entity interface is a foreign key and pulls that interface in transitively.

## Datatypes and constraints

Datatypes come from the interface property types. Constraints come from `DbConfig` brand types (from `rastack/types`) or JSDoc tags:

| Interface | Inferred |
| --- | --- |
| `name: string` | a text column |
| `code: string & Unique & MaxLength<3>` | unique, max length 3 |
| `age: Int` | an integer column |
| `nickname?: string` / `\| null` | a nullable column |
| `airport: Airport` | a foreign key to `Airport` |

## Circular-dependency checks

Just as it validates foreign keys, the compiler detects circular dependencies over the merged FK graph:

- **An all-required cycle** is a compile **error** — there is no way to create the first row.
- **A cycle broken by an optional field** is a **warning** — the optional edge is the seam.

> **Warning:** When an interface and a `resource()` call name the same `app.model`, the DSL wins. Use interfaces for speed and the DSL when you need explicit control.

---

_Source: [https://reactapistack.com/docs/interface-entities](https://reactapistack.com/docs/interface-entities) · Full docs as one file: [llms-full.txt](https://reactapistack.com/llms-full.txt)_
