DEV Community

loading...

ClojureScript + Firebase

Kazuki Baba
・4 min read

ClojureScript + Firebase

I'll talk about ClojureScript and Firebase. I use next libraries:

  • shadow-cljs ... a build tool
  • reagent ... React wrapper
  • firebase npm library

Setup a project

First of all, I generate a shadow-cljs project:

npx create-cljs-project cljs-firebase
Enter fullscreen mode Exit fullscreen mode

Its results:

cd cljs-firebase
tree -a -I node_modules .
.
├── .gitignore
├── package-lock.json
├── package.json
├── shadow-cljs.edn
└── src
    ├── main
    └── test
Enter fullscreen mode Exit fullscreen mode

Next, I edit a shadow-cljs.edn:

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 [[binaryage/devtools "1.0.2"]
  [reagent "1.0.0"]]

 :builds
 {:app {:target :browser
        :output-dir "public/js"
        :asset-path "/js"

        :modules
        {:main
         {:entries [cljs-firebase.core]}}

        :devtools
        {:http-root "public"
         :http-port 8080
         :preloads [devtools.preload]}

        :release
        {:output-dir "dist/js"}}}}
Enter fullscreen mode Exit fullscreen mode

And, I edit a package.json:

{
  "name": "cljs-firebase",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "dev": "shadow-cljs watch app"
  },
  "devDependencies": {
    "shadow-cljs": "2.11.11"
  },
  "dependencies": {}
}
Enter fullscreen mode Exit fullscreen mode

OK. I finished to set up a shadow-cljs, so I run the next command:

$ npm run dev

> cljs-firebase@0.0.1 dev /home/kbaba/repos/cljs-firebase
> shadow-cljs watch app

shadow-cljs - config: /home/kbaba/repos/cljs-firebase/shadow-cljs.edn
shadow-cljs - updating dependencies
Retrieving thheller/shadow-cljs/2.11.11/shadow-cljs-2.11.11.pom from https://repo.clojars.org/
Retrieving reagent/reagent/1.0.0/reagent-1.0.0.pom from https://repo.clojars.org/
Retrieving cljsjs/react/17.0.1-0/react-17.0.1-0.pom from https://repo.clojars.org/
Retrieving cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.pom from https://repo.clojars.org/
Retrieving cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.pom from https://repo.clojars.org/
Retrieving cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.jar from https://repo.clojars.org/
Retrieving thheller/shadow-cljs/2.11.11/shadow-cljs-2.11.11-aot.jar from https://repo.clojars.org/
Retrieving reagent/reagent/1.0.0/reagent-1.0.0.jar from https://repo.clojars.org/Retrieving
 cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.jar from https://repo.clojars.org/
Retrieving cljsjs/react/17.0.1-0/react-17.0.1-0.jar from https://repo.clojars.org/
shadow-cljs - dependencies updated
running: npm install --save --save-exact react@17.0.1 react-dom@17.0.1
+ react@17.0.1
+ react-dom@17.0.1
added 5 packages from 2 contributors and audited 104 packages in 2.176s

3 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

shadow-cljs - HTTP server available at http://localhost:8080
shadow-cljs - server version: 2.11.11 running at http://localhost:9630
shadow-cljs - nREPL server started on port 27730
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required namespace "cljs-firebase.core" is not available.
Enter fullscreen mode Exit fullscreen mode

The command setups dependencies, builds the system and runs a server at http://localhost:8080. But, I didn't write any code, so the build was failure now. I will fix those errors.

I type ctrl+c to finish npm run dev and run the next commands:

$ mkdir src/main/cljs-firebase
$ touch src/main/cljs-firebase/core.cljs
Enter fullscreen mode Exit fullscreen mode

I edit src/main/cljs-firebase/core.cljs:

(ns cljs-firebase.core
  (:require [reagent.dom :as rdom]))

(defn view []
  [:div "hello world"])

(defn mount-root []
  (let [root-el (.getElementById js/document "app")]
    (rdom/unmount-component-at-node root-el)
    (rdom/render view root-el)))

(defn ^:export init []
  (mount-root))
Enter fullscreen mode Exit fullscreen mode

I rerun npm run dev

$ npm run dev

> cljs-firebase@0.0.1 dev /home/kbaba/repos/cljs-firebase
> shadow-cljs watch app

shadow-cljs - config: /home/kbaba/repos/cljs-firebase/shadow-cljs.edn
shadow-cljs - HTTP server available at http://localhost:8080
shadow-cljs - server version: 2.11.11 running at http://localhost:9630
shadow-cljs - nREPL server started on port 38281
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[:app] Build completed. (173 files, 0 compiled, 0 warnings, 7.39s)
Enter fullscreen mode Exit fullscreen mode

And, I open a browser and access http://localhost:8080

localhost:8080

Initialize Firebase

I install firebase

npm install --save firebase
Enter fullscreen mode Exit fullscreen mode

And, I create a next file:

; src/main/cljs_firebase/firebase/init.cljs
(ns cljs-firebase.firebase.init
  (:require ["@firebase/app" :refer (firebase)]))

(defn initialize-firebase []
  (if (zero? (count (.-apps firebase)))
    (-> firebase
        (.initializeApp
         #js {:apiKey "..."
              :authDomain "..."
              :databaseURL "..."
              :projectId "..."
              :storageBucket "..."
              :messagingSenderId "..."
              :appId "..."}))
    (.-app firebase)))
Enter fullscreen mode Exit fullscreen mode

We can use npm packages by using "" at :require, so I wrote ["@firebase/app" :refer (firebase)]. And, I initialize Firebase by config in Firebase SDK snippet.

I edit src/main/cljs_firebase/core.cljs:

 (ns cljs-firebase.core
-  (:require [reagent.dom :as rdom]))
+  (:require [reagent.dom :as rdom]
+            [cljs-firebase.firebase.init :refer [initialize-firebase]]))

 (defn view []
   [:div "hello world"])
@@ -10,4 +11,5 @@
     (rdom/render view root-el)))

 (defn ^:export init []
+  (initialize-firebase)
   (mount-root))
Enter fullscreen mode Exit fullscreen mode

Next, I will connect to Firestore.

Connect to Firestore

I make a next file:

; src/main/word_penne/firebase/firestore.cljs
(ns cljs-firebase.firebase.firestore
  (:require ["@firebase/app" :refer (firebase)]
            ["@firebase/firestore"]))

(defn firestore []
  (.firestore firebase))
Enter fullscreen mode Exit fullscreen mode

And I edit src/main/cljs_firebase/core.cljs

(ns cljs-firebase.core
  (:require [reagent.dom :as rdom]
            [reagent.core :as r]
            [cljs-firebase.firebase.init :refer [initialize-firebase]]
            [cljs-firebase.firebase.firestore :refer [firestore]]))

(def todos (r/atom [{:task "aaa"}]))

(defn set-todos [val]
  (reset! todos val))

(defn load-todos-from-firestore []
  (-> (firestore)
      (.collection "todo")
      (.get)
      (.then
       (fn [snapshot]
         (let [result (r/atom [])]
           (.forEach snapshot
                     (fn [doc]
                       (swap! result conj
                              (conj {:uid (.-id doc)}
                                    (js->clj (.data doc) :keywordize-keys true)))))
           (set-todos @result))))))

(defn view []
  [:div "hello world"
   [:ul
    (js/console.log @todos)
    (for [todo @todos]
      [:li (:task todo)])]])

(defn mount-root []
  (let [root-el (.getElementById js/document "app")]
    (rdom/unmount-component-at-node root-el)
    (rdom/render view root-el)))

(defn ^:export init []
  (initialize-firebase)
  (load-todos-from-firestore)
  (mount-root))
Enter fullscreen mode Exit fullscreen mode

conclusions

I connected to Firebase and got data. Because I used npm packages directly, the system is easy to maintain. And, we can use Firebase Authentication, Functions, etc.
I made a system by using those and Reagent, re-frame, stylefy, etc.

Discussion (1)

Collapse
edutanaka profile image
Eduardo Tanaka

nice article! I think you forgot to put index.html code and where it will be