With the vibrant community, the Clojure language has a good core of libraries, collections, and other ready-to-use tools. According to a 2019 survey, 25% of Clojure users create or maintain open source solutions. Some programmers also check and fix issues (17%), or report on them (15%).
Although all these libs aim to simplify Clojure programming tasks, they may confuse a beginner. Sometimes, it’s difficult to understand how to put all of them together and find an optimal solution for a specific task. So, now I want to show you my favorite tools for programming in Clojure. We will talk about:
- working with web server and endpoints.
- interaction with web servers and databases.
- routing on servers.
- using GraphQL in Clojure programming.
- support for modular architecture.
Let’s start the trip to the Clojure programming world!
Note: If moving from Java and other similar languages, you should know that there aren’t any traditional frameworks in Clojure. Everything is based on libs and their collections. This makes the language more flexible and multi-purpose.
The Clojure ecosystem includes libs of different complexities. Some of them are only a set of additional functions (for example, a library for working with time). Meanwhile, system libs provide a platform for creating the entire framework. Such libraries allow the programmer to present an application as a set of components, describe the connections between them, and so on. The library itself ensures the creation of dependency graphs. For example, it can convert components into more understandable things, like functions.
Purpose: convenient work with web server and end-points
The HTTP server abstraction is a key element of the Clojure web stack. It enables simple work with web servers and end-points, which is essential for successful app development. Created with WSGI (Python) and Rack (Ruby) in mind, Ring abstracts the information of HTTP into a clear, unified API. As a result, the web developer can build modular systems consisting of components and share these components among various servers and applications.
How Does Ring Work?
It gets HTTP requests and returns HTTP responses. So, Ring isn’t a so-called web application framework.
Reasons for Using Ring
As a dominant web library in Clojure, Ring is a nice choice for beginners. First of all, its main function is to provide a convenient interface for a wide range of web-servers. From this point of view, the technology is extremely useful and works perfectly.
Another Ring feature is providing a set of modules (‘middleware’). So, it allows you to use different pre-written libraries, compatible with each other.
Most Clojure programmers choose Ring for end-points because of its ability to use middleware. For instance, these modules allow transforming required parameters from a URL to the Clojure code map. A web developer can easily write new middleware and integrate it into the ecosystem. For example, using the cemerick/friend library, you can fully manage the authorization process, apply different strategies (starting from the login and password, ending with OAuth), etc. Ring middleware helps to conveniently describe and start the process. It closes all endpoints that are prohibited for unauthorized users.
Experience in using Ring is important for working with other Clojure technologies. It is the first thing you can use to get an app moving.
Purpose: server start up
The event-driven, highly effective HTTP server/client library includes WebSocket, as well as asynchronous support.
How Does Http-kit Work?
It’s a library for organizing a proper web server interaction. Http-kit is suitable for highly concurrent asynchronous or synchronous applications. As for WebSocket and HTTP long polling/streaming, it’s better to feature a unified API.
Reasons for Using Http-kit
Http-kit provides an opportunity for working both with sync and async systems. The first option is simple, while the second works faster. So, you can make a choice based on your specific purpose. Moreover, you can use the library with Ring. It works almost like the Jetty adapter.
The library ensures support for WebSockets, as well as perfect handling of long-held HTTP requests. So, creating a real-time app becomes easier. What's more, it’s an open source project. The library is available on GitHub under the Apache License Version 2.0.
HTTP-kit shows high performance and works fast in spite of great loads. At the same time, each connection requires only a few KB of memory. A client/server that is written from scratch is available as a single ~90KB JAR with 0 dependencies and ~3k lines of clear code.
Many software developers consider HTTP-kit to be a basic tool for Clojure programming.
3. Compojure and Bidi
Purpose: routing on server
Both Compojure and Bidi are technologies for routing in web apps. The first library is quite popular among the community, while the second one is known as a handy solution for writing in ClojureScript.
How Do Compojure and Bidi Work?
These small libraries ensure routing on a web server. As a result, a software developer can write applications that are composed of several separate parts.
What’s the Difference Between Them?
The two libraries perform the same function. But, unlike Compojure, Bidi:
- supports both clj and cljs.
- is isomorphic and extensible.
The main difference is that in Bidi routes are data structures. There are no macros here. So, the library provides a convenient bi-directional approach and other advantages. Some web developers prefer Bidi because it shows each route of a particular handler. Moreover, there is an opportunity to read the routes from a configuration file, generate and transform them by functions, and introspect. They work independently from handled requests. So, the developer can match on things that aren’t necessarily handlers (for example, keywords).
Purpose: implementation of GraphQL in Clojure programming.
This popular Clojure library is useful for those, who want to implement GraphQL while working with web APIs.
How Does Lacinia Work?
Reasons for Using Lacinia
Using GraphQL, you can easily get a richer, more complete Web API. Lacinia simplifies API development because it supports inline/named query fragments, GraphQL Schema Introspection, subscriptions, interfaces, unions, enums, inputs, and custom scalars.
Written in an EDN-based schema language, the library perfectly works with GraphQL queries. It is built on Antlr4. Lacinia helps to rich efficient asynchronous query execution. You can plug it into any Clojure HTTP pipeline and use the companion library lacinia-pedestal for HTTP support.
5. HoneySQL and HugSQL
Purpose: successful database interaction
Many web programmers feel more comfortable working with SQL technologies, such as Oracle, MS SQL, MySQL, PostgreSQL, SQLite, etc. HoneySQL and HugSQL are query builders that provide stable access to SQL databases in Clojure applications.
How Do HoneySQL and HugSQL Work?
Both libraries bring SQL into the world of Clojure programming. So, you will be able to write SQL database commands even in Clojure web apps.
What’s the Difference Between Them?
Using HugSQL, you start with writing separate SQL files and putting them in your application. The query constructor is inspired by another popular library, YeSQL. It not only parses SQL files into Clojure functions but is also suitable for more use cases than YeSQL. HugSQL is actively maintained. It has protocol-based adapters supporting multiple database libs.
HugSQL has several advantages due to two fundamental features:
- the queries are written directly, one query for each required function.
- the queries are written in SQL.
HugSQL provides separation of Clojure syntax and SQL semantics (WHAT and HOW).
- We describe WHAT to do in Clojure
(get-user-emails-by-username “Alexandr Petrov”) ;-> [“[email protected]” “[email protected]”]
- We describe HOW to do this in SQL
— :name get-user-emails-by-username :? :* select user_emails.email from user_emails where user_emails.user_id = :id
As a result, we can change the logic of getting the data directly in SQL, without rewriting Clojure code. It’s useful when a database schema is changed, and the old query doesn’t return the needed structure.
Moreover, SQL itself is a much more powerful tool for working with a specific DBMS. It allows for the writing of large complex queries with “join,” aggregations, and Window Functions, which can be extremely difficult in Clojure.
Furthermore, SQL allows us to use the benefits of a specific DBMS (for example, Postgres). It’s important if you are sure the database won’t be changed.
- It’s difficult to change the DBMS in the middle of the project. It will be necessary to check a lot of requests and carefully test the syntax support and specific output.
- You can pass parameters to SQL queries. But, in some cases, the parts of the queries must depended upon certain conditions. Although HugSQL snippets allow you to complete this task, more often you need to do this using Clojure.
HoneySQL doesn’t provide such separation of SQL and Clojure code. You will see the queries as a map.
- It’s independent of a specific implementation of the DBMS because it usually avoids direct SQL queries. Instead of them, it uses specific DSL.
- It allows you to maintain your own DSL based on Clojure functions/macros. You can also create a query builder while saving intermediate requests on the Clojure side. For example, if you need to define a user by ID or other complex logic, you can save part of the HoneySQL structure. It will show that we need exactly the user, not the goods or other data. This “fragment” can be substituted into the necessary queries. As a result, you will get a shorter code.
- It can be challenging to use benefits of a specific DBMS, as well as to write complex queries (for example, with Window Functions).
- The Clojure programmer needs to separate the code for retrieving data and the code for processing this data. The separation leads to additional work on refactoring.
Purpose: support of modular architecture
Helpful technology for developing highly modular applications in Clojure. The framework supports effective interaction between modules and has good compatibility with other parts of the varied Clojure ecosystem.
How Does danielsz/system Work?
The library allows a web programmer to use a component-based approach. So, you can build an application using modules/components and the connections between them. Each component is a special object. It has its own lifecycle. The programmer describes how it starts, what it needs to start, and how it stops.
For example, our application consists of a web server and a database. It makes no sense to start the web server before the database is activated. We describe the database component, describe the server component, and the server’s dependency on the database. When launching the project, we indicate that we need to start the web server. The library will automatically try to activate the database. Then, if possible, it will start the server and transfer the finished activated database component to the web server component. Then, if necessary, the system will be stopped.
Reasons for Using danielsz/system
Unlike the description of the entire application with a set of ordinary Clojure functions, the project description as components makes it easy to replace one component with another. For example, when testing an application, replacing one database with another, or turning it off completely, replacing it with the current data.
In the case of functions, you would change old functions with new ones. They will have the same name, which often causes bugs. danielsz/system is a ready-made set of solutions (for a web server, database, and much more), organized as a set of components.
I hope that you now have a better understanding of how to use Clojure and what solutions should be implemented in your project. My future articles will include the benefits of this powerful and beautiful language, as well as some tips on development of web applications, services, and APIs. So, subscribe to FreshCode blog, if you want to learn more about Clojure programming world.