-
Notifications
You must be signed in to change notification settings - Fork 0
Connecting to the Service
To follow along with this section, start with tag v2.0.13
.
In this section you will connect the client and service projects. This will involve writing a real service implementation in the client.
The following pedestal-app concepts will be introduced in this section:
- Communicating with a service
- Integrating application and service projects
- Development while running against the real service
In the tutorial-client
project, you have already implemented a
service named MockServices
which simulates the real service. Now
that a real back-end service exists, you can write the code which
connects to it.
Add a new ClojureScript source file named
tutorial-client/src/tutorial_client/services.cljs
for the namespace
tutorial-client.services
.
The mock version of the service has two parts: the services object
which implements the Activity
protocol and the function named
services-fn
to handle outgoing messages. The new version will do the
same thing except that it will create an EventSource
for receiving
Server Sent Events and an XMLHttpRequest
for sending outgoing
messages.
The namespace declaration will require the following namespaces
(ns tutorial-client.services
(:require [io.pedestal.app.protocols :as p]
[cljs.reader :as reader]))
The Services
implementation will create an EventSource
at the
endpoint /msgs
and will respond to msg
events by reading the
message string to Clojure data and placing the message on the input
queue.
All input goes through the input queue.
(defn receive-ss-event [app e]
(let [message (reader/read-string (.-data e))]
(p/put-message (:input app) message)))
(defrecord Services [app]
p/Activity
(start [this]
(let [source (js/EventSource. "/msgs")]
(.addEventListener source
"msg"
(fn [e] (receive-ss-event app e))
false)))
(stop [this]))
The services-fn
implementation will send messages as a POST
to /msgs
.
(defn services-fn [message input-queue]
(let [body (pr-str message)]
(let [http (js/XMLHttpRequest.)]
(.open http "POST" "/msgs" true)
(.setRequestHeader http "Content-type" "application/edn")
(.send http body))))
The body of the request is the messages data printed to a string using
(pr-str message)
. The content type is set to application/edn
.
That is all. This will allow the client to send and receive messages.
The starting point for the development and production version of the
application is the main
function located in the
tutorial-client.start
namespace.
To use the new service, require the namespace
[tutorial-client.services :as services]
and then update the main function to create the service, start it and
use the services/services-fn
function to consume the effects
queue.
(defn ^:export main []
(let [app (create-app (rendering/render-config))
services (services/->Services (:app app))]
(app/consume-effects (:app app) services/services-fn)
(p/start services)
app))
Three new lines have been added to the main
function. First create the
service, providing it with the application.
(services/->Services (:app app))
Then arrange for the services-fn
to consume all messages on the
effects queue.
(app/consume-effects (:app app) services/services-fn)
Finally, start the service.
(p/start services)
So now, when you run the development or production aspects of the
application (these two aspects are configured to run the main
function above), it will attempt to connect to the service.
If you were to run this as it is, it would not work. This is because the service that is created will attempt to connect to the server from which the HTML page hosting the JavaScript source was served. While running the client project that server is the application development server, not the service server.
In order for this application to work, it must be served from the service.
All of the compiled output of the application project is compiled to
out/public
. To get the service to serve this content, we simply need
to create a symlink to this directory in the service project.
In the service project, do the following:
mkdir resources
cd resources
ln -s ../../tutorial-client/out/public
The resources directory is already on the classpath. For this to work,
the out/public
directory must exist.
With this in place you can access anything compiled into out/public
from the service server.
Before you try the application, make one configuration change to the development application which will make the workflow a little better.
In the config file tutorial-client/config/config.clj
, under the :application
section, add the following key and value
:api-server {:host "localhost" :port 8080 :log-fn nil}
This tells the development server that there is a service running on localhost port 8080.
For any aspects which you would like to redirect to the service, add the following key and value
:use-api-server? true
Do this for the :development
and :production
aspects.
You are now ready to start everything up and try it out.
If you are running the development server, stop it and start it in the usual way. You need to stop it because of the change which was made to the config file.
To start the server project, enter that directory and run the, now familiar, commands for starting the server.
lein repl
(use 'dev)
(start)
Next, navigate to the development project
http://localhost:3000
and then click on either the Development or Production links
in the Tools Menu, the applications will be compiled and then you
will be redirected to the service running on http://localhost:8080
.
The application should now work. Start up multiple browsers and connect to the same URL to try it out!
You may have noticed that you first went to the development server and clicked on the links to take you over to the service. You did this instead of just going directly to the service. The reason for this is that clicking on the links in the development server will actually cause compilation to happen before redirecting to the service.
If while running from the service, you change the application code and
then refresh the page, you will not see the change. This is because the
service doesn't know anything about ClojureScript or compilation. It
only knows that it is serving static files from the resources/public
directory.
To trigger compilation, you have to go back to the development server and again click on the Development or Production links.
This is fine if you only need to do it once but if you find that you would like to work on the application, making a lot of changes while serving it from the service, then there is a better way.
The watch
function is located in the dev
namespace and can be used
to watch files for changes and trigger compilation. This will trigger
the same compilation process that is triggered when you reload a page
in the development application. So you can change SCSS, HTML templates or
ClojureScript files and compilation will be initiated.
To compile the development aspect when files change run
(watch :development)
from the development project REPL.
To stop watching run
(unwatch)
The argument to watch
is the aspect that you would like to compile. It
is best to use the :development
aspect so that compilation times are
faster but you may use any of the available aspects.
This concludes Part 1 of the tutorial which covers all of the basics of pedestal-app applications. This should give you good grounding in pedestal-app and allow you to get started working on your own projects.
This is not, however, the end of the tutorial. In Part 2, you will get some new requirements which, as you implement them will introduce you to the following important pedestal-app concepts:
- External JavaScript and externs files
- Rendering without the DOM
- Messages with parameters
- Multi-screen applications and Focus
- Continue functions
- Parallel processing with Web Workers
The tag for this section is v2.0.14
.