lustra v0.18.3

Advanced ORM between PostgreSQL and Crystal

Lustra

Lustra

Crystal CI GitHub release License

Lustra is a PostgreSQL-focused ORM and SQL builder for Crystal.

It provides an Active Record style model layer, expressive query collections, migrations, associations, validations, lifecycle callbacks, and direct access to PostgreSQL features such as JSONB, arrays, CTEs, cursors, full-text search, enums, UUIDs, and geometric types.

Lustra started as a fork of Clear at version 0.8 and has since evolved as an independent project.

Why Lustra?

Use Lustra when you want:

  • PostgreSQL-first behavior instead of database-agnostic lowest-common-denominator APIs
  • readable Active Record style models for application code
  • a composable SQL builder for lower-level queries
  • typed Crystal model fields with explicit nilability
  • associations, eager loading, scopes, callbacks, validations, and migrations
  • PostgreSQL-specific querying for JSONB, arrays, full-text search, CTEs, and more

Lustra is PostgreSQL-only. It is not designed for MySQL, MariaDB, or SQLite.

Installation

Add Lustra to shards.yml:

dependencies:
  lustra:
    github: crystal-garage/lustra
    version: ">= 0.18.1"

Require it in your application:

require "lustra"

Initialize the database connection:

Lustra::SQL.init("postgres://user:password@localhost/my_app")

For production applications, configure the connection pool explicitly:

Lustra::SQL.init("postgres://user:password@localhost/my_app?max_pool_size=10&initial_pool_size=1&max_idle_pool_size=2&checkout_timeout=5")

Tune max_pool_size per running process and keep the total below PostgreSQL's max_connections.

Quick Start

Define a model:

class User
  include Lustra::Model

  primary_key

  column email : String
  column first_name : String?

  timestamps
end

Create the table with a migration:

class CreateUsers
  include Lustra::Migration

  def change(dir)
    create_table :users do |t|
      t.column :email, :string, null: false
      t.column :first_name, :string
      t.timestamps
    end
  end
end

Create, query, and update records:

user = User.create!(email: "ada@example.com", first_name: "Ada")

found = User.query.find_by!(email: "ada@example.com")
found.first_name = "Ada Lovelace"
found.save!

Querying

Lustra queries are model collections. They are chainable and execute when you use terminal helpers such as each, to_a, first, count, or exists?.

users = User.query
  .where(active: true)
  .order_by(created_at: :desc)
  .limit(20)

Associations can be joined by name:

User.query.join(:posts).where { posts.published.true? }

Presence filters are available for association queries:

User.query.where.associated(:posts)
User.query.where.missing(:posts)

Related-record counts can be selected without loading associated records:

User.query.with_count(:posts)
Tag.query.with_count(:post_tags, alias_name: "tagging_count")

Eager loading avoids N+1 queries when reading associations:

Post.query.with_user.each do |post|
  puts post.user.email
end

Collections are mutable. Use dup when branching from a reusable base query:

base = User.query.where(active: true)
admins = base.dup.where(role: "admin")
editors = base.dup.where(role: "editor")

Documentation

Start with the manual if you are new to Lustra. It covers model definitions, associations, querying, migrations, PostgreSQL-specific features, and production connection-pool setup.

Development

Run the test suite:

crystal spec

Build the project:

shards build

License

Lustra is released under the MIT License.

Repository

lustra

Owner
Statistic
  • 26
  • 0
  • 1
  • 4
  • 4
  • 1 day ago
  • December 25, 2019
License

MIT License

Links
Synced at

Mon, 13 Jul 2026 20:35:22 GMT

Languages