DEV Community

muro
muro

Posted on

Querido Yo del Futuro: Hoy intentaremos configurar una aplicación fullstack en Clojure

Hola, querido Yo del Futuro:
Como recordarás (o quizás no, dada nuestra mala memoria), hoy hemos estado sumergiéndonos en el mundo de Clojure y ClojureScript, intentando construir algunas aplicaciones simples. El camino no ha sido fácil; todavía nos cuesta entender cómo hacer ciertas cosas, pero poco a poco hemos ido progresando.

Pensé que sería bueno dejar unas notas detalladas de cómo hemos ido avanzando. Así, cuando en el futuro nos preguntemos "¿cómo demonios hicimos funcionar esa aplicación en Clojure?", tendremos un registro de cómo nos las ingeniamos para lograrlo.
¡Que lo disfrutes tanto como yo disfruté (y sufrí) creándolo!"

It's Time!

Creando el esqueleto de la aplicacion

Hemos seguido la guia de Shadow CLJS para crear nuestro proyecto:

npx create-cljs-project fullstack

Como ibamos a usar Reagent en nuestro Frontend, entonces tuvimos que instalar react:
npm install react react-dom create-react-class

Estructura Inicial del Proyecto

Cuando creamos nuestro proyecto vimos que se crearon una serie de archivos y folders.

Directorios y archivos creados por shadow-cljs

  • src/: Directorio principal para el código fuente.
  • test/: Directorio para los tests.
  • shadow-cljs.edn: Archivo de configuración de Shadow CLJS.
  • package.json: Archivo de configuración de npm.
  • node_modules/: Directorio de dependencias de Node.js.

Modificación de la estructura.

Todo muy bien hasta aca, pero decidimos modificar un poco la estructura para incluir el backend. Recuerda que no estamos completamente seguros que esta sea la mejor forma de hacerlo. Si cuando leas esto en el futuro conoces una mejor manera, no dudes en usarla!

Nuestra estructura modificada se ve algo así:

Estructura modificada

Los cambios claves:

  1. Hemos creado dentro de src/main directorios separados para backend y frontend. Nota que tambien creamos dentro de cada uno de estos directorios core.clj para el backend y core.cljs para el frontend.
  2. Añadimos un archivo deps.edn para manejar las dependencias de Clojure (backend).
  3. Mantuvimos shadow-cljs.edn para las configuraciones de ClojureScript (frontend).
  4. Removimos el directorio test. Esto es opcional.

Configurando el Frontend en shadow-cljs.edn

Hemos realizado varios ajustes significativos a nuestro archivo shadow-cljs.edn:

;; shadow-cljs configuration
{:source-paths
 ["src/main"]

 :dependencies
 [[reagent "1.2.0"]
  [cider/cider-nrepl "0.24.0"]]
 :dev-http {8080 "public"}
 :builds
 {:frontend
  {:target :browser
   :output-dir "public/js"
   :asset-path "/js"
   :modules {:main {:init-fn frontend.core/init}}}}}
Enter fullscreen mode Exit fullscreen mode

En primer lugar, modificamos la ruta de origen a ["src/main"] ya que nuestro codigo fuente estará en este directorio.

También hemos agregado Reagent como dependencia. Esta librería nos permitirá construir UI reactivas en ClojureScript aprovechando React.

Como estamos utilizando Vim y Conjure como nuestro IDE, hemos agregado cider/cider-nrepl, esto mejora la integración con el REPL en Conjure (según la documentación, aun tenemos que ver si es cierto).

En cuanto al servidor de desarrollo, configuramos :dev-http para servir desde el directorio "public" en el puerto 8080.

Por último, definimos un build específico llamado :frontend. El target es :browser ya que estamos haciendo el proyecto para la web. El directorio de salida es "public/js" con la ruta de assets en "/js". Como punto final, especificamos el entry point de nuestra aplicación, que será con la función init en core.cljs. Para esto decimos que el módulo principal se inicializará con la función frontend.core/init, la cual aún tenemos pendiente crear.

Configurando el Backend en deps.edn

Ahora toca configurar el backend, recuerdas que habíamos creado deps.edn? Bueno, ahora es momento de usarlo:

{:paths ["src/main"]
 :deps 
 {
  org.clojure/clojure {:mvn/version "1.11.1"}
  ring/ring-core {:mvn/version "1.9.6"}
  ring/ring-jetty-adapter {:mvn/version "1.9.6"}
  }
 :aliases
{:run-server {:main-opts ["-m" "backend.core"]}}
 }
Enter fullscreen mode Exit fullscreen mode

Al igual que hicimos en el frontend, comenzaremos con definir la ruta de origen de nuestro código, que en nuestro caso también es ["src/main"].

En cuanto a dependencias necesitamos org.clojure/clojure y para el servidor web hemos elegido Ring. Específicamente estamos utilizando ring/ring-core y ring/ring-jetty-adapter.

Hemos también creado un alias llamado :run-server que está configurado para ejecutar la función main en el namespace backend.core cuando se invoque.

Vamos a probar si todo funciona bien

Primero vamos por el backend. En src\main\backend\ hemos creado core.clj

(ns backend.core
  (:require [ring.adapter.jetty :as jetty]
            [ring.middleware.params :as params]))


(defn handler [request]
  {:status 200
   :headers {"Content-Type" "text/plain"}
   :body "Hola desde el backend de Clojure."})

(def app 
  (-> handler 
      (params/wrap-params)))

(defn -main [& args]
  (println "Iniciando el servidor en http://localhost:3000")
  (jetty/run-jetty app {:port 3000 :join? false}))

Enter fullscreen mode Exit fullscreen mode

Aquí, luego de importar las dependencias de Ring, creamos una función handler que por ahora simplemente responde con un mensaje de texto.
También hemos definido nuestra app que usa wrap-params para manejar parámetros de la solicitud.
Finalmente tenemos nuestra función de entrada -main que inicia el servidor en el puerto 3000.

Para probar el backend simplemente abremos un terminal en el root de nuestro proyecto:


clj -M:run-server

Si vamos a http://localhost:3000/ deberiamos ver el mensaje "Hola desde el backend de Clojure."

Ahora probemos si el frontend functiona correctamente. Vamos a crear en src/main/frontend/ el archivo core.cljs con el siguiente contenido:

(ns frontend.core
  (:require
    [goog.dom :as gdom]
    [reagent.core :as reagent :refer [atom]]
    [reagent.dom :as rdom]
    ))

(defn app []
  [:div 
   [:h1 "Welcome to my Clojurescript App"]
   [:p "This is a basic Reagent component"]])

(defn mount-app-element []
  (rdom/render [app] (gdom/getElement "app")))

(mount-app-element)

(defn ^:after-load on-reload []
  (mount-app-element))
Enter fullscreen mode Exit fullscreen mode

Aquí, primero importamos las dependencias necesarias como goog.dom para manipular el DOM y los componentes core y dom de Reagent.
Tambien hemos definido la función app que es nuestro componente principal de Reagent. Por ahora es un simple HTML con un título y un párrafo.
Luego tenemos mount-app-element que es la función que renderizará nuestro componente app en el elemento del DOM con id app.
Finalmente creamos una función on-reload que está diseñada para trabajar con hot reloading así no tenemos que recargar la página para que se actualize el navegador.

Para arrancar el frontend abrimos un nuevo terminal, vamos al root del proyecto y ejecutamos:

shadow-cljs watch frontend

Si todo sale bien deberiamos ver una página que dice "Welcome to my ClojureScript App" en localhost:8080.

Hasta aquí tenemos todo funcionando bien. Aún tenemos que configurar las bases de datos y mucho más, nos queda mucho que aprender. Espero que para cuando leas esto ya tengamos una mejor idea de como hacerlo.

Saludos cordiales,
Tu Yo del Pasado.

Top comments (2)

Collapse
 
fred_functional profile image
Fred Functional

¿Podrías explicar un poco más por qué decidiste eliminar el directorio test? ¿Alguna razón técnica o solo preferencia?

Collapse
 
mrmuro profile image
muro

ninguna razon en particular. Solo queria tener 2 directorios, uno para frontend y otro para backend.