After over two years (dang that was quick 😳) I have decided to give Crystal another go. During those two years some nice libraries appeared and language seems quite promising - I hope it will gain some traction in the future. I will try to make a series of short posts regarding Crystal within this month as I think it lacks documentation/references and attention it deserves - and as I’m taking some longer time off from IT in general very next month I have to hurry :P.

Note 1: I’m assuming you’re coming from Ruby/Rails world, thus references to other frameworks.

Note 2: Those posts are backed by my first crystal web app that is at this point soft-released, hopefully I will be able to show it to the public this month, after it collects some data from 3rd party service (it’s time consuming process and I can’t really speed it up because of reasons)

Web framework options for Crystal

First of all - awesome-crystal is, well, awesome starting point when you’re trying to build your first app in this language - and not only web app, but app in general.

At this moment it seems we have two major players here - amber and kemal.

I think Amber is trying to be a Rails-for-Crystal kind of thing - it have ton of stuff build-in, app generator, support for various databases, migrations, build-in watcher for your app (so binary is being constantly recompiled during development, same for your assets), support for two popular ORMs, routes, you name it - basically all the goodies you are probably used to when working with mature web framework.

Kemal on the other hand is basically a Sinatra-for-Crystal, dead simple, you will be up&running within a minute.

I tried Amber, but right away I had problems with granite-orm, it seems last known stable version vs version from master branch produced different kind of errors. I was impatient to start so I quickly gave up on idea about having ORM as I kinda knew it would probably limit me anyway - as I was planning to do some batch upserts and CTE queries in my lovely Postgres. It’s still all fresh, so you have to applicate the effort either way <3.

So I went with Kemal.

Update 26.03.2018: It seems lucky is getting some traction recently as well and it seems it’s under heavy development. It looks really promising and I encourage you to take a look at it as well!

No ORM, so what about database?

There is a common db api for Crystal for which you can select specific driver - in my case I went postgres.

Because Kemal is so minimal you have to take care of the migrations process - you can use Amber’s micrate that is easily pluggable into any kind of application and does the job quite right. You can create simple shell script for convince and call it a day.

Known limitation of crystal-db is bulk insert, also it seems WHERE IN (?) doesn’t quite work for arrays. But if you’re in a rush like me you can work around this problem simply by building proper prepared query - you just need to pass corresponding arguments ($1, $2 ... $n) wrapped in parenthesis as needed and in the query and as an exec argument pass flat array - so you end up with prepared query that postgres can understand and arguments that will align nicely with that query.

And if you’re not in a hurry you can maybe prepare a pull request that fixes it ;).

Here is my small db class I’m using

require "pg"

module Models
  class Db
    @@instance : DB::Database
    @@instance = DB.open("postgresql://#{ENV["DB_USER"]}:#{ENV["DB_PASS"]}@#{ENV["DB_HOST"]}/#{ENV["DB_NAME"]}")

    def self.instance
      @@instance
    end

    def self.close
      @@instance.close
    end

    # this is for my bulk inserts / upserts
    protected def self.preapre_nested(rows, nested_count)
      rows.times.map do |i|
        "(" + Array.new(nested_count) { |j| "$#{(j + 1) + (i * nested_count)}" }.join(",") + ")"
      end.join(",")
    end
  end
end

Next we will take a look how we can hook in sidekiq.cr as a background worker and how to deal with deploying binaries to the server.

Update 22.03.2017:

I forgot to mention one kinda controversial/interesting (?) thing in Kemal - it adds it’s own custom X-Powered-By header to each response, you can remove it via middleware or by before filter - click here for more references.