Srini
Srini

Reputation: 883

clojure-api: Single main clj for multiple API handlers

The below is my app project.clj

(defproject clojure-my-app-api "0.1.0-SNAPSHOT"
      :description "FIXME: write description"
      :url "http://example.com/FIXME"
      :license {:name "Eclipse Public License"
                :url "http://www.eclipse.org/legal/epl-v10.html"}
      :ring {:handler clojure-my-app-api.core/app} 
      :dependencies [[org.clojure/clojure "1.8.0"]
                     [metosin/compojure-api "1.1.10"]
                     [ring/ring-core "1.4.0"]
                     [ring/ring-jetty-adapter "1.4.0"]]
      :main clojure-my-app-api.core)

and my app core.clj is

(ns clojure-my-app-api.core
  (:require [ring.adapter.jetty :as jetty])
  (:require [compojure.api.sweet :refer :all])
  (:require [ring.util.http-response :refer :all]))

(defapi app
  (GET "/hello" []
    :query-params [name :- String]
    (ok {:message (str "Dear " name ", Hello I am here ")})))

(jetty/run-jetty app {:port 3000})

My doubt is, Is it mandatory to put (jetty/run-jetty app {:port 3000}) in every clj class if we have multiple classes for handling multiple API requests.

Can you please help me out is there any single main class mechanism for multiple clj class to handle different API path.

I have modified my code.

project.clj

(defproject clojure-dauble-business-api "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :ring {:handler {clojure-dauble-business-api.core/app 
                  clojure-dauble-business-api.test/test1}}
  :repl-options {:init-ns clojure-dauble-business-api.user}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [metosin/compojure-api "1.1.10"]
                 [ring/ring-core "1.4.0"]
                 [ring/ring-jetty-adapter "1.4.0"]]
  :main clojure-dauble-business-api.user)

user.clj

(ns clojure-dauble-business-api.user
  (:require [ring.adapter.jetty :as jetty])
  (:require [compojure.api.sweet :refer :all])
  (:require [ring.util.http-response :refer :all])
  (:require [clojure-dauble-business-api.core :as core])
  (:require [clojure-dauble-business-api.test :as test]))

(jetty/run-jetty (list core/app test/test) {:port 3000})

core.clj

(ns clojure-dauble-business-api.core
  (:require [ring.adapter.jetty :as jetty])
  (:require [compojure.api.sweet :refer :all])
  (:require [ring.util.http-response :refer :all]))

(defapi app
  (GET "/hello" []
    :query-params [name :- String]
    (ok {:message (str "Dear " name ", Hello I am here ")})))

test.clj

(ns clojure-dauble-business-api.test
  (:require [ring.adapter.jetty :as jetty])
  (:require [compojure.api.sweet :refer :all])
  (:require [ring.util.http-response :refer :all]))

(defapi test
  (GET "/ping" []
    :query-params [name :- String]
    (ok {:message (str "Dear " name ", Hello I am here ")})))

Error while running http://localhost:3000/hello?name=acbd endpoint in Postman

2017-07-08 10:46:34.413:WARN:oejs.HttpChannel:qtp191004666-15: /hello?name=abcd
java.lang.ClassCastException: clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
    at ring.adapter.jetty$proxy_handler$fn__1401.invoke(jetty.clj:24)
    at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
    at org.eclipse.jetty.server.Server.handle(Server.java:497)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:310)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
    at java.lang.Thread.run(Thread.java:745)

Upvotes: 1

Views: 339

Answers (2)

Mike
Mike

Reputation: 3416

You shouldn't have (jetty/run-jetty (list core/app test/test) {:port 3000}) in any of your namespaces. (Having it in user.clj is fine for REPL work).

Typically, you have a single handler route that you pass to ring for handing your application.

For example, I generally have a routes namespace that I combine by routes from other namespaces.

So, from your example.

(ns clojure-dauble-business-api.routes
    :require [compojure.core :refer :all]
             (clojure-dauble-business-api [core :as core]
                                          [test :as t]))
(def app
 (routes core/app t/test))

This is what you could pass into Jetty, or refer to in your :ring entry in your project.clj.

:ring {:handler clojure-dauble-business-api.routes}

You're getting the error, because run-jetty must be passed a function, but you're passing it a list instead (because you're trying to consolidate your routes with (list core/app test/test)

I would also add the plugin lein-ring to your project and remove the :main entry. You will also have to correct your :ring entry in your project as above.

Upvotes: 2

Payal
Payal

Reputation: 43

I am a newbie too, but hope this helps:

This is how I have implemented. There is one file named user.clj which has
(jetty/run-jetty app {:port 3000})

and other common function that you may specify in main and then in project.clj :repl-options {:init-ns user}

you can also use injections to load namespaces but I have never used them.

Upvotes: 0

Related Questions