Skip to content
Charles Huang edited this page Jun 7, 2023 · 12 revisions

All exceptions should be handled gracefully. Compojure-api ships with customizable exception handling with working defaults. Customization is done via api or api-middleware options - which delegate actual processing to compojure.api.middleware/wrap-exceptions doing the actual work. It catches all thrown exceptions and selects a custom handler based on the first match of:

  1. exception ex-data key :type
  2. exception class
  3. exception parent classes

If none of the above matches, :compojure.api.exception/default type is used.

Exception handlers are 3-arity functions, getting the exception, ex-data and request as arguments. Below are the default type definitions and default handling:

type what default
:compojure.api.exception/request-parsing Input data de-serialization errors. 400 + error in body
:compojure.api.exception/request-validation Request Schema coercion errors. 400 + schema error in body
:compojure.api.exception/response-validation Response Schema coercion errors. 500 + schema error in body
:compojure.api.exception/default Everything else. 500 + print stacktrace + safe message

example to override the default case + add a custom exception type + handler for it:

(require '[compojure.api.exception :as ex])
(require '[ring.util.http-response :as response])

(defn custom-handler [f type]
  (fn [^Exception e data request]
    (f {:message (.getMessage e), :type type})))

(api
  {:exceptions 
   {:handlers 
    {;; catches ex-infos with {:type ::calm}
     ::calm (custom-handler response/enhance-your-calm :calm)
     
     ;; catches all SQLExceptions (and it's subclasses)
     java.sql.SQLException (custom-handler response/internal-server-error :sql)

     ;; log all request validation errors
     ::ex/request-validation (ex/with-logging ex/request-validation-handler :info)

     ;; everything else
     ::ex/default (custom-handler response/internal-server-error :unknown)}}}
  ...)

Logging

By default only :compojure.api.exception/default handler logs the messages. Since 0.24.5, to add logging to other default handlers you can use function compojure.api.exception/with-logging:

(api
  {:exceptions 
   {:handlers 
    {::ex/request-parsing (ex/with-logging ex/request-parsing-handler :info)
     ::ex/response-validation (ex/with-logging ex/response-validation-handler :error)}}}
  (POST "/bang" [] (ok)))

with-logging just wraps the given handler function and calls simple logging function before calling the handler. The function takes optional second argument to set the log level for messages, this is useful to mark e.g. request parsing errors caused by bad clients less important. To control what is logged create your own handlers or own logging wrapper.