Coming from a background of programming following oriented objects programming, mainly with Java and Ruby, I have been working mainly with javascript during the last 3 years, not touching much on Java. With that I have gained more and more interest in functional languages and this is where clojure comes in.
Here I am leaving some notes to my self while experimenting, gradually, with clojure. It is a slow learning. There are a lot of () and {} I am not used to :)
In this post I go through the steps to create an hello world web service with clojure while deploying it to heroku.
I am using a Mac and VSCode with leiningen to boostrap the project. To install leiningen on mac using homebrew just execute the following on terminal:
brew install leiningenHttp Web Service #
I am using compojure for this example, an http router framework over ring library.
To start we create a new project with:
lein new compojure web-apicd web-apiStart a server with:
lein ring server-headlessWe have now a web server working on http://localhost:3000 and when you do some change on your source code it updates. You only need to reload the browser.
Go to http://localhost:3000 and confirm you see Hello World.
Add Users route #
Go to src/web_api/handler.clj and add a GET route for users:
(GET "/users" [] "TODO: fetch users on database and send json response of users")
Final code will be
(ns web-api.handler
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]))
(defroutes app-routes
(GET "/" [] "Hello World")
(GET "/users" [] "TODO: fetch users on database and send json response of users")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Deploy using heroku #
We will now use heroku to deploy our web api and make it public, using heroku. You can check this tutorial from heroku
Here I am assuming you have heroku installed. I am in a Mac. For that I did:
brew install heroku/brew/herokuYou can check here how to install heroku cli here.
Create git repository #
We will need a git repository to integrate with heroku.
git initHeroku #
Login in heroku
heroku loginCreate application in heroku
heroku createIt will create a git repository on heroku servers for your application. With that, every time you push code for master or main branch on that repository, your application will be deployed. We will get there.
To confirm it, let see you have your git repository connection with repository created by heroku:
git remove -vYou should see something like:
heroku https://git.heroku.com/evening-thicket-89043.git (fetch)heroku https://git.heroku.com/evening-thicket-89043.git (push)Prepare your application to be deploy on heroku #
To allow heroku deploy our application we have to follow some conventions and add some dependencies.
We will start by defining a jar name for out final artifact. Go to project.clj and add:
:uberjar-name "web-api.jar"
(defproject web-api "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.10.0"]
[compojure "1.6.1"]
[ring/ring-defaults "0.3.2"]]
:plugins [[lein-ring "0.12.5"]]
:ring {:handler web-api.handler/app}
:uberjar-name "web-api.jar"
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.3.2"]]}})
Now we will add some dependencies:
jetty will be used as our web server and we use environ to be able to read environment variales:
(defproject web-api "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:min-lein-version "2.0.0"
:dependencies [[org.clojure/clojure "1.10.0"]
[compojure "1.6.1"]
[ring/ring-defaults "0.3.2"]
[ring/ring-jetty-adapter "1.7.1"]
[environ "1.1.0"]]
:plugins [[lein-ring "0.12.5"]]
:ring {:handler web-api.handler/app}
:uberjar-name "web-api.jar"
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring/ring-mock "0.3.2"]]}})
If you now execute
lein uberjarA web-api.jar is generate on target folder. This is our build that will be used by jetty web server to serve our application.
We now need to give those instructions for heroku. We start by defining the main function to be executed to start the application. Go to src/web_api/handler.clj and change it to:
(ns web-api.handler
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]
[ring.adapter.jetty :as jetty]
[environ.core :refer [env]]))
(defroutes app-routes
(GET "/" [] "Hello World")
(GET "/users" [] "TODO: fetch users on database and send json response of users")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))]
(jetty/run-jetty app {:port port :join? false})))
First we require jetty and environ dependencies
[ring.adapter.jetty :as jetty] [environ.core :refer [env]]
And then we add main function in the end of the file:
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))]
(jetty/run-jetty app {:port port :join? false})))
This function will be the one executed to start our web application. It is expecting a port argument, if it is not defined it will check port env (useful for heroku), if not defined, 5000 will be the default port.
Then it will start jetty with our routes defined on app function.
Now we create Procfile for heroku with the following content:
web: java $JVM_OPTS -cp target/web-api.jar clojure.main -m web-api.handler
This is configuring an heroku dyno called web (it needs to be this name), which will execute our jar calling the main function on src/web_api/handler
Send our code to heroku #
git add .git commit -m "Web api on clojure"git push heroku masterheroku openYour first web application on clojure is live.
Published