RESTful Services with Pedestal

How to build HTTP services with Pedestal using routes, interceptors, and current Clojure CLI project setup instead of legacy scaffolding.

Pedestal is most useful when your HTTP service needs an explicit request-processing pipeline instead of a thin handler chain. Its core idea is the interceptor queue: request and response work becomes a sequence of small units that can validate input, enrich context, enforce policy, and construct a response without collapsing everything into one large handler.

That architecture makes Pedestal a strong fit for APIs that have meaningful cross-cutting concerns such as authentication, auditing, content negotiation, tracing, and error shaping. It is less compelling when you only need a tiny CRUD layer and want the smallest possible abstraction surface.

Start with a Current Project Shape

For a new example, prefer the Clojure CLI with deps.edn instead of old Leiningen scaffolding. Current Pedestal docs are on the 0.8 line, where connectors are the preferred startup model:

1{:paths ["src" "resources"]
2 :deps {org.clojure/clojure {:mvn/version "1.12.4"}
3        io.pedestal/pedestal.service {:mvn/version "0.8.2-beta-1"}
4        io.pedestal/pedestal.http-kit {:mvn/version "0.8.2-beta-1"}}}

The exact version numbers will evolve, but the structure matters more than the snapshot:

  • one explicit dependency file
  • ordinary namespaces under src
  • build and packaging handled by the Clojure CLI or tools.build

That keeps the example aligned with the current official Clojure toolchain instead of teaching a legacy bootstrap flow as if it were the only default.

Routes Should Stay Small and Readable

Pedestal routes work best when they identify what happens, not when they hide business logic inside route definitions:

 1(ns myapp.service
 2  (:require [io.pedestal.http :as http]
 3            [io.pedestal.http.route :as route]))
 4
 5(defn health-handler [_request]
 6  {:status 200
 7   :body {:status "ok"}})
 8
 9(def routes
10  (route/expand-routes
11   #{["/health" :get health-handler :route-name :health]}))

This is intentionally simple. A route should point at work, not become the work itself.

Interceptors Are the Real Differentiator

Pedestal’s strength is not “it can return HTTP responses.” Every web stack can do that. The strength is the interceptor model.

An interceptor can participate in:

  • request validation
  • authn/authz
  • correlation IDs
  • structured logging
  • metrics and tracing
  • response shaping
  • uniform error handling
 1(ns myapp.interceptors
 2  (:require [io.pedestal.interceptor :as interceptor]))
 3
 4(def request-id-interceptor
 5  (interceptor/interceptor
 6   {:name ::request-id
 7    :enter (fn [context]
 8             (assoc-in context [:request :request-id]
 9                       (str (java.util.UUID/randomUUID))))
10    :leave (fn [context]
11             (let [request-id (get-in context [:request :request-id])]
12               (assoc-in context [:response :headers "x-request-id"] request-id)))}))

That is the right mental model: interceptors are composable policy and workflow units wrapped around the actual business handler.

Build the Connector Map Explicitly

Recent Pedestal releases prefer connector maps over the older service-map-first startup style. The connector form is explicit without forcing the application through the older io.pedestal.http setup:

 1(ns myapp.server
 2  (:require [io.pedestal.connector :as conn]
 3            [io.pedestal.http.http-kit :as hk]
 4            [myapp.service :as service]))
 5
 6(defn start []
 7  (-> (conn/default-connector-map 8080)
 8      (conn/with-default-interceptors)
 9      (conn/with-routes service/routes)
10      (hk/create-connector nil)
11      conn/start!))

This pattern matters because it makes the pipeline inspectable:

  • start with routes
  • apply the default interceptor stack to the connector map
  • create a specific connector
  • start the service

That is clearer than scattering behavior across middleware wrappers and global setup, and it reflects the direction Pedestal itself now documents. From there, custom interceptors can be layered onto routes or connector setup deliberately instead of becoming hidden global magic.

Know When Pedestal Fits Better Than Simpler Stacks

Pedestal is strongest when:

  • the request pipeline has multiple meaningful stages
  • the team wants explicit control over request/response flow
  • context enrichment and policy composition matter
  • operational behavior should be visible in one chain

Pedestal is a weaker fit when:

  • the service is tiny and mostly passthrough
  • the team wants the lowest possible conceptual overhead
  • the project does not benefit from interceptor-oriented design

The point is not that Pedestal is universally “more enterprise.” The point is that its abstractions pay off only when the service shape needs them.

A Better Way to Compare It

The old comparison is “Pedestal versus Ring versus Compojure.” A more useful comparison is:

  • Pedestal when you want an explicit interceptor pipeline
  • Ring-based stacks when a simpler handler-first composition is enough
  • A smaller routing layer when the service is narrow and the main complexity lives elsewhere

Pedestal is not mainly a routing novelty. It is a request-lifecycle architecture.

The Interceptor Flow

    flowchart LR
	    A["Incoming Request"] --> B["Enter Interceptors"]
	    B --> C["Route Match and Handler"]
	    C --> D["Leave Interceptors"]
	    D --> E["HTTP Response"]

The key thing to notice is that the handler sits in the middle, not at the whole center of the design. Interceptors before and after it shape most of the operational behavior.

Key Takeaways

  • Use Pedestal when request processing is a real pipeline, not just one handler plus a router.
  • Prefer current Clojure CLI and deps.edn examples over legacy lein new scaffolding.
  • Keep routes thin and move reusable request behavior into interceptors.
  • Treat the connector map as a useful explicit contract, not just configuration noise.
  • Choose Pedestal for its interceptor model, not simply because it can serve HTTP.

References and Further Reading

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026