---
title: Lucky, an experimental new web framework by thoughtbot
teaser: 'Lucky, a new Crystal web framework built to catch bugs at compile time, run
  quickly, and help you write maintainable code.

  '
tags: lucky,crystal,web
author: Paul Smith
published_on: 2017-10-27
---

Ruby and Elixir make it fun to write beautiful code, but I still see bugs in
production that *could* have been caught with a better type system.

Rails is productive, but I quickly run into speed issues that require view
caching.

Then there’s Elm. It’s beautiful. Fun. But only available on the front-end.

Where’s my Holy Grail!

## Enter Lucky, a new web framework and ORM written in Crystal

[![lucky logo](https://images.thoughtbot.com/blog-vellum-image-uploads/SlDY2xXhSNJBr014VjGB_lucky-logo-on-navy.png)](https://luckyframework.org)

The goal: catch bugs early, forget about most performance issues, and spend
more time on code instead of debugging and writing tests.

[Lucky](https://luckyframework.org) is an experimental framework built to achieve
these goals. It uses Crystal — a beautiful, fast, and type safe language with a
syntax _unabashedly_ inspired by Ruby.

Lucky leverages the type system and meta programming in Crystal to help you
create web applications quickly, while maintaining performance and catching
subtle bugs that you’d normally miss.

## Yeah yeah yeah, show me how it works already!

> In this post we’ll be talking about the ORM, but sign up at
[luckyframework.org](https://luckyframework.org) to hear about new posts, guides,
and future releases.

The other day I reviewed some code that had a bug but neither I nor the author
saw the bug. We wanted to reformat fax numbers to remove the US country code at
the beginning.

```ruby
def formatted_fax_number
  # fax_number is a method generated by ActiveRecord
  fax_number.gsub("+1", "")
end
```

We deployed to production and got our favorite error: `Undefined method gsub on
Nil`…great.

### With Lucky this bug would have been caught at compile time:

```ruby
# Define a model
class Facility < BaseModel
  table :facilities do
    field fax_number : String? # Adding ? Makes this nilable
  end

  def formatted_fax_number
    fax_number.gsub("+1", "")
  end
end
```

When we declare `field fax_number : String?` We are saying that the fax number
*might* be `nil`. So when we try to call `gsub` on it the compiler will
helpfully tell us: `Method gsub does not exist for types (String | Nil)`

Instead we can change it to this:

```ruby
def formatted_fax_number
  fax_number.try { |number| number.gsub("+1", "") }
end
```

Bug removed!

## Flexible type safe queries

So how do we query the database with Lucky?

```ruby
# First set up a model
class User < BaseModel
  table :users do
    field name : String
    field age : Int32
  end
end
```

When we define a model with `table` a `User::BaseQuery` class is added. Let’s
see how we can use that:

```ruby
class UserQuery < User::BaseQuery
end

user_query = UserQuery.new
user_query.name("Paul") # Query for users whose name is "Paul"
user_query.age.gt(30) # Query for users whose age is greater than 30
```

Because `name` is a method, you don’t have to worry about renaming the column or
having a typo in your query. It will fail to compile if you do.

You also will get a compile time error if you accidentally pass something to it
that might be `nil` or that is not the correct type for the field.

### Type specific query methods

You'll get methods specific to the type of the column:

```ruby
# Strings get things like `lower` and `ilike`
UserQuery.new.name.lower.ilike("pa%") # Will find users with a name like "Paul"
```

This makes for incredibly flexible, type safe querying. That means less time
debugging and less time writing tests since you can be confident your queries
will work.

## Scopes with plain old Crystal methods

It’s easy to extend queries by using regular Crystal methods

```ruby
class UserQuery < User::BaseQuery
  def adults
    age.gte(18)
  end

  def search(query)
    name.lower.ilike("#{query.downcase}")
  end
end

UserQuery.new.adults.search("Paul")
```

Everything is type safe, and the methods are regular Crystal methods. This makes
it easy to understand and easy to extend with modules or classes.

## What's next for Lucky

More blog posts and guides are coming next so you can start playing around with
Lucky on your own projects. Soon we'll build some internal projects in Lucky to
help flesh out corner cases and areas where Lucky can improve.

## Sign up for updates on new posts, guides, and Lucky releases

Sign up at [luckyframework.org](https://luckyframework.org). We’ll let you know
about new blog posts, new release and when we have the guides section up so you
can start playing with Lucky on your own.
