Current section
Files
Jump to
Current section
Files
guides/getting_started.md
# Getting Started
This guide walks through adding Squirrelix to a Mix project, writing your first
query, and generating typed Elixir modules.
## Add the dependency
Squirrelix is a **codegen Mix tool** — keep it out of production. Postgrex stays a
**runtime** dependency because generated modules call it:
```elixir
def deps do
[
{:squirr_elix, "~> 0.5.0", only: [:dev, :test], runtime: false},
{:postgrex, "~> 0.22"}
]
end
```
Include `:test` so `mix squirrelix.check` works in CI (`MIX_ENV=test`). Use
`runtime: false` so Squirrelix is not started with your application.
Then run:
```sh
mix deps.get
```
## Project layout
Squirrelix discovers queries under `lib/`, `test/`, and `dev/` in directories
named `sql/`:
```txt
my_app/
├── lib/
│ └── my_app/
│ └── accounts/
│ ├── sql/
│ │ └── find_user.sql
│ └── sql.ex # generated
├── mix.exs
└── squirr_elix.exs # optional metadata file
```
Each `sql/` directory maps to one generated module. The module name is derived from
your app name and the path under `lib/`:
| SQL directory | Generated module |
| --- | --- |
| `lib/my_app/sql/` | `MyApp.SQL` |
| `lib/my_app/accounts/sql/` | `MyApp.Accounts.SQL` |
| `test/support/sql/` | `Support.SQL` (under test) |
The generated file is always named `sql.ex` and lives in the parent of the `sql/`
directory.
## Write a query
Create one SQL statement per file. Use leading comments for documentation:
```sql
-- lib/my_app/accounts/sql/find_user.sql
-- Find a user by primary key.
select
id,
name,
email
from
users
where
id = $1
```
The filename (`find_user.sql`) becomes the function name (`find_user/2` — conn
plus one parameter).
## Generate code
### With Postgres inference (recommended)
Point Squirrelix at a database that has your schema applied (migrations run):
```sh
mix squirrelix.gen --infer --database my_app_dev
```
Squirrelix connects to Postgres, prepares each query, and reads parameter and
column types from Postgrex metadata.
Set connection details via `DATABASE_URL`, `PG*` environment variables, or flags:
```sh
export DATABASE_URL=postgres://postgres@localhost/my_app_dev
mix squirrelix.gen --infer
# or:
export PGHOST=localhost
export PGDATABASE=my_app_dev
export PGUSER=postgres
mix squirrelix.gen --infer
```
Or pass a URL:
```sh
mix squirrelix.gen --infer --url postgres://postgres@localhost/my_app_dev
```
### With a metadata file
If you cannot connect to Postgres during generation, provide types manually in
`squirr_elix.exs`:
```elixir
%{
"lib/my_app/accounts/sql/find_user.sql" => [
params: [:integer],
returns: [
%{name: "id", type: :integer, nullable?: false},
%{name: "name", type: :string, nullable?: false},
%{name: "email", type: :string, nullable?: true}
]
]
}
```
```sh
mix squirrelix.gen
```
See [Configuration](configuration.md) for the full metadata format.
## Watch while editing
To regenerate whenever a discovered `.sql` file changes:
```sh
mix squirrelix.gen --watch
mix squirrelix.gen --infer --watch
```
Watch uses the same metadata / `--infer` connection options as a one-shot generate.
Press Ctrl-C to stop. See [Configuration](configuration.md#watch-mode) for details.
## Use the generated module
After generation, call functions from the generated module with a Postgrex connection:
```elixir
alias MyApp.Accounts.SQL
defmodule MyApp.Accounts do
def find_user(conn, id) do
SQL.find_user(conn, id)
end
end
```
Row queries return a list of maps:
```elixir
[%{id: 1, name: "Ada", email: "ada@example.com"}] = SQL.find_user(conn, 1)
```
Command queries (no returned rows) return `:ok`:
```elixir
:ok = SQL.delete_user(conn, 1)
```
Soft companions return ok/error tuples instead of raising. Soft commands include the
affected-row count:
```elixir
{:ok, [%{id: 1, name: "Ada"}]} = SQL.find_user_ok(conn, 1)
{:ok, 1} = SQL.delete_user_ok(conn, 1)
{:error, %Postgrex.Error{}} = SQL.find_user_ok(conn, -1)
```
## Keep generated code in sync
Run the check task in CI to catch stale generated files:
```sh
mix squirrelix.check --infer --database my_app_dev
```
If SQL changed but `sql.ex` was not regenerated, the check fails with an
`OutdatedFile` error. Fix it by running `mix squirrelix.gen` again and committing
the updated `sql.ex`.
## Next steps
- [Writing Queries](writing_queries.md) — naming, comments, nullable parameters
- [Types](types.md) — Postgres to Elixir type mapping
- [Configuration](configuration.md) — metadata, env vars, programmatic API
- [Phoenix + CI Cookbook](phoenix.md) — migrate-then-gen, Mix aliases, Ecto coexistence