# Compile & Relations

> How rastack compile reads the type system to infer foreign keys and emit the manifest.

The compile step is where the type system becomes a schema. `rastack compile` builds a real `ts.Program` and `TypeChecker`, walks each `resource(...)` call, and reads each field's type.

## Foreign keys are inferred, not declared

A field whose type is a reference to another resource is a foreign key. The compiler recovers the target from the `Ref<App, Model>` string-literal type arguments — you write the relation once, as a type, and never again.

```ts title="resources/airports.ts"
export const Terminal = resource("airports", "terminal", {
  airport: s.ref(() => Airport),   // Ref<"airports", "airport">
  label: s.string({ maxLength: 20 }),
});

// The compiler emits, in the manifest:
//   relations: [{ field: "airport", target: "airports.airport", kind: "fk" }]
```

## The compiler modules

| Module | Role |
| --- | --- |
| `tools/src/define/` | The DSL — `resource()` and `s.*`. |
| `compile/program.ts` | Builds the `ts.Program` + `TypeChecker`. |
| `compile/analyze.ts` | Walks each `resource` call and reads field types. |
| `compile/manifest.ts` | Emits `schema.rastack.json`. |
| `compile/openapi.ts` | Emits `openapi.json` with the FK and sync extensions. |

## The URL convention

The compiler emits paths in exactly the shape the codegen expects, so the hook generator and runtime never change:

```text title="path shape"
/api/{appName}/v1/{modelName}/          ← list endpoint
/api/{appName}/v1/{modelName}/{id}/     ← detail endpoint
```

> **Note:** The codegen derives `appName` from `path.split('/')[2]` and `modelName` from `path.split('/')[4]`. Because the compiler emits this exact shape, the existing hooks codegen is unchanged.

---

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