All Projects → chr15m → sitefox

chr15m / sitefox

Licence: other
Node + cljs backend web framework

Programming Languages

clojure
4091 projects

Projects that are alternatives of or similar to sitefox

Closp
Clojure template for web development (with SPA support)
Stars: ✭ 173 (-3.89%)
Mutual labels:  webframework
webfr
moved to: https://github.com/godzillaframework/godzilla.git
Stars: ✭ 13 (-92.78%)
Mutual labels:  webframework
rn-shadow-steroid
React Native with shadow-cljs on steroids
Stars: ✭ 57 (-68.33%)
Mutual labels:  shadow-cljs
Frappejs
Node + Electron + Vue based metadata web framework (inspired by Frappe)
Stars: ✭ 214 (+18.89%)
Mutual labels:  webframework
mango
mango is a powerful and simple golang web framework
Stars: ✭ 20 (-88.89%)
Mutual labels:  webframework
ui-gorilla
Reagent UI renderer for data visualization.
Stars: ✭ 14 (-92.22%)
Mutual labels:  shadow-cljs
Webgo
A minimal framework to build web apps; with handler chaining, middleware support; and most of all standard library compliant HTTP handlers(i.e. http.HandlerFunc).
Stars: ✭ 165 (-8.33%)
Mutual labels:  webframework
firmeve
a out-of-the-box, full-featured go framework supporting http, http2, websocket, tcp, udp, rpc and microservice
Stars: ✭ 36 (-80%)
Mutual labels:  webframework
threeagent
ClojureScript library for building Three.js apps in a reagent-like fashion
Stars: ✭ 124 (-31.11%)
Mutual labels:  clojurescript-library
godzilla
a powerful go web framework
Stars: ✭ 22 (-87.78%)
Mutual labels:  webframework
Golf
⛳️ The Golf web framework
Stars: ✭ 248 (+37.78%)
Mutual labels:  webframework
rough-cljs
A data-driven, reagent wrapper for RoughJS
Stars: ✭ 16 (-91.11%)
Mutual labels:  clojurescript-library
shadow-cljs-tailwindcss
shadow-cljs + tailwindcss
Stars: ✭ 136 (-24.44%)
Mutual labels:  shadow-cljs
Clog
CLOG - The Common Lisp Omnificent GUI
Stars: ✭ 181 (+0.56%)
Mutual labels:  webframework
presley
Presley - A lightweight web framework for Windows
Stars: ✭ 26 (-85.56%)
Mutual labels:  webframework
Alpas
🚀 The Rapid and Delightful Kotlin Web Framework. Easy, elegant, and productive!
Stars: ✭ 166 (-7.78%)
Mutual labels:  webframework
nim-servy
Servy is a fast, simple and lightweight micro web-framework for Nim
Stars: ✭ 30 (-83.33%)
Mutual labels:  webframework
learn-re-frame-course-files
🎦 Learn re-frame course files for building Cheffy app
Stars: ✭ 51 (-71.67%)
Mutual labels:  shadow-cljs
reagent-shadow-cljs-starter
A minimal starter pack for Reagent-based projects
Stars: ✭ 29 (-83.89%)
Mutual labels:  shadow-cljs
tsukuyomi
Asynchronous Web framework for Rust
Stars: ✭ 81 (-55%)
Mutual labels:  webframework

Web framework for ClojureScript on Node. WIP.

Sitefox logo

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

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

(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_URL - configure the outgoing SMTP server e.g. SMTP_SERVER=smtps://username:[email protected]/?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.13" :git/sha "39c7d80886d11e2459e56a1b9e2976000cde3be2"}}}

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.

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 "somedatabase":

DATABASE_URL=postgres://someuser:somepassword@somehost:5432/somedatabase

Or simply DATABASE_URL=postgres:///somedatabase if your user has local access on the deploy server.

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 db "select * from sometable WHERE x = 1")
    (.then (fn [rows] (print rows)))))

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

Sessions

Sessions are enabled by default and each visitor to your server will have their own session. The session data is persisted server side across page loads so you can use it to store authentication status for example. Sessions are backed into a namespaced kv table (see the database section above). You can read and write arbitrary JS data structures to the session using req.session.

To write a value to the session store (inside a route handler function):

(let [session (aget req "session")]
  (aset session "myvalue" 42))

To read a value from the session store:

(aget req "session" "myvalue")

Authentication

Sitefox wraps the Passport library to implement authentication. You can add simple email and password based authentication to your app with three function calls:

(defn setup-routes [app]
  (let [template (fs/readFileSync "index.html")]
    (web/reset-routes app)
    ; three calls to set up email based authentication
    (auth/setup-auth app)
    (auth/setup-email-based-auth app template "main")
    (auth/setup-reset-password app template "main")
    ; ... add your additional routes here ... ;
    ))

The template string passed in is an HTML document and "main" is the selector specifying where to mount the auth UI. This will set up the following routes by default where you can send users to sign up, sign in, and reset their password:

  • /auth/sign-in
  • /auth/sign-up
  • /auth/reset-password

It is also possible to override the default auth UI Reagent forms and the redirect URLs to customise them with your own versions. See the auth documentation for detail about how to supply your own Reagent forms. Also see the source code for the default Reagent auth forms if you want to make your own.

When a user signs up their data is persisted into the default Keyv database used by Sitefox. You can retrieve the currently authenticated user's datastructure on the request object:

(let [user (aget req "user")] ...)

You can then update the user's data and save their data back to the database. The applied-science.js-interop library is convenient for this (required here as j):

(p/let [user (aget req "user")]
  (j/assoc! user :somekey 42)
  (auth/save-user user))

If you want to create a new table it is useful to key it on the user's uuid which you can obtain with (:id user).

See the authentication example for more detail.

To add a new authentication scheme such as username based, or 3rd party oauth, consult the Passport docs and auth.cljs. Pull requests most welcome!

Email

Sitefox bundles nodemailer for sending emails. Configure your outgoing SMTP server:

SMTP_SERVER=smtps://username:[email protected]/?pool=true

Then you can use the send-email function as follows:

(-> (mail/send-email
      "[email protected]"
      "[email protected]"
      "This is my test email."
      :text "Hello, This is my first email from **Sitefox**. Thank you.")
    (.then js/console.log))

If you don't specify an SMTP server, the email module will be in debug mode. No emails will be sent and the ethereal.email service will be used. After running send-email you can print the url property of the result. You can use the links that are printed for testing your emails in dev mode.

Also see the send-email example project.

Forms

See the form validation example which uses node-input-validator and checks for CSRF problems.

To ensure you can POST without CSRF warnings you should create a hidden element like this (Reagent syntax):

[:input {:name "_csrf" :type "hidden" :default-value (.csrfToken req)}]

If you're making an ajax POST request from the client side, you should pass the CSRF token as a header. A valid token is available in the document's cookie and you can add it to the headers of a fetch request as follows:

(js/fetch \"/api/endpoint\"
          #js {:method \"POST\"
               :headers #js {:Content-Type \"application/json\"
                             :XSRF-Token (csrf-token)}
                             :body (js/JSON.stringify (clj->js data))})

Logging

By default the web server will write to log files in the ./logs folder. These files are automatically rotated by the server. There are two types of logs:

  • logs/access.log which are standard web access logs in "combined" format.
  • logs/error.log where console.log and console.error is written (and duplicated to stdout).

Note: the error.log is not written by default, you need to enable it by calling (logging/bind-console-to-file). This will rebind stdout to "tee" into the logfile ./logs/error.log as well as printing to stdout.

Tracebacks

If an error occurs in a running server in production it can be useful to have the traceback emailed to somebody. You can tell Sitefox to email all tracebacks using install-traceback-emailer from sitefox.tracebacks:

(install-traceback-emailer (env "ADMIN_EMAIL"))

Set the ADMIN_EMAIL environment variable to the email address where you want tracebacks to be sent. You can also specify an email as a string.

Live reloading

Live reloading is supported using both nbb and shadow-cljs. It is enabled by default when using the npm create scripts. Examples have more details.

Who

Sitefox was made by Chris McCormick (@mccrmx). I iterated on it while building sites for myself and for clients.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].