A Clojure library designed to construct SQL (and maybe other types of) queries dynamically and fetch persistent entities from a storage.
Here entities are the units of long-living state in a domain model of an application: person, user, customer, order, etc. A simplest entity instance may be thought of as a row in a table or a node in a graph.
There are no macros in norm - just data structures and functions.
- describe entities and their relations as a map
 - auto-discovery of entities and relations in a database
 - CRUD for the entities
 - controlled eager fetching of the 
has-oneandbelongs-torelated entities - fetch 
has-manyrelated entities on demand - built-in filters for the entity and its relations
 - filter by properties of related entities when selecting, updating and deleting
 - create and update aggregates in a transaction when related entities are embedded (encouraged by DDD)
 - build derived (slightly different) queries without repeating yourself
 - prepare entity data before saving
 - transform entity data after fetching
 - access to underlying Query Builder
 - sub-queries in WHERE clause
 - combine commands and requests for transactional execution
 - test entities descriptions with spec when creating a repository
 
TODO
- support enums trough namespaced keywords as described here
 - create specs for entity fields from column types
 - test for MySQL support
 
This README file is meant to be a quick reference for norm's functions. However a step-by-step
explanation of use-cases comes in handy when you want a gentle introduction and in-depth exploration
of the library.
Fortunately we can get one from AI nowadays. I have generated and read through this one and, except few halucinations, it looks to be useful for understanding what I have done a while ago.
I encourage you to generate a fresh one youself using code2tutorial
service. This way you get a tutorial for the latest version of norm composed by the latest LLM.
norm will be available from clojars as soon as the documentation is finished.
In the mean time, please, use jitpack repository in order to add it as a dependency to your project:
Also you want to ensure that there is a JDBC driver for the database of your choice among dependencies. norm supports H2 and PostgreSQL as of now, so check if at least one of the following is present:
[com.h2database/h2 "2.1.214"]
[org.postgresql/postgresql "42.2.12"]Require the core namespace of norm into a namespace in your project:
(ns my-project.db-component
  (:require [norm.core :as norm]))
;; or
(require '[norm.core :as norm])Prepare datasource to establish DB connection. Any jdbc.next's Connectable will do.
That means any javax.sql.DataSource implementation will do too.
;; prepare datasource the way you like it
;; using Hikari pool, for example
(def db (HikariDataSource.
          (doto (HikariConfig.)
                (.setJdbcUrl jdbc-url)
                (.setUsername username)
                (.setPassword password))))
;; or for testing with an in-memory H2 instance
(with-open [db (next.jdbc/get-connection {:dbtype "h2:mem"})]
  ;; your code goes here
)Describe entities and their relations in a map (let's call it mapping). There is a helper function that builds the mapping based on foreign keys of a provided database (described in the section below).
(def mapping {:address {:table :addresses}})See doc/mapping-example.md for the full-featured example of a mapping.
Build a repository using the mapping and the database connection from the previous steps.
(def repository (norm/create-repository :sql mapping {:db db}))Use entities from the repository to build queries and commands.
All queries get executed only with explicit fetch! (or execute! for commands) call or when
called with a function that has the ! suffix.
(let [page 0
      size 40
      sort :role
      clause {:active true}
      query (-> (norm/find (:employee repository) clause)
                ;; (norm/where clause) may be here when clause is not specified above
                (norm/order sort)
                (norm/skip (* page size))
                (norm/limit size))
      total (norm/fetch-count! query) ;; fetches total number of the records as if limit were not specified
      employees (norm/fetch! query)]  ;; fetches entities according to the query
  {:total total
   :employees employees})That's how you would execute a command which creates a user:
(-> (:user repository)
    (norm/create {:login "admin"})
    norm/execute!)
;; or
(norm/create! (:user repository) {:login "admin"})You can adjust and combine commands before executing. If you want multiple commands executed in
the same transaction, it may be achieved using then function:
(let [{:keys [user person]} repository]
  (-> (norm/create user {:login "admin"})
      (norm/then (norm/create person {:name "John Doe"}))
      (norm/then #(norm/create-relation user (:id (first %)) :person (:id (second %))))
      norm/execute!))Note that a unary function may be used as a step in a transaction. It receives a vector of previous commands' results and is supposed to return a command (or query) built upon the data that are unknown before actual execution.
This document uses the following terms to explain what norm is capable of:
- entity is a part of the application's domain model which has name, structure and relationships
with other entities. For example 
customer,orderandreceiptare entities of a typical business domain. - instance is a single data-point of the domain model. It has an id and its data complies with
the structure that corresponding entity declares. For exemple, an instance of 
customermay look this way{:id 1, :first-name "John", :last-name "Doe"}. - mapping is a description of the data-structures that represent entities and their relationships.
 - repository binds mapping to an underlying database where instances are stored.
As a result it provides a collection of 
normentities - objects that provide an interface to find instances and manage their lifecycle. 
The mapping is a regular clojure map that contains entities of your domain keyed by the entity names.
norm is easy to use on an existing database schema. You can automate writing of the mapping using provided helper method:
(require '[norm.sql.helper :refer [find-entities]])
(find-entities {:db db :schema "public"}) ; :schema may be omitted for auto-detectionThis helper method works only for H2 and PostgreSQL currently.
The autogenerated entity description may suffer from weird naming of entities and their relations
due to lack of context. It doesn't handle pluralization of irregular nouns (such as person - people).
It may miss relations when an entity references another entity more than once. But it provides a good
starting point to build a perfect version of the mapping.
Manual writing of the mapping from scratch is totally fine. Below you will find description of mapping's parts.
Database identifiers for table names and fields are supposed to be in kebab-case.
Those converted to snake_case when building SQL queries.
For a mapping example see doc/mapping-example.md.
The mapping has to contain at least one entity.
The only required property of an entity in the mapping is :table. It specifies which table of the databases
contains main data that consists the entity.
The following optional properties are supported:
:pk- primary key column name (defaults to:idwhen absent):fields- data fields of the entity (defaults to all columns of the table when absent):filter- imposes filtering rule on the rows of the table in case not every row represents an instance of the entity:prepare- an unary function that modifies an instance before saving:transform- an unary function that modifies an instance after fetching from storage (TODO consider renaming to :post-fetch):rels- a map that describes relations of the entity to other entities
TODO consider getting rid of :rels and making types of rels a top level property
Some entities may have bound or related entities.
Relations of an entity are described with a map where keys stand for names of the relations and values
(which are maps as well) convey the essence of the relation. Required properties of a relation
are :entity (the main entity is related to), :type (of the relation) and :fk (stands for foreign key).
norm supports following relation types:
This kind of relationship tells that the entity is an aggregate root (main entity) for another subordinate entity.
attachment has-one file means that there can be only one file bound to an attachment.
In the database that would look the following way: file has the attachment-id property that refers to the id
property in attachment's storage.
{:attachment {:table :attachments
              :rels  {:file {:type   :has-one
                             :entity :file
                             :fk     :attachment-id}}}
 :file       {:table :files}}This relationship allows the entity to aggregate several instances of a subordinate entity.
post has-many attachments means that there might be more than one attachment for a post.
In the database it would look the following way: attachment has the post-id property that refers to the id
property of post's storage.
{:post       {:table :posts
              :rels  {:attachments {:type   :has-many
                                    :entity :attachment
                                    :fk     :post-id}}}
 :attachment {:table :attachments}}This kind of relationship is reciprocal to the previous two. It tells that the entity is a subordinate part of another entity.
attachment belongs-to post means that attachment has the post-id property that refers to the id property in
post's storage. It doesn't say anything about how many attachments per post is allowed.
{:attachment {:table :attachments
              :rels  {:post {:type   :belongs-to
                             :entity :post
                             :fk     :post-id}}}
 :post       {:table :posts}}In case two entities aggregate each other's instances, they are in many-to-many relationship. Entities of this relationship are both aggregate roots and we cannot tell that one is a subordinate of another.
In the database, two entities are joined through an intermediary table that stores identifiers of both of them.
Such table goes to :join-table in the description of a relation. When description of a relation includes
:join-table it must include :rfk (stands for relation's foreign key) - a column in the join-table
which contains identifiers of related entity.
employee has-many responsibilities and responsibility in its turn has-many employees.
They relate through the join-table employees-responsibilities.
{:employee       {:table :employees
                  :rels {:responsibilities {:entity :responsibility
                                            :type :has-many
                                            :fk :employee-id
                                            :join-table :employees-responsibilities
                                            :rfk :responsibility-id}}}
 :responsibility {:table :responsibilities
                  :rels {:employees {:entity :employee
                                     :type :has-many
                                     :fk :responsibility-id
                                     :join-table :employees-responsibilities
                                     :rfk :employee-id}}}}:filter- clause to filter related entities:eager- when true, norm fetches a related entity eagerly (falseby default). Works only for:has-oneand:belongs-torelationships.
Repository is a collection of entities backed by a data source.
You can create a repository of chosen type using norm/create-repository function.
;; creating a SQL repository
(def repository (norm/create-repository :sql entities {:db db}))Now norm implements only SQL repositories which are backed by relational databases. I would love to implement support for graph databases (Neo4j for example) and other kinds of non-relational databases just to check if the general concept works fine with them.
You can create a derived repository using the following methods: norm/add-entity, norm/except and
norm/only. Please, refer to their documentation for details. Derived repositories can be handed over
to different parts of an application so that they cannot construct queries for certain entities.
norm/transaction allows creating a new transaction for a repository and use this object to collect
several queries and commands for transactional execution. Commands and queries may be appended into
the transaction using conj or norm/then.
TODO consider adding another arity for the commands where the last element is a transaction TODO in this case the first argument doesn't have to be an entity - a key of the entity would be enough
(-> (norm/transaction repository)
    (conj (norm/create (:user repository) {:login "admin"}))
    (norm/then (norm/create (:person repository) {:name "John Doe"}))
    norm/execute!)Most of the norm's core functions generate queries and commands which do not execute immediately.
Instead you can pass and modify them between different functions of an application before they reach
the right place to be actually executed. That gives us an opportunity to split the application to
pure functional (free from side-effects) and imperative (with side-effects) parts. All the functions
which cause side-effects have an exclamation mark ! at the end of their names.
What is the benefit of norm commands and queries instead of just plain old SQL queries in strings? norm represents them as structured objects which can be analyzed and adjusted so that the final query can be built in several independent steps. norm's queries allow building derived queries without repeating yourself when writing two slightly different queries. If you want to change one little piece in the middle of a query, it is easier to achieve modifying a structured object than trying to split a string in the right places and recombine it correctly.
You can create a query using norm/find function specifying the entity you want and optionally
a list of fields you are interested in and filtration criteria. norm/fetch! performs the query
and fetches matching entities from the database. You can also use norm/find! in order to fetch
the same entities immediately.
(def active-users (norm/find (:user repository) {:active true})) ;; `active-users` holds a query
(def users (norm/fetch! active-users)) ;; `users` gets actual data on active users in the database
;; or
(norm/find! (:user repository) {:active true}) ;; creates the same query and fetches the data immediately
;=> [{:id 1, :login "admin", :active true}, ...]If you already have a base query where you would like to specify an additional filtration criteria, you
can use norm/where function. The additional criteria is conjunct with the existing criterion using logical and.
(-> active-users
    (norm/where {:id [< 3]})
    norm/fetch!)
;=> [{:id 1, ...}, {:id 2, ...}]When you need a certain instance of an entity and its id is known, you can use norm/fetch-by-id!
to fetch that in a short line of code.
(norm/fetch-by-id! (:user repository) 5)
;=> {:id 5, ...}In order to fetch only related entities of an aggregate root, you can use norm/find-related function.
;; getting personal info of a user with a known login
(-> (:user repository)
    (norm/find-related :person {:login "jdoe"})
    norm/fetch!)
;=> {:id 3, :name "John Doe"}Commands, by contrast to the queries, modify data in the storage. Similar to queries, commands do
not take effect immediately. In order to execute a command, use norm/execute! function or one of
the following functions with an exclamation mark at the end.
norm/create allows creating a new instance of an entity in the backing storage.
(-> (:user repository)
    (norm/create {:login "guest"})
    norm/execute!)
;=> {:id 42}
;; or this way for brevity
(norm/create! (:user repository) {:login "guest"})norm/create accepts an aggregate with related data of the entity as the value. This way related
entities are created in the same transaction as the aggregate root. In case if the related entity
already exists and its id provided, a relation between newly created entity and the existing one
will be created in the same transaction.
;; this call creates a new user and a new related person in the same transaction
(-> (:user repository)
    (norm/create {:login "john", :person {:name "John Doe"}})
    norm/execute!)
;=> {:id 43}
;; this call creates a new user and a relation to an existing person by the person's id in one transaction
(-> (:user repository)
    (norm/create {:login "jane", :person {:id 63}})
    norm/execute!)
;=> {:id 44}norm/update updates existing entities in the storage. It takes a patch that will be applied to
matched entities and a filtration clause as the arguments. norm/execute! here returns a number of
updated instances.
;; activate a user with id = 42
(-> (:user repository)
    (norm/update {:active true} {:id 42})
    norm/execute!)
;=> 1norm/delete removes entities from the storage. It takes a filtration clause as the argument and
returns a number of removed entities.
;; remove all inactive users
(let [user (:user repository)]
  (-> (norm/delete user {:active false})
      norm/execute!))
;=> 99You can manage relations between existing entities using norm/create-relation and
norm/delete-relation functions. Depending on the type of relations, they works as norm/create
and norm/delete or norm/update both. They are especially useful for management of
many-to-many relationships.
You can modify existing entity or create a derived one with the following functions.
norm/with-filter applies a default filter to all the queries for specified entity.
For example, it may be useful in order to make different entities which reside in the same table
but can be distinguished using some discriminator field.
;; let's find top 10 incoming and outgoing documents
(let [document (:document repository)
      incoming (-> document (norm/with-filter {:type "incoming"}))
      outgoing (-> document (norm/with-filter {:type "outgoing"}))]
  {:incoming (-> (norm/find incoming) (norm/limit 10) norm/fetch!)
   :outgoing (-> (norm/find outgoing) (norm/limit 10) norm/fetch!)})norm/with-rels amends relations of the specified entity (adds new or replaces an old one with the
same name).
(def user-with-personal-data
     (norm/with-rels (:user repository)
                     {:person {:entity :person
                               :type   :has-one
                               :fk     :user-id
                               :eager  true}}))norm/with-eager makes specified relations to be fetched eagerly (works only for :has-one and
:belongs-to relations). It is just a special case of norm/with-rels which sets true to
the :eager property of specified relations which already exist.
(-> (:user repository)
    (norm/with-eager [:person])
    (norm/find {:id 1})
    (norm/fetch!))
;=> {:id 1, :login "admin", :person {:name "John Doe"}}Please, note that norm/with-eager is used on an entity from the repository before creation of
a query. It will not work on a partially defined query as the function takes and returns an entity
definition.
norm/then allows combining multiple commands and queries into a single transaction.
;; 3 users get created in the same transaction
(let [{:keys [user]} repository]
    (-> (norm/create user {:login "john"})
        (norm/then (norm/create user {:login "jane"}))
        (norm/then (norm/create user {:login "bob"}))
        norm/execute!))
;=> [{:id 1}, {:id 2}, {:id 3}]In case of transaction, norm/execute! returns a vector of results of each command and query in
the transaction preserving the order.
You can use an unary function as a command inside a transaction. It receives a vector of previous commands' results and is supposed to return a command or query built upon the data unknown before the actual execution.
(let [{:keys [user person]} repository]
  (-> (norm/create user {:login "john"})
      (norm/then (norm/create person {:name "John Doe"}))
      (norm/then #(norm/create-relation user (:id (first %)) :person (:id (second %))))
      norm/execute!))
;=> [{:id 1}, {:id 1}, 1]By default the result of transaction execution is a vector of individual commands' and queries' results but you can explicitly manage the result of transaction execution.
Assigning {:tx-result true}to metadata of one of the commands/queries in a transaction makes it
the result provider of the whole transaction.
;; making result of the last query in the transaction to be the result of the whole transaction
(let [{:keys [user]} repository
      create-john (norm/create user {:login "john"})
      create-jane (norm/create user {:login "jane"})
      select-john-and-jane (norm/find user {:login ["john" "jane"]})]
  (-> create-john
      (norm/then create-jane)
      (norm/then (vary-meta select-john-and-jane assoc :tx-result true))
      norm/execute!))
;=> [{:id 1, :login "john"}, {:id 2, :login "jane"}]:tx-result-fn metadata of a transaction object allows even more flexibility in modification of a transaction result
before return from the norm/execute! function. It allows providing a unary function that receives a vector of results
of individual commands in a transaction at the end of the execution and modifies the final result of the transaction.
;; transaction execution returns a vector of id for newly created entities
(let [{:keys [user]} repository
      create-john (norm/create user {:login "john"})
      create-jane (norm/create user {:login "jane"})]
  (-> create-john
      (norm/then create-jane)
      (vary-meta assoc :tx-result-fn #(mapv :id %))
      norm/execute!))
;=> [1, 2]TODO nested transactions and :tx-propagation.
In case you need fine-grained control over the generated queries, there is a low-level query builder at your disposal. It provides direct access to the functions which entities use for construction of queries and commands.
Require the sql namespace of norm into a namespace in your project:
(ns my-project.db-component
  (:require [norm.sql :as sql]))
;; or
(require '[norm.sql :as sql])There are four functions to build different kinds of queries: sql/insert, sql/update,
sql/select and sql/delete.
They take a database object as the first argument and a table-name keyword as the second.
The rest of the arguments differs for all of them so, please, refer to their docs for the details.
A query object can be compiled into SQL statement with str call:
(-> (sql/select db :users [:id :login])
    (norm/where {:active true
                 :role "admin"})
    str)
;=> "SELECT id AS \"id\", login AS \"login\" FROM users WHERE (active IS true AND role = ?)"If you want to use your own datatype as a parameter then the idiomatic approach of
implementing next.jdbc's SettableParameter protocol is enough.
TODO using vector for a list of choices
TODO using :or in a where-map
TODO using '(or ...)
TODO prefixing identifiers when building query with the name of an entity and ^:exact in where and order
If norm does not quite work for you in some scenarios, it is possible to get down a couple of layers of abstraction.
Consider the low level query builder in order to compose and execute SQL queries using
norm DSL. This DSL is used internally by the high-level API. It enables you defining your very own high-level API that
fits your case using already existing building blocks.
If you ever want to write a raw SQL query as a plain string, there is
next.jdbc that can execute it. norm uses it as a transitive dependency
in order to execute queries composed with high-level DSL. You can mix and match both of them in your project as you see
fit and completely migrate from one to another when time comes.
If norm is not quite what you are looking for, there are other libraries similar in some ways which I can positively or negatively recommend to spare you some time reading docs and testing.
In the world of lots of slightly different solutions, I wish all of them included such section into their documentation.
seql may be a better fit if you want to add predefined set of mutations into the entity model.
Toucan2 looks really close to norm in its intentions. It is older and has a company behind it. So the library probably have seen more usage in production. The first version of Toucan seemed to be too object oriented for a Clojure library: it required defining a record for every entity out there. The second version has emerged since. It has REPL-friendly syntax and allows defining named queries.
Hyperion allows defining entities however dealing with relations may be tricky.
Relational Mapper offers a lightweight solution for querying related entities based on HoneySQL. It has a namespace with functions to perform DB migrations based on the mapping. However it looks like creation, modification and deletion of entities is out of scope for this library.
I recommend you to skip Korma which is mentioned in every clojure-related resource for beginners. Korma is great for simple tasks but it is long abandoned and lacks some features that come in handy even in a moderately complex project. Maybe someone takes over the project in the future and makes it great again. For now I can only say many thanks to Korma for the inspiration it gave me. I made norm to be a better version of Korma.
Copyright © 2020-2025
This program and the accompanying materials are made available under the terms of the The MIT License (MIT).
See LICENSE file for the full tex of the license.