Skip to content

A little demo web app in Clojure, using Component, Ring, Compojure, Selmer (and a database)

License

Notifications You must be signed in to change notification settings

seancorfield/usermanager-example

 
 

Repository files navigation

Example Web Application in Clojure

This is a simple web application using Component, Ring, Compojure, and Selmer connected to a local SQLite database.

Clojure beginners often ask for a "complete" example that they can look at to see how these common libraries fit together so this is a self-contained repo containing a fully-functional web app example.

On this branch, it has been migrated to the Polylith architecture:

Workspace

Following Polylith's Transitioning to Polylith guide, steps 1 & 2 were:

  • The entire application was moved to bases/web without any renaming,
  • workspace.edn was added,
  • A development deps.edn file was added at the root,
  • The application can be built in projects/usermanager,
  • The application can be run in :dev mode,
  • The tests can all be run via the poly tool.

Step 3 was:

  • The application was split into bases/web and components/usermanager, namespaces were updated to reflect the full split (usermanager.web.main and usermanager.usermanager.api were the two entry points; the old usermanager.model.user-manager became usermanager.usermanager.model to implement the api).

Step 4, over several iterations, was:

  • In preparation for adding more components as I refactored the code, I switched back to the more standard interface naming convention from api (which worked for the somewhat monolithic component identified in Step 2). So usermanager.usermanager.interface became the main component entry point and usermanager.usermanager.model implemented that interface.
  • Then I refactored the monolithic usermanager component into app-state, database, department, schema, schema-fixture (test-only), user, and web-server components.
  • schema has a subinterface for each table, so that new tables can be added and they are created and populated on the first run of the application without affecting previously created tables.
  • You still start the application with clojure -M:dev -m usermanager.web.main (with an optional port number).
  • You still build the uberjar with (cd projects/usermanager && clojure -X:uberjar)
  • You still run the uberjar with java -jar projects/usermanager/usermanager.jar (with an optional port number).
  • You can run tests for code that has changed since the last stable tag with clojure -M:poly test (optionally with :project to also run project tests). You can run the entire suite of tests with clojure -M:poly test :all :dev.

A variant of the non-Polylith version of this example application, using Integrant and Reitit (instead of Component and Compojure), can be found in Michaël Salihi's repo.

Requirements

This example assumes that you have a recent version of the Clojure CLI installed (at least 1.10.1.727), and provides a deps.edn file.

Clojure 1.10 (or later) is required. The "model" of this example app uses namespace-qualified keys in hash maps. It uses next.jdbc -- the "next generation" JDBC library for Clojure -- which produces namespace-qualified hash maps from result sets.

Usage

Clone the repo, cd into it, change to the polylith branch git checkout polylith and then see below to run the application (from the command-line or in a REPL), run the tests, build an uberjar, and run the uberjar.

Run the Application

You can run the application from the root of the workspace:

clojure -M:dev -m usermanager.web.main

It should create a SQLite database (usermanager_db) and populate two tables (department and addressbook) and start a Jetty instance on port 8080.

If that port is in use, start it on a different port. For example, port 8100:

clojure -M:dev -m usermanager.web.main 8100

Run the Application in a REPL

Start REPL

$ clj -M:dev

Once REPL starts, start the server on port 8888, for example:

user=> (require 'usermanager.web.main)                           ; load the code
user=> (in-ns 'usermanager.web.main)                             ; move to the namesapce
usermanager.web.main=> (def system (new-system 8888))            ; specify port
usermanager.web.main=> (alter-var-root #'system component/start) ; start the server

When you exit the REPL, the server will shutdown. You can shut it down in the REPL like this:

usermanager.web.main=> (alter-var-root #'system component/stop)   ; stop the server

Run the Tests

You can run all the tests via Polylith's poly tool:

clojure -M:poly test :all :dev

Normally you would just use clojure -M:poly test or clojure -M:poly test :dev to run tests that depend on code that has changed since your last stable commit (see the Polylith documentation for more details).

Build and Run an Uberjar

To build a compiled, runnable JAR file:

(cd projects/usermanager && clojure -T:build uber)

This uses tools.build under the hood to AOT-compile usermanager.web.main and build usermanager-standalone.jar in the projects/usermanager/target folder.

To run that uberjar:

java -jar projects/usermanager/target/usermanager-standalone.jar

As with running the application from the Clojure CLI or the REPL above, this should create a SQLite database (usermanager_db) and populate two tables (department and addressbook) and start a Jetty instance on port 8080.

If that port is in use, start it on a different port. For example, port 8100:

java -jar projects/usermanager/target/usermanager-standalone.jar 8100

Stop the application with ctrl-C (^C) on Linux/macOS or ctrl-Z (^Z) on Windows.

License & Copyright

Copyright (c) 2015-2023 Sean Corfield.

Distributed under the Apache Source License 2.0.

About

A little demo web app in Clojure, using Component, Ring, Compojure, Selmer (and a database)

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 5