Tren Logo

Build

Tren

Use SQL files as first-class Crystal methods.

Tren reads SQL files at compile time and generates native Crystal methods from them.
You keep SQL in .sql files, then call it like regular Crystal code.

Why Tren?

30-Second Example

Create queries/users.sql:

-- name: get_users(name : String, age : Int32)
SELECT * FROM users WHERE name = '{{ name }}' AND age = {{ age }}

Load and call it:

require "tren"

Tren.load("./queries/*.sql")

sql = get_users("john", 42)
# => "SELECT * FROM users WHERE name = 'john' AND age = 42"

That method (get_users) is generated by Tren during compilation.

Installation

Add this to your shard.yml:

dependencies:
  tren:
    github: sdogruyol/tren

Then install dependencies:

shards install

SQL File Format

Each query must start with metadata:

-- name: method_name(arg : Type, ...)

After that line, write the SQL body:

-- name: find_user(id : Int32)
SELECT * FROM users WHERE id = {{ id }}

Parameter Rules

-- name: by_name(name : String)
SELECT * FROM users WHERE name = '{{ name }}'

-- name: with_clause(clause : String)
SELECT * FROM users {{! clause }}

Overloading

Multiple SQL entries can share the same method name with different signatures:

-- name: get_users(name : String, surname : String)
SELECT * FROM users WHERE name = '{{ name }}' AND surname = '{{ surname }}'

-- name: get_users(name : String, age : Int32)
SELECT * FROM users WHERE name = '{{ name }}' AND age = {{ age }}

Crystal resolves overloads and reports errors if arguments do not match.

Composing Queries

You can reuse generated SQL methods to build larger queries:

-- name: filter_user(name : String, surname : String)
WHERE name = '{{ name }}' AND surname = '{{ surname }}'

-- name: get_users(name : String, surname : String)
SELECT * FROM users {{! filter_user(name, surname) }}

Escaping Behavior

String parameters are escaped by default.
Non-string values are passed through as-is.

You can customize the escape behavior:

Tren.escape_character = "\\"
# => escapes both quotes and backslashes with a backslash prefix (default)

Tren.escape_character = "\\'"
# => PostgreSQL-style single-quote escaping ("I'm" => "I''m")

Security Notes

Error Messages

Tren now fails with clearer parse errors (including file and line) for invalid metadata or malformed placeholders.

Expected metadata format:

-- name: method_name(args)

Development

Run tests:

crystal spec

Run format check:

crystal tool format --check src spec

Contributing

  1. Fork it (github.com/sdogruyol/tren/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am "Add some feature")
  4. Push to the branch (git push origin my-new-feature)
  5. Open a Pull Request

Contributors

Built on a TREN from Ankara to Istanbul.