Configuring MongoDB in a Clojure Luminus web framework

798 views Asked by At

I am having issues configuring a MongoDB database inside a Luminus project. This should be pretty straightforward given the lein templates: https://github.com/yogthos/luminus-template. Typing lein new luminus <name> +mongodb will give you a default mongoDB setup which is the file: src/app-name/db/core.clj To run the server, type lein ring server which should open a web browser and point it to localhost:3000 by default.

A default home page will be displayed, and for me, it tells me that "MongoDB configuration is Required." It tells me that I can configure it in the same file: src/app-name/db/core.clj. I have tried a number of different things, but what I am currently trying and what makes the most sense to me is the following:

(defonce coll "collection-name")
(defonce db (let [uri "mongodb://127.0.0.1/db-name"
                  {:keys [conn db]} (mg/connect-via-uri uri)]
              db))

Unfortunately, when I connect my browser, I still get the same "MongoDB configuration is Required" message. I have also tried using CURL and various HTTP routes defined in my application that access the database without success. What is odd though, is that this works in the REPL.

EDIT: To be more clear, here is the example in the REPL:

clj-project-name.db.core> (get-replies 2)
["mew-mew" [1.0 "hello"]]

In the code I have the following pieces:

 (ns clj-project-name.routes.home
  (:require [compojure.core :refer :all]
            [clj-project-name.layout :as layout]
            [clj-project-name.util :as util]
            [clj-project-name.db.core :as project-db]))

(defn get-replies [id] (mc/distinct db coll "replies" {:_id id}))
(GET "/user" [id] (user-page id))  ; defined in home-routes inside namespace clj-project-name.routes.home 
(defn user-page [& [id]]           ;defined inside namespace clj-project-name.routes.home
  (layout/render "user.html"
                 {:id id
                  :replies (projectl-db/get-replies id)}))

<h1>User {{id}}'s page</h1>           ; part of the HTML template
<p> <b>Replies:</b> {{replies}} </p>  

Here is the page loaded in the browser:

page showing that the call to mongodb does not work

As we can see, the replies list is empty, when it should be ["mew-mew" [1.0 "hello"]] as we saw in the REPL.

EDIT: Another oddity is that just when the browser is loaded after typing lein ring server I can see the following output from mongodb in the terminal:

2014-12-02T21:16:57.941-0500 [initandlisten] connection accepted from 127.0.0.1:38854 #28 (5 connections now open).

What else can I do to get connected to MongoDB? Thanks for your help.

3

There are 3 answers

0
Leonid Beschastny On BEST ANSWER

I followed your steps and created new Luminus project using luminus template.

I also studied generated code an found, that default home page is 100% static. So, it shows MongoDB Configuration is Required regardless of whether it actually configured or not:

(defn home-page []
  (layout/render
    "home.html" {:content (util/md->html "/md/docs.md")}))

In other words, it just renders resources/public/md/docs.md into .html and shows it, always the same html page.

As for your configuration, it's absolutely fine.

As for your user.html page, the actual problem is that id in user-page route is a string, while _id in your database is a number. So, instead of (get-replies 2) you're calling (get-replies "2"). Try using stringified _ids, or parse incoming ids with read-string function or Long/parseLong first:

(defn user-page [& [id]]
  (layout/render "user.html"
                 {:id id
                  :replies (-> id
                               Long/parseLong ; throws NumberFormatException
                               project-db/get-replies)}))

I would recommend using stringified _ids, because it's easier and safer than parsing strings into numbers.

1
myguidingstar On

Maybe because you use defonce instead of def, the those vars (coll and db in namespace app-name.db.core) haven't been reloaded. Try stopping the lein process and start again with:

lein do clean, ring server

When you're done with this, consider taking a further step with component workflow:

https://github.com/stuartsierra/component

0
Dave Liepmann On

There are too many moving parts in your application, and you've provided us access to too few of those moving parts, for us to make any clear determination of what's causing you trouble. All we can do is give general debugging advice.

  1. Make sure you have a clean environment. Shut down your REPL, your web server, maybe even your editor. Restart everything in order, and remember that order.
  2. I see a bunch of weirdnesses in the code in your post, but I can't tell if they're from your codebase or a product of you copying code to the question. What's projectl-db? Is get-replies in the same namespace as user-page or not? Make sure you're not referring to an old definition of get-replies in your repl.
  3. In what order are things defined in the db namespace? Maybe coll, db, or get-replies are in the wrong order and failing quietly.
  4. You say (get-replies) works in the repl. What does user-page say in the REPL?
  5. You say you're seeing a "MongoDB configuration is Required" message, but I don't see it. Is that from the ring server, displayed in the browser, or in the browser console?
  6. What happens when you switch the current definition for the value of replies to an integer value? E.g. inside user-page, return a map of {:id id, :replies 5} instead of the call to get-replies. The answer to this will help narrow down where the problem is coming from.
  7. Can you take user-page out of the equation and return a string from your get-routes? Once you have that, what happens if you return get-replies (again, without user-page)?

The principle here, as with all debugging, is to reduce the problem to the fewest number of pieces that reproduces the problem. Right now you have a whole lot of pieces that could be the issue, and you're only showing us glimpses of them. Help yourself by removing elements until you have the shortest possible sequence of calls that causes the problem. When asking for help or reporting the bug, show people that minimal example, so that they don't have to learn your entire codebase. More importantly, a copied version of the code (that you produce for people to help debug) might not even have the bug!

You can also debug in the other direction, by starting with a fresh working repository and adding elements from your broken repo one-by-one until the bug arises. This can be helpful when learning the ins-and-outs of a new framework like Luminus.