SkillAgentSearch skills...

Sitefox

Node + cljs backend web framework

Install / Use

/learn @chr15m/Sitefox
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Web framework for ClojureScript on Node.

<p align="center"> <img src="docs/logo.svg?sanitize=true" alt="Sitefox logo"><br/> </p>

In the tradition of Flask, and Sinatra. Designed for indie devs who ship fast. Battle tested on real sites.

Philosophy | Quick start | Documentation | API | Examples | Community

Getting started YouTube video.

(ns webserver
  (:require
    [promesa.core :as p]
    [sitefox.html :refer [render]]
    [sitefox.web :as web]))

(defn root [_req res]
  (->> (render [:h1 "Hello world!"])
       (.send res)))

(p/let [[app host port] (web/start)]
  (.get app "/" root)
  (print "Serving on" (str "http://" host ":" port)))

Philosophy

Batteries included

Environment variables

  • PORT - configure the port Sitefox web server binds to.
  • BIND_ADDRESS - configure the IP address Sitefox web server binds to.
  • SMTP_SERVER - configure the outgoing SMTP server e.g. SMTP_SERVER=smtps://username:password@mail.someserver.com/?pool=true.
  • DATABASE_URL - configure the database to connect to. Defaults to sqlite://./database.sqlite.

Quick start

The quickest way to start is using one of the create scripts which will set up an example project for you with one command. If you're building a simple website without much front-end interactivity beyond form submission, the nbb create script is the way:

npm init sitefox-nbb mywebsite

This will create a folder called mywebsite containing your new project. Note you can use Scittle to run cljs client-side.

If you're building a full-stack ClojureScript application the shadow-cljs create script is the way:

npm init sitefox-shadow-fullstack myapp

That will create a folder called myapp containing your new project.

Manually installing Sitefox

Add Sitefox to your project as a dependency:

{:deps
 {io.github.chr15m/sitefox {:git/tag "v0.0.26" :git/sha "e6ea2027b5d4277917732d43d550083c8e105da9"}}}

If you're using npm you can install sitefox as a dependency that way. If you do that you will need to add node_modules/sitefox/src to your classpath somehow.

npm i sitefox

Note: M1 Mac users may need to set the Python version in npm like this:

npm config set python python3

This is because the node-sqlite3 build sometimes fails without the setting. See this issue for more details.

Example server

An example server with two routes, one of which writes values to the key-value database.

(ns my.server
  (:require
    [promesa.core :as p]
    [sitefox.web :as web]
    [sitefox.db :refer [kv]]))

(defn home-page [req res]
  ; send a basic hello world response
  (.send res "Hello world!"))

(defn hello [req res]
  ; write a value to the key-value database
  (p/let [table (kv "sometable")
          r (.write table "key" 42)]
    (.json res true)))

(defn setup-routes [app]
  ; flush all routes from express
  (web/reset-routes app)
  ; set up an express route for "/"
  (.get app "/" home-page)
  ; set up an express route for "/hello"
  (.post app "/hello" hello)
  ; statically serve files from the "public" dir on "/"
  ; (or from "build" dir in PROD mode)
  (web/static-folder app "/" "public"))

(defn main! []
  ; create an express server and start serving
  ; BIND_ADDRESS & PORT env vars set host & port.
  (p/let [[app _host _port] (web/start)]
    ; set up the routes for the first time
    (setup-routes app)))

More Sitefox examples here.

Community

If you need support with Sitefox you can:

API

Web server & routes

Sitefox uses the express web server with sensible defaults for sessions and logging. See the express routing documentation for details.

Create a new server with web/start and set up a route which responds with "Hello world!" as follows:

(-> (web/start)
  (.then (fn [app host port]
    (.get app "/myroute"
      (fn [req res]
        (.send res "Hello world!"))))

Sitefox comes with an optional system to reload routes when the server is changed. Your express routes will be reloaded every time your server code is refreshed (e.g. by a shadow-cljs build). In this example the function setup-routes will be called when a rebuild occurs.

(defn setup-routes [app]
  ; flush all routes from express
  (web/reset-routes app)
  ; ask express to handle the route "/"
  (.get app "/" (fn [req res] (.send res "Hello world!"))))

; during the server setup hook up the reloader
(reloader (partial #'setup-routes app))

I recommend the promesa library for managing promise control flow. This example assumes require [promesa.core :as p]:

(p/let [[app host port] (web/start)]
  ; now use express `app` to set up routes and middleware
  )

Also see these examples:

Templates

Instead of templates, Sitefox offers shortcuts for server side Reagent rendering, merged wth HTML documents.

[sitefox.html :refer [render-into]]

You can load an HTML document and render Reagent forms into a selected element:

(def index-html (fs/readFileSync "index.html"))

(defn component-main []
  [:div
   [:h1 "Hello world!"]
   [:p "This is my content."]])

; this returns a new HTML string that can be returned
; e.g. with (.send res)
(render-into index-html "main" [component-main])

Sitefox uses node-html-parser and offers shortcuts for working with HTML & Reagent:

  • html/parse is shorthand for node-html-parser/parse.
  • html/render is shorthand for Reagent's render-to-static-markup.
  • html/$ is shorthand for the parser's querySelector.
  • html/$$ is shorthand for the parser's querySelectorAll.

Also see the templates example project.

Database

Sitefox makes it easy to start storing key-value data with no configuration. You can transition to more structured data later if you need it. It bundles Keyv which is a database backed key-value store. You can access the key-value store through db/kv and the underlying database through db/client.

See the full documentation for the db module.

By default a local sqlite database is used and you can start persisting data on the server immediately without any configuration. Once you move to production you can configure another database using the environment variable DATABASE_URL. For example, to use a postgres database called "DBNAME" you can access it as follows (depending on your network/local setup):

DATABASE_URL="postgres://%2Fvar%2Frun%2Fpostgresql/DBNAME"
DATABASE_URL=postgres://someuser:somepassword@somehost:5432/DBNAME
DATABASE_URL=postgres:///somedatabase

Note that you will also need to npm install @keyv/postgres if you want to use the Postgres backend.

To use the database and key-value interface first require the database module:

[sitefox.db :as db]

Now you can use db/kv to write a key-value to a namespaced "table":

(let [table (db/kv "sometable")]
  (.set table "key" "42"))

Retrieve the value again:

(-> (.get table "key")
  (.then (fn [val] (print val))))

You can use db/client to access the underlying database client. For example to make a query against the configured database:

(let [c (db/client)]
  (-> (.query c "select * from sometable WHERE x = 1")
    (.then (fn [rows] (print rows)))))

Again, promesa is recommended for managing control flow during database operations.

To explore key-value data from the command line use sqlite and jq to filter data like this:

sqlite3 database.sqlite "select * from keyv where key like 'SOMEPREFIX%';" | cut -f 2 -d "|" | jq '.'

Sqlite3 full stack traces

By default the node-sqlite3 module does not provide full stack traces with line numbers etc. when a database error occurs. It's possible to turn on verbose stack traces with a small performance penalty as follows:

(ns yourapp
  (:require
    ["sqlite3" :as sqlite3]))

(.verbose sqlite3)

Enabling Sqlite3 WAL mode

If you want to run sqlite3 in production you may run into the error SQLITE_BUSY: database is locked when performing simultaneous database operations from different clients. It is possible to resolve these concurrency and locking issues by enabling write-ahead logging mode in sqlite3 as follows:

(ns yourapp
  (:require
    [sitefox.db :refer [client]]))

(p/let [c (client)
        wal-mode-enabled (.query 

Related Skills

View on GitHub
GitHub Stars319
CategoryDevelopment
Updated7d ago
Forks8

Languages

Clojure

Security Score

100/100

Audited on Mar 22, 2026

No findings