It’s probably not what the cool kids are doing, but monoliths are cheaper than microservices when it comes to both time and money for individual developers, so that’s what we’re doing today, we’re going to be cheap. Hold your head up high though, DHH has let us all know that monoliths can be majestic.

If you haven’t installed leiningen yet, do it. Same goes for cursive.

Fire up that terminal, and be proud about what is about to happen

Some fancy footwork in the terminal to get started

So majestic is a lein template I made for my personal projects and it basically creates a simple rest api with email signups/jwt auth and it’s heroku ready. Let’s check it out.

The main entry point of the app

This file does a few things. It has a main function (just like java or c#) !! It also references a few other files: env, server and utils. Yesql doesn’t like json/jsonb so there’s a boilerplate-y function to get it working for when you need json in your columns. It also has three functions for starting and stopping the server from the repl (while also reloading code with tools.namespace). Let’s check out the server file.

There’s the functions that start/stop/restart the server from the repl and then there’s two other functions app and wrap-exceptions.

So clojure throws exceptions, one might say an excessive amount if you don’t rewire the way your brain works and only make small changes, and I mean small changes while testing in the repl and writing tests. The workflow usually goes write a function, test it in the repl with some data, write a test with even more data. If you have touched more than one file without restarting from the repl and without running tests (I prefer lein-test-refresh) you’re probably dangerously close to an exception and probably a null exception at that. It’s similar to statically typed systems where if you change too much at once without an IDE, you’re going to get “build” errors. It’s definitely a trade off, there’s less static analysis but faster iterations because you don’t have to compile on every change. One thing I think that may change this is spec but we’ll see.

The app function is a combination of http routing and functions that run on every request. KEEP IT TIGHT FOR SPEED. Seriously, don’t keep adding stuff when you don’t need it. Next up is routes which is where the actual routes are (finally getting somewhere)

From the top there’s a function that attempts to get a value from “Authorization: Bearer value” and then there’s a function that attempts to decode a json web token and stick the result value (user) in the request map.

Then there’s the routes, “protected” (requires an auth header) and just routes, there are two “test” routes, get/post “/”, just to make sure that the app is up and listening.

There’s two more files but I’m just going to talk about new user sign ups with email and a password.

There’s a lot going on here but it just boils down to the create! function which gets called on a post request to /users. It does quite a few things, first it makes sure that the email key is an actual email, then it checks the password for a minimum length and then it makes sure that the password and confirm-password are the same value. Old school, but there ain’t no school like the old school. If any of those predicates fail then it just returns a 400 with the relevant failure. If it succeeds, then it’s time to convert the request body into database parameters. What’s nice is that it’s very plain to see what’s happening thanks to the thread-first macro -> . First it takes the body of the request then calls body->db then insert-user<! and then assuming the user is actually inserted into the database, remove the password and then return a 200 with the inserted row. The only tricky thing here is that insert-user<! isn’t declared anywhere in the file, it’s actually generated from sql/users.sql with yesql at the top of the file (yesql/defqueries “sql/users.sql" (db/yesql-conn)) . There’s more to it, but you can discover the rest or change it if you want, fork, remix do whatever. But most importantly have fun!