Over a million developers have joined DZone.

Building Your First Crystal Web App, Part 1

DZone's Guide to

Building Your First Crystal Web App, Part 1

Learn how to use Crystal, a relatively new language that combines the best aspects of C/C++ and Ruby to help create better web apps.

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

In this article, we will cover building your first web application in Crystal. The application will utilize JSON Web Tokens (JWTs) to authenticate our users around the restricted resources of our app. The completed sample application can be found in this GitHub repo. I'd encourage you to check out the repo to follow, alongside this tutorial.

I wrote a technical article back in June titled "The Highs & Lows of Crystal." It was an introduction to the Crystal Language and an overview of my perspective on it, trying it out for the first time. I enjoyed trying out Crystal very much and the good news is that Crystal is still on the up, and more than ever people are trying it out, and finding that they really rather like it (just as I do!).

Today, I'd like to share a guide on building your first web app with Crystal and using JSON Web Tokens (JWTs) to authenticate users in said application. Before we dive into the technical tutorial, for those who haven't read the previous article, and perhaps don't know about Crystal Language at all, I'd like to quickly introduce it.

Crystal was introduced by its creator, Ary Borenszweig, back in mid-2014 as a general-purpose, object-oriented language with the aim of giving speeds close to C/C++ and being heavily syntactically influenced by Ruby. Crystal is a statically-typed, garbage collected language, boasting the speed, efficiency, and type safety of a compiled language.

Crystal runs on the LLVM, and although the feel of the language is rather similar to Ruby, Crystal compiles down to much more efficient code, with much greater processing speeds. It is also self-hosted with the Crystal compiler actually being written in Crystal itself. As I mentioned in my previous Crystal introduction, the language is built to support concurrency primitives right out of the box and uses similar concurrency concepts (including lightweight threading) to languages like Golang and Elixir.

Two of the things I like most about Crystal is the excellent built-in tooling available, and the ease with which you can bind C libraries. When I look at new languages, especially relatively immature languages, it's always very reassuring when the language has extensive built-in tooling available to help developers stay productive and happy! In Crystal, there are a bunch of tools that make hacking around in the language super fun but also help us to stay on the right track with semantics and more. I touched on those topics in a bit more detail in my previous article.

So that's Crystal in a nutshell! And now that we have that covered, we can get on with building our first Crystal web application and authenticating our app users with JWTs. If you would like to see the completed sample application to follow along with this article, it can be found in this GitHub repo.

Web App Concept TL;DR

The idea behind the web app that we're going to build is a community-driven Coding Challenge app in which members of the Open Source community can set coding challenges, and other members of the community can take the challenge and provide the solution. The joy of this app would be that coding challenges would have a whole array of solutions, in many different languages. They could act as great learning resources to others in the developer community, alongside being a fantastic learning experience in solving the challenge itself (an idea I'd love if someone were to build and launch in the real world!).

In our app members of the community will have to be authorized to post and access the coding challenges, and therefore we're going to need a means of authentication. For that, we're going to use JSON Web Tokens. We also need to store the challenges posted, for that, we'll use standard MySQL as our database.

Installing Crystal Lang

If you run on a Mac, you can install Crystal through Homebrew. Simply run:

brew update 
brew install crystal-lang 

If you run on Debian or Ubuntu, you can install through apt-get:

sudo apt-get install crystal 

One thing worth noting is that if you are going to install onto Ubuntu or Debian, you must first add the repository and signing key to your APT configuration. It's super simple to do, and instructions can be found here.

Alternatively, you can build from source with a tar.gz file. The latest files can be found on the releases page on GitHub: https://github.com/crystal-lang/crystal/releases.

Crystal does not yet have a direct Windows port but that being said, if you have Windows 10 onward you can install and run Crystal as normal through the Bash/Ubuntu for Windows Subsystem. Details for that can be found here.

To test that your installation has worked successfully, simply type crystal into your terminal, and you will have returned the Crystal CLI default menu. Now we have Crystal installed and working on our machines, we can get on with the fun part-building our app!

Creating Our Project

For building our web app, we shall use the popular Crystal web micro-framework, Kemal. Kemal is heavily influenced by Sinatra for Rubyists and works in a very similar way. I can confirm it is a joy to use - being both simple and functional. Handily, we can scaffold our new project using Crystal's built-in project tooling. Switch into your development working directory, and run the command:

crystal init app challenger 

With challenger being the name I gave my application. Feel free to name your project whatever you wish! Open up the project in your chosen editor, and head over to the file shard.yml in the project root. Much like having a Ruby Gems file, this YML file contains all of the dependencies for our project. Open up shard.yml and add the following:

    github: kemalcr/kemal
    branch: master

Once that's in, head back into your terminal and run shards install. Doing this will pull down Kemal and its dependencies for us to utilize.

The main file in your project is located in the /src directory and will be named with whatever you called your project, so for me it's: /src/challenger.cr. This file will contain the routes for our Kemal app alongside the controller logic, some config for our Kemal app, and not much else. To get our app up and running immediately, open up that file and add in the following:

require "./challenge/*"
require "kemal"

module Challenger

  get "/" do
    render "src/challenge/views/home.ecr", "src/challenge/views/layouts/main.ecr"

  Kemal.config.port = 6969


The require statements at the beginning of the file are telling our app to include Kemal to serve pages, and to include the src/challenge folder, which will contain files and folders necessary to our project such as Config, Models, Views, etc. Next, we have defined a route and instructed it to render two ECR files. This is because Kemal allows us to create layout templates, alongside file-specific views.

To enforce those views, create the layout file src/challenge/views/layouts/main.ecr:

  <title>Challenger // Coding Challenges</title>
  <link rel="stylesheet" href="/css/main.css"/>

  <section id="main">
    <div class="container">
      <%= content %>

  <script src="/js/main.js"></script>

Now create the home file src/challenge/views/home.ecr, and enter in whatever you like. I simply included an <h1> tag saying hello.

You can now test your application by running crystal run src/challenger.cr from your terminal. Your app should now be running on whichever port you specified-in this case ":6969".

Tune in next time when we'll cover how to add authentication to our application! 

Take a look at an Indigo.Design sample application to learn more about how apps are created with design to code software.

web dev ,crystal ,web application development

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}