This is part two of a series teaching Clojure to people with little or no programming experience. In Part 1 you built a small website and ran it on your computer.
Right now the only person who can see your website is you. In this part we'll fix that. By the end you'll have a link you can open on your phone and send to a friend.
The server code barely changes. Most of the work is teaching the outside world how to run it.
What you need
On top of what you have from Part 1:
- A free Vercel account. Vercel is where your website will run.
- Node.js installed on your computer. We need it to install and run Vercel's command line tool.
To check that Node is installed, run npm --version in the terminal.
Open your my-site folder in VS Code and open a terminal (Terminal > New Terminal), just like last time.
Why a few things have to change
On your computer, you run a command and the program runs. When someone else's computer runs it, three things need to be true:
- The program has to listen on whichever port they tell it to, not the one you hard-coded.
- The program has to be bundled into a single file, so it can be copied around.
- They need a recipe that says how to build and start it.
We'll take these one at a time, and after each one you can check that your website still works.
Step 1: Let the outside world choose the port
In Part 1, you hard-coded the port as 8080 ((http/run-server handler {:port 8080})). Vercel requires that your program use a port they define in an environment variable.
Environment variables live outside your code, and you can look them up by name.
The one you'll use is called PORT.
Replace -main in src/app/core.clj with:
Read it from the inside out:
(System/getenv "PORT")asks "is there an environment variable calledPORT?" You get its value, ornil(nothing) if there isn't one.(or ... "8080")means "usePORT, or (ifPORTis nil) use"8080"instead".(Integer/parseInt ...)turns the text"8080"into the number8080. Environment variables are always text, so we have to convert.letgives a name to a value, soportrefers to either the value ofPORTor8080.
Run it (clojure -M:run) and open localhost:8080. Nothing changed, because PORT was never defined.
Now stop the server (Ctrl+C) and start it with a port of your choosing:
(On Windows PowerShell this would be $env:PORT=3000; clojure -M:run.)
The terminal now says Server running on port 3000.
Your website is now running at localhost:3000. It adapts to its environment.
Stop the server again with Ctrl+C.
Step 2: Bundle it into one file
Right now your program is a folder of source files plus a set of libraries that clojure downloads on demand.
To copy it to another machine, we want a single file with everything inside.
In the Java world that file is called a jar, and one that contains everything is called an uberjar.
We need three changes to create an uberjar.
First, add (:gen-class) to the ns at the top of src/app/core.clj:
This tells Java that this file can be the starting point of a packaged program.
Second, replace deps.edn with this. The only new part is the :build alias:
:run is still a shortcut for starting the program. :build is a shortcut for a tool, tools.build, that knows how to bundle a program.
Third, create a file called build.clj in the root of the project (next to deps.edn, not inside src):
This is a program whose job is to build your program.
Read the steps in uber in order and you'll get the idea: delete the old target folder, copy the source in, compile it, and pack it together with all the libraries into target/app.jar.
Now build it:
The first time, Clojure downloads the build tool, so it can take a minute. It prints nothing when it succeeds. Check that target/app.jar appeared in the file explorer.
Then run the jar directly:
Open localhost:8080. You should see Hello, World!.
Checkpoint: this is exactly what Vercel will do with your project. If it works here, the hard part is done.
Stop the server with Ctrl+C.
Step 3: Write the recipe
Create a file called Dockerfile.vercel in the root of the project:
This is the recipe Vercel follows to build and run your program. Vercel looks for a file with exactly this name, and runs the result.
You don't need to understand the details of this file right now. Its shape is what matters. It has two parts, each starting with FROM:
- Build. Start from a machine that has Clojure installed, copy in
deps.edn,build.cljandsrc, and run:clojure -T:build uber. - Run. Start from a much smaller machine that has only Java, copy in
app.jar, and start it usingjava -jar app.jar.
Step 4: Leave some things out
Create two more files in the root of the project.
.dockerignore lists things that shouldn't be sent to the build machine:
.gitignore lists things you don't want to save into version control (when you start using Git, it will skip these):
Step 5: Write some documentation
Add one more file called README.md so future-you remembers what this folder is:
Your project should now look like this:
If you compare it with vercel-clojure, the template this series is based on, you'll find the same files. But you wrote them yourself, and you know why each one is there.
Step 6: Deploy
Install Vercel's command line tool:
Log in. This opens your browser to confirm who you are:
In your browser, Vercel asks you to authorize your device. Once you do (you'll see a message like "Congratulations! You are now signed in."), run the command below to deploy:
The first time, Vercel asks a few questions about your project. When asked "Which project?", select "Create a new project". For the other questions, the suggested answers are usually fine. After you answer all of the questions, Vercel will build and deploy your website.
When it finishes you'll get a link that ends in vercel.app.
Open it. You should see Hello, World!.
That's your program, running in the cloud, reachable from anywhere in the world.
Checkpoint: open the link on your phone or another computer.
If something went wrong
vercel: command not found: the install in Step 6 didn't finish, or your terminal needs to be reopened.- The build fails on Vercel: the output in the terminal shows the failing line. The most common cause is a typo in
build.cljorDockerfile.vercel. Ifclojure -T:build uberworks on your computer, the build step should work on Vercel. - The link shows an error page: first check that
java -jar target/app.jarworks locally. If it does, runvercel logswith your deployment's link to see what your program printed. - The link asks you to log in to Vercel: Vercel can protect some of the links it creates so only you can see them. Open the link labelled Production in the output of
vercel deploy --prod, or find your project in the Vercel dashboard and use its production domain.
What you learned
- Your program now reads its port from an environment variable (
PORT) and falls back to8080when there isn't one, so the same code works on your computer and on Vercel. - An uberjar is a single file with your code and all of its libraries.
clojure -T:build ubercreates it andjava -jar target/app.jarruns it. build.cljis a small program whose job is to build your program, and the:buildalias indeps.ednis how you call it.Dockerfile.vercelis the recipe Vercel follows: one stage builds the jar, the other runs it.- Deploying means giving another computer a recipe and the files it needs.
vercel deploy --proddoes that, and the result is a link anyone on the internet can open.
Next
We'll stop saying "Hello, World!" and put your own words and pages on the website.