servant-elm-example-app
servant-elm-example-app copied to clipboard
An example application using servant-elm.
Servant-Elm Example App
This is an example application making use of the servant-elm library.
To build and run this application, you need to install stack and Elm.
Assuming you have both tools installed, you can run the example with:
make && make serve
App layout
If you study servant-elm-example-app.cabal, you will see that the Haskell part of this project comprises four build targets:
-
an
apilibrary, which will contain our Servant API type and server implementation; -
a
backendexecutable, which serves a home page and our API under/api, as well as our static assets; -
a
code-generatorexecutable, which uses servant-elm to generate Elm code for interacting with our API; and -
a test-suite (not yet implemented).
Our Elm code is in the frontend/src directory.
Build process
The Makefile ties everything together.
First we build the api library and the backend and code-generator
executables by running stack build.
Then we run stack exec code-generator, directing the output Elm code to
frontend/src/Generated/Api.elm.
Finally, we run elm-make to build frontend/dist/app.js.
Components in detail
The API
The definition of our API lives in api/Api/Types.hs. It's pretty simple. You can:
POST /booksGET /books
The first returns a JSON representation of a book, and the second returns a list of such representations.
The implementation lives in api/Api/Server.hs. The book database
state lives in a TVar BookDB, which must be supplied by the user.
The backend
backend/Main.hs defines a type called SiteApi, which wraps
our book API under /api, provides an index route and serves assets under
/assets.
The server function implements this SiteApi. It wraps the API server
implementation, serves the frontend/dist directory as /assets, and serves
a home page.
The home page simply sets a page title and bootstraps our Elm app (which will be
built to frontend/dist/app.js).
The main function creates a new TVar for our book database API and starts the app
on port 8000.
The code generator
code-generator/Main.hs imports our Api type and calls
servant-elm's elmJSWith with some options:
-
moduleNameconfigures the name of the Elm module that will be generated. This must match the filepath that the Elm code will be written to, and any import statements in your own Elm code using the generated code. In our case we have usedGenerated.Api. -
urlPrefixspecifies where the frontend code can connect to the API. In our case it ishttp://localhost:8000/api.
The frontend
In frontend/src/Main.elm we import the Generated.Api
module. Our Elm app uses the Html module (see
The Elm Architecture for
details).
The functions fetchBooks and the use of postBooks in update demonstrate
how the generated Elm functions can be used. The generated functions getBooks
and postBooks have types Http.Request (List (Book)) and Book -> Http.Request (Book) respectively. The Http.Request a type is passed to the
Http.send function to actually perform the requests.
TODO
- Demonstrate API endpoints that take parameters and captures.
- Try to make coupling between components more explicit:
- port and API prefix must match between
backendandcode-generator, - Elm module name must match between
Makefileandcode-generatorandfrontend, - frontend build directory must match between
Makefileandbackend.
- port and API prefix must match between