Logo

dev-resources.site

for different kinds of informations.

ClojureScript on Cloudflare Workers

Published at
4/18/2021
Categories
serverless
cloudflare
clojurescript
devops
Author
pilt
Author
4 person written this
pilt
open
ClojureScript on Cloudflare Workers

I am a long-time user of AWS. Lambda is great, but for many use cases I feel like it brings too much complexity. You need to be aware of the surrounding AWS landscape and how Lambda ties into IAM, CloudWatch Events if you run cron-like jobs or API Gateway if you want to expose HTTP endpoints. Serverless framework and similar make things easier by hiding details. The hoops you need to go through to avoid cold starts are annoying.

I am yet to use Cloudflare Workers professionally and can't speak to how the service fares in practice but I'm excited by the operational simplicity it promises. I'm not using Durable Objects in this post but access to persistent memory in V8 isolates at edge nodes seems like a strong approach to scale out web services. It seems like you could avoid introducing many cache layers commonly found in web services.

Clojure I'm just fascinated by. I've been keeping tabs on it but have never done anything substantial with it. I have worked mostly in statically typed languages over the last years in code bases that follow an imperative style. While I appreciate types and compile-time errors and how these things bring at least some level of structure to any pile of code, there seems to be a sweet-spot where, if you can keep things simple enough, the type system adds more overhead than it brings clarity. I also appreciate that in Clojure, immutability is something that you need to actively opt out of.

Let's get started.

Project setup and worker source

First, we create an empty npm project that depends on shadow-cljs.

mkdir lispmachine && cd $_
Enter fullscreen mode Exit fullscreen mode

package.json:

{
  "name": "lispmachine",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "shadow-cljs": "^2.12.5"
  },
  "scripts": {
    "build": "shadow-cljs compile worker"
  }
}
Enter fullscreen mode Exit fullscreen mode

shadow-cljs.edn:

{:source-paths ["src/main"]
 :dependencies []
 :builds       {:worker {:target     :esm
                         :output-dir "dist"
                         :modules    {:worker {:exports {default lispmachine.worker/eventHandlers}
                                               }}}}}
Enter fullscreen mode Exit fullscreen mode

Next, add our worker code. src/main/lispmachine/worker.cljs:

(ns lispmachine.worker)

(def eventHandlers
 #js {:fetch
      (fn [request env]
          (js/Response. (str "Clojure <3 Cloudflare Workers!\n\n" (js/JSON.stringify request))))
      })
Enter fullscreen mode Exit fullscreen mode

Now we should be able to compile.

npm install
npm run build
Enter fullscreen mode Exit fullscreen mode

Cloudflare Workers setup

Next, install and configure Wrangler if you haven't already.

You need to be on 1.16.0 or later with support for ES modules. For now, to use ES modules on Workers you also need to opt-in to the Durable Objects open beta. (You can avoid ES modules and add a bundler but I didn't want that extra step. If you do, esbuild will do the trick.)

Add wrangler.toml:

name = "lispmachine"
workers_dev = true
type = "javascript"

[build]
cwd = "./dist"
command = "npm run build"
upload.format = "modules"
upload.main = "worker.js"
upload.rules = [{ type = "ESModule", globs = ["**/*.js"] }]
Enter fullscreen mode Exit fullscreen mode

I had to add upload.rules because shadow-cljs generates files with .js extensions and Wrangler assumes such files are CommonJS. I also had to use cwd = "./dist" after getting errors about the main module not being found with upload.main = "./dist/worker.js".

Now we are ready to take this live and see that it works!

CF_ACCOUNT_ID=... wrangler publish
Enter fullscreen mode Exit fullscreen mode
curl https://lispmachine.yoursubdomain.workers.dev
Enter fullscreen mode Exit fullscreen mode
Clojure <3 Cloudflare Workers!

{"fetcher":{},"redirect":"manual","headers":{},"url":"https://lispmachine.yoursubdomain.workers.dev/","method":"GET","bodyUsed":false,"body":null}
Enter fullscreen mode Exit fullscreen mode

(If anyone knows why request.cf isn't part of the output from (js/JSON.stringify request) please let me know.)

clojurescript Article's
30 articles in total
Favicon
Querido Yo del Futuro: Hoy intentaremos configurar una aplicación fullstack en Clojure
Favicon
Why I chose Clojure/Script for building Vade Studio
Favicon
Converting JS Libraries to Clojure: Part 1
Favicon
Deploy your ClojureScript App to Cloudflare Workers
Favicon
shadow-cljs and running tests
Favicon
Giving new life to existing Om legacy SPAs with re-om
Favicon
Is Clojure the only language you need?
Favicon
Building an Application with ClojureScript
Favicon
How to Set up A Clojure Script and Phoenix Project
Favicon
How to create a library that works with Clojure and ClojureScript
Favicon
Setup shadow-cljs react project
Favicon
Logging readable ClojureScript (.cljs) errors to sentry!!
Favicon
How can I create a ClojureScript web app from scratch with Reagent and npm?
Favicon
Set up SSL/TLS for shadow-cljs https server
Favicon
ClojureScript on Cloudflare Workers
Favicon
Storybook.JS with Shadow-CLJS
Favicon
World Statistics Exercise
Favicon
The Pleasure of Clojure(Script): Part 1
Favicon
Using Specter on tree data structures in Clojure
Favicon
Clojure Re-Frame Exercise
Favicon
¿Por qué Clojure?
Favicon
Implementing the feed
Favicon
Try something new this week
Favicon
ClojureScript async MVU
Favicon
ClojureScript simple MVU loop
Favicon
Integrating ClojureScript with JavaScript tooling
Favicon
Understanding Transducers in JavaScript
Favicon
Casting visual spells with p5.js and ClojureScript, part 1
Favicon
ClojureScript REPL Workflow
Favicon
Developing, Testing and Deploying AWS Lambda Functions written in ClojureScript

Featured ones: