Crystal Cove 01: From 0 to Webapp

Rishav Sharan
4 min readJun 28, 2018

--

Howdy adventurer!
If you are here, I am assuming that you have endured the Intro to the series.
Previous Article in the series: None
Next Article in the series: Crystal Cove 02: It’s a Viewtiful world

Are you ready to start on the journey of your life? Have you some Lembas in your bag? Some copper in your pockets? A trusty sword? A computer?

Goody! So we start with the very basics. If you haven’t installed Crystal yet, go ahead and follow this guide. If you are on Windows, open your WSL bash.

In your workspace directory, create a new Crystal project using;
crystal init app cove.

This will create a basic project structure and add the files you will need to use in your git checkins.

Talking of Git, this project is saved in https://github.com/rishavs/cove

At any time you feel lost, check out the repo and the code therein. I will try to give the relevant commits anchors at the start and end of every article.

Open your src/cove.cr file and update it to;

Now run this server using crystal run src/cove.cr

Open localhost:8080 in your browser.
You should see a page with “Hello world” text in it.

Congratulations! you have your first hello world server.

Currently, our tiny server has a basic logging handler which will show the requests made by the browser in the console. Currently any url you type in the browser address bar shows only the “Hello world” message. This is because we have no router in place. Time to rectify that.

Update the cove.cr file to;

Run Crystal.

This is the simplest router imaginable. Its just a case statement. If you goto http://localhost:8080/hola
you will see “Yo buddy!” as the text. going anywhere else will show “Bewarsies! This be wasteland!”

So whats happening here? Everytime a new request comes in, Crystal server creates a new instance of the context object. This object contains all the info your server needs like the request method, url, headers etc and the response that the server is to make.

In our router, we are saying that every time the context.request.resourse(this is where the url in the addressbar is stored) is instanced, check it against the case statements. If it is equal to “/hola” then give that particular response (the response being to print a text on the browser). If the `context.request.resourse` is anything else, goto the else statement instead.

One issue you may have found is that you type “/hola/” instead of “/hola”, our app doesn’t recognizes the route. and if you type in the url with a lot of trailing whitespace, it is again treated as a different url. So, to summarize; trailing whitespace in urls is bad and you need to stick to a convention of ending routes in a trailing “/” character.

Lets update our cove.cr to;

Run Crystal (I will not be repeating this bit everytime. 😄)

Notice the if !route_url.rstrip.ends_with?(“/”) line. rstrip removes trailing whitespaces from my url string and eds_with? checks if the last character is “/”. You can get more info on string manipulation in the APIDocs

If you run the code, you will see whether you type http://localhost:8080/hola

or http://localhost:8080/hola/ you get to see “Yo buddy!”

Nice, so now our routes are end-slash agnostic. For extra pain/fun i have also added a new route called “/json”. This simply creates a new json object that you can see if you navigate to http://localhost:8080/json/

Note the context.response.content_type = “application/json” in that block. This content-type declaration overrides the content declaration we made in line 10 context.response.content_type = “text/plain” . This is our app telling the browser that hey, this is json data you are seeing. So format and show it as such.

The current commit state of the project in my github repo is here.

Take a break. Things are going to get a wee bit scary. 🙀

We are now going to split our trusty cove.cr file and move the router stuff into its own separate file.

Create a new file route.cr in your src/cove folder. Add this stuff in it;

and update your main cove.cr to;

If you run the code, it should work as before. We have not added anything new. just structured the app better. in the main pogram, when the code reaches Router.run(route_method, route_url, context) , it then calls the function run in the Router class instance. This function cleans the url and then passes it through our basic router.

This ends the current article. If you have been checking the console, you would have seen amazing performance in the log.

rishav@Romana:/mnt/c/Users/Mockingbird/Documents/GitHub/cove$ crystal run src/cove.cr
GET /json/ — 200 (25.0µs)
GET /sss — 200 (12.0µs)

note them delicious numbers. 12 us! mmmmmmm….. and our app wasn’t even compiled in release mode. ✊

whats that? are you saying that its fast because its an empty app?

💩
Damn, you got me. But still, the numbers are good. And we will always keep an eye on them. When we add databases and heavy logic in our app, i would expect the time to be in a few hundred milliseconds.

Anywaysss, all done! See you next time. 💃
We will be speeding up more as we go forward. There is a lot to cover and I am assuming that you will be exploring the language and its ecosystem in the meantime.

The commit in our repo till this point is here.

--

--