Deploy your ClojureScript App to Cloudflare Workers
Tags: clojurescript, cloudflare, microservices, javascript
Published: 2023-04-16 07:57:33

**Updated: 19-Jul-2023**

Its now easy to deploy ClojureScript compiled output to Cloudflare workers.

Since the recent update with regards to [node.js compatibility](https://blog.cloudflare.com/workers-node-js-asynclocalstorage/), no need for clever tricks anymore. 

Here is step by step poc:

```bash
$ cd /tmp
$ pnpm create cloudflare@latest

In which directory do you want to create your application?
dir ./hello2

What type of application do you want to create?
type "Hello World" Worker

Do you want to use TypeScript?
no

Retrieving current workerd compatibility date
compatibility date 2023-07-17

Do you want to use git for version control?
no

Do you want to deploy your application?
no

$ cd hello2
```

At this point, check if we're ready to continue.

```bash
$ pnpm exec wrangler --version
 ⛅️ wrangler 3.0.0 (update available 3.3.0)
```

Now add shadow-cljs. 

```bash
$ pnpm install --save-dev shadow-cljs@latest
```

Shadow-cljs just need simple configuration, we can generate default config with `pnpm exec shadow-cljs init` or just create one by ourself.

```bash
$ cat <<EOF > shadow-cljs.edn
{:source-paths ["cljs/main"]
 :dependencies []
 :builds {:cf {:target :npm-module
               :output-dir "out"
               :entries [hello]}}}
EOF
```

Then modify its content to be like below, the important part is that we set `:target` to `:node-module`.

Next, verify if shadow-cljs is ready. This will download required clojure/java/maven dependencies if necessary.

```bash
$ pnpm exec shadow-cljs info
```

One more step in cljs part is create our sample source file:

```bash
$ mkdir -p cljs/main
$ cat <<EOF > cljs/main/hello.cljs
(ns hello)

(defn ^:export message []
  "Hello from Clojurescript")
EOF
```

Partly done, take a breath first. 

---

Now, for this demo, we compile the cljs source like so: 

```bash
$ pnpm exec shadow-cljs release :cf
```

It will put the compiled output to `./out/` directory. Now lets modify workers entry file in `src/worker.js`, for this demo, we import the compiled cljs output and use it there somehow, like shown below: 

```js
import { message } from '../out/hello';

export default {
	async fetch(request, env, ctx) {
		return new Response(message());
	},
};
```

Only final **important** step is to activate the node compatibility in wrangler.toml, set `node_compat = true`.

Our full `wrangler.toml` file now would look more or less like this:

```toml
name = "hello2"
main = "src/worker.js"
compatibility_date = "2023-07-17"
node_compat = true
```

Ok, its done now. 

---

We could test it first locally: 

```bash
$ pnpm exec wrangler dev
```

On separate terminal, test if dev server ok:

```bash
$ curl -v http://127.0.0.1:8787/
```

No error, right. Okay, so lets ship it.

```bash
$ pnpm exec wrangler deploy
```

It will authorize with your cloudflare account first before deploying, if every thing is ok, you would see message more or less like below:


```
...
Total Upload: 56.67 KiB / gzip: 11.25 KiB
Uploaded hello2 (4.46 sec)
Published hello2 (8.61 sec)
  https://hello2.datafy.workers.dev
Current Deployment ID: eae28984-581c-4ef2-b821-0932efac5003
```


Thank you for reading.

---

**Update**

As of today, compile cljs to `:esm` is also works. Below are relevant changes:

`shadow-cljs.edn`

```clojure
{:source-paths ["cljs/main"],
 :dependencies [],
 :builds {:cf {:target :node-module,
               :output-dir "out",
               :entries [hello]},
          :cf2 {:target :esm,
                :output-dir "out2",
                :modules {:hello {:exports {message hello/message}}}}}}
```

`src/worker.js`

```js
import { message } from '../out2/hello';

export default {
	async fetch(request, env, ctx) {
		return new Response(message());
	},
};
```

`wrangler.toml`

```toml
name = "hello2"
main = "src/worker.js"
compatibility_date = "2023-07-17"
```