The Kit framework (https://kit-clj.github.io/) is a lightweight, modular framework for scalable web development in Clojure. It uses Reitit for routing and Integrant for system management, making it well-suited for building REST APIs. Below is an example of how you can create a simple REST API using Kit, based on its conventions and structure.
This example assumes you’ve set up a Kit project (e.g., yourname/guestbook
) and will demonstrate a basic REST API with endpoints to manage a list of messages (e.g., a guestbook). I'll walk you through the key parts.
If you haven’t already, create a Kit project:
clojure -Tclj-new create :template io.github.kit-clj :name yourname/guestbook
cd guestbook
Ensure your deps.edn
includes the necessary dependencies. Kit already includes Reitit and Ring, but you might want to add a database (e.g., SQLite via kit-sql
). Run this in your project directory:
(kit/install-module :kit/sql)
This adds SQL support with SQLite by default. Restart your REPL after this.
Edit src/clj/yourname/guestbook/web/routes/api.clj
(create it if it doesn’t exist). Here’s an example that defines RESTful endpoints:
(ns yourname.guestbook.web.routes.api
(:require
[reitit.ring :as ring]
[ring.util.http-response :as response]
[kit.edge.db.sql :refer [query]]))
;; Mock database operations (replace with real DB logic)
(defn get-messages [db]
(query db {:select [:*] :from [:messages]}))
(defn add-message [db message]
(query db {:insert-into :messages
:values [{:name (:name message) :message (:message message)}]}))
;; Route handlers
(defn get-messages-handler [{:keys [db]}]
(response/ok (get-messages db)))
(defn add-message-handler [{:keys [db body-params]}]
(let [result (add-message db body-params)]
(response/created "" {:message "Message added" :result result})))
;; API routes
(defn api-routes [{:keys [db]}]
[["/messages"
{:get {:summary "Get all messages"
:handler (fn [req] (get-messages-handler (assoc req :db db)))}
:post {:summary "Add a new message"
:parameters {:body [:map
[:name string?]
[:message string?]]}
:handler (fn [req] (add-message-handler (assoc req :db db)))}}]])
- GET /api/messages: Retrieves all messages.
- POST /api/messages: Adds a new message with a JSON body like
{"name": "John", "message": "Hello!"}
.
Edit resources/system.edn
to include the API routes and database connection:
{
:reitit.ring/router
{:routes #ig/ref :reitit.routes/api
:data {:middleware [;; Add middleware as needed
:ring.middleware.json/wrap-json-body
:ring.middleware.json/wrap-json-response]}}
:reitit.routes/api
{:base-path "/api"
:db #ig/ref :db.sql/connection}
:db.sql/connection
#profile {:dev {:jdbc-url "jdbc:sqlite:_dev.db"}
:test {:jdbc-url "jdbc:sqlite:_test.db"}
:prod {:jdbc-url #env JDBC_URL}}
:db.sql/query-fn
{:conn #ig/ref :db.sql/connection
:options {}
:filename "sql/queries.sql"}
:db.sql/migrations
{:store :database
:db {:datasource #ig/ref :db.sql/connection}
:migrate-on-init? true}
}
Create resources/sql/queries.sql
for the database schema and queries:
-- :name create-messages-table
-- :command :execute
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
-- :name get-messages :? :*
SELECT * FROM messages
-- :name add-message :! :n
INSERT INTO messages (name, message) VALUES (:name, :message)
Edit src/clj/yourname/guestbook/core.clj
to include the API routes:
(ns yourname.guestbook.core
(:require
[yourname.guestbook.web.routes.api]
[kit.edge.db.sql]
[integrant.core :as ig]))
;; Add :reitit.routes/api to the system config
(defmethod ig/init-key :reitit.routes/api [_ opts]
(yourname.guestbook.web.routes.api/api-routes opts))
Start the development server:
clojure -M:run
The API will be available at http://localhost:3000/api
.
-
Get messages:
curl http://localhost:3000/api/messages
Response:
[]
(initially empty). -
Add a message:
curl -X POST http://localhost:3000/api/messages \ -H "Content-Type: application/json" \ -d '{"name": "John", "message": "Hello, Kit!"}'
Response:
{"message": "Message added", "result": 1}
. -
Get messages again:
curl http://localhost:3000/api/messages
Response:
[{"id": 1, "name": "John", "message": "Hello, Kit!", "created_at": "2025-03-27T..."}]
.
- Routes: Defined using Reitit, which supports a data-driven approach to routing. The
:parameters
key ensures the POST request validates the JSON body. - Database: Uses
kit-sql
with SQLite for simplicity. You can swap it for PostgreSQL or another DB by adjusting the:db.sql/connection
config. - Middleware: Added JSON handling middleware to parse request bodies and format responses.
- System: Integrant ties everything together, injecting the database connection into the routes.
This is a basic example from the Kit framework’s structure. You can expand it by adding more endpoints, authentication, or error handling as needed. Check https://kit-clj.github.io/ for more details on customization!