Search code examples
clojureswaggerleiningenringcompojure-api

Compojure API running ring server raises map exception


I'm following this tutorial on how to use the compojure api and have run into a dead end with this exception:

lein ring server
2022-08-09 23:19:55.538:INFO::main: Logging initialized @921ms
WARN clojure.tools.logging not found on classpath, compojure.api logging to console.
Exception in thread "main" java.lang.RuntimeException: Map literal must contain an even number of forms, compiling:(ring_test/core.clj:16:16)

I have kept everything the same as the tutorial except changed my routes to a test route:

(ns ring-test.core
  (:require [compojure.api.sweet :refer :all]
            [ring.util.http-response :refer :all]))

(def app
  (api
   {:swagger
    {:ui "/"
     :spec "swagger.json"
     :data {:info {:tile "Test"}
            :tags [{:name "api"}]}}
    (context "/api" []
      :tags ["api"]
      (GET "/test" []
        :body ["test"]
        (ok)))}))

and updated some of the versions in project.clj:

(defproject ring-test "0.1.0-SNAPSHOT" 
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [metosin/compojure-api "2.0.0-alpha28"]]
  :ring {:handler ring-test.core/app}
  :profiles {:dev
             {:plugins [[lein-ring "0.12.5"]]
              :dependencies [[javax.servlet/servlet-api "2.5"]]}})

If anyone could help me with this issue that would be awesome!


Solution

  • In the tutorial, (context ...) is outside the map.

    (def app
      (api {:swagger
            {:ui   "/"
             :spec "/swagger.json"
             :data {:info {:title "Account Service"}
                    :tags [{:name "api"}]}}} ;; END-OF-MAP HERE
    
           ;; This from is _NOT_ in the map
           (context "/api" []
                    :tags ["api"]
                    (POST "/account" []
                          :body [account (describe NewAccount "new account")]
                          (ok))
                    (POST "/transfer" []
                          :body [transfer (describe NewTransfer "new transfer")]
                          (ok)))))
    

    The problem is in your version, you move (context ...) inside this map.

    (def app
      (api {:swagger {:ui "/"
                      :spec "swagger.json"
                      :data {:info {:tile "Test"}
                             :tags [{:name "api"}]}}
    
            ;; This from is in the map
            (context "/api" []
                     :tags ["api"]
                     (GET "/test" []
                          :body ["test"]
                          (ok)))
            } ;; END-OF-MAP HERE
           ))
    

    You can fix this by moving (context ...) to outside the map as shown in the tutorial.