Current section
Files
Jump to
Current section
Files
guides/getting-started.md
# Getting started
Typelizer makes your Elixir code the single source of the shapes that your
TypeScript frontend sees. You define a serializer once. It serializes your data
at runtime and it is the source of the TypeScript interface. Typelizer also
generates typed route helpers from your Phoenix router and typed page props for
Inertia.js pages.
This guide takes a Phoenix app from zero to generated types.
## 1. Install
Add `typelizer` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:typelizer, "~> 0.2"}
]
end
```
Then run `mix deps.get`.
`ecto`, `ecto_sql`, `phoenix`, `plug` and `inertia` are optional dependencies.
Typelizer uses them when your app has them.
## 2. Write a serializer
```elixir
defmodule MyAppWeb.TaskSerializer do
use Typelizer.Serializer, schema: MyApp.Tasks.Task
attributes [:id, :title, :status, :due_on, :inserted_at]
attribute :overdue, type: :boolean, value: &MyApp.Tasks.overdue?/1
has_one :assignee, serializer: MyAppWeb.UserSerializer, nullable: true
has_many :comments, serializer: MyAppWeb.CommentSerializer
end
```
Use it in a controller:
```elixir
json(conn, %{data: MyAppWeb.TaskSerializer.serialize(task)})
```
The result is a map with camelCase string keys and JSON-ready values:
```elixir
%{
"id" => "0b6c5e1a-…",
"title" => "Ship v0.1",
"status" => "todo",
"dueOn" => "2026-10-01",
"insertedAt" => "2026-09-30T10:00:00Z",
"overdue" => false,
"assignee" => %{"id" => "…", "name" => "Ana"},
"comments" => []
}
```
See [Serializers](serializers.md) for the full DSL.
## 3. Configure the output
All keys are optional. This is a typical setup for a Phoenix + Inertia + React
app:
```elixir
# config/config.exs
config :typelizer,
output: [
serializers: "assets/js/generated/serializers",
routes: "assets/js/generated/routes",
pages: "assets/js/generated/pages"
],
repo: MyApp.Repo
# config/dev.exs and config/test.exs
config :typelizer, validate_inertia_props: true
```
See [Configuration](configuration.md) for every key.
## 4. Generate the TypeScript
```sh
mix typelizer.gen
```
```text
typelizer: wrote 14 files, 0 unchanged, removed 0 stale files.
```
The serializer above becomes `assets/js/generated/serializers/Task.ts`:
```ts
// Generated by typelizer — do not edit.
// Source: MyAppWeb.TaskSerializer
import type { Comment } from "./Comment";
import type { User } from "./User";
export interface Task {
id: string;
title: string;
status: "todo" | "doing" | "done";
dueOn: string | null;
insertedAt: string;
overdue: boolean;
assignee: User | null;
comments: Comment[];
}
```
Commit the generated files. They are the contract between the backend and the
frontend.
## 5. Use the types in React
```tsx
import type { Task } from "@/generated/serializers";
import { routes } from "@/generated/routes";
export function TaskLink({ task }: { task: Task }) {
return <a href={routes.task.show(task.id).url}>{task.title}</a>;
}
```
With Inertia, declare the props of each page and type the page component with
the generated props interface. See [Inertia page props](inertia.md).
```tsx
import type { TasksIndexProps } from "@/generated/pages";
export default function Index({ tasks, filters }: TasksIndexProps) {
// …
}
```
## 6. Fail on drift
```sh
mix typelizer.check
```
The task generates everything in memory and compares it with
the files on disk. When a serializer, a schema, a route or a page declaration
changed and the committed TypeScript did not follow, it prints the difference
and exits with status 1:
```text
typelizer: the generated TypeScript is out of date (2 files):
changed assets/js/generated/serializers/Task.ts
- dueOn: string | null;
+ dueOn: string;
missing assets/js/generated/routes/taskComment.ts
Run `mix typelizer.gen` and commit the result.
```
Run it in a pre-commit hook, in a pre-push hook, in CI and before a deploy:
```sh
# .githooks/pre-commit
#!/bin/sh
mix typelizer.check
```
When `repo:` is configured, both tasks read column nullability from the
database, so the database must be up.
## Next steps
- [Serializers](serializers.md): the DSL, type inference from Ecto, nullability.
- [Route helpers](routes.md): typed URLs from the Phoenix router.
- [Inertia page props](inertia.md): typed page props and runtime validation.
- [Configuration](configuration.md): every `config :typelizer` key.