ArangoDB and Elm — Fetching data

In the previous part, on Authentication, we managed to get a JWT, which we need to do further requests to the Arango REST API.

Evan, the creator of Elm, wrote a basic tutorial on HTTP with Elm, to get you started. The building blocks for requesting and parsing (decoding) are in the Http package, and the Json.Decode and Json.Encode packages.

The Jwt library that I am using, wraps the Http functions. It exposes the same functions, like get and post. The only difference is, that we can pass in a JWT, and the function will make sure it's added to the http request header.

Fetching data from a REST API in Elm follows a common pattern that you have to get used to. To me, it was quite confusing in the beginning, because people are doing it in various ways. Evan's explanation helped to get my first request working, but then, when I wanted to customize things, the whole thing stopped working. It's still a bit mysterious to me how a Decoder co-operates with the request functions. I'll try to explain, and show how I got things working.

There are several ways to get data out of Arango. Simple Query is very convenient, because you just give an example of what kind of documents you want. For my app, I chose the cursor api, because I can use the full AQL, and do graph queries.

From the Arango documentation, I learned that I need to send a POST to the _api/cursor endpoint. From the Jwt documentation, I learned that I can create a request, by calling Jwt.post. Following Evan's tutorial, I should be able to give this request to the Http.post function, together with my own Msg, and the results of the request should arrive in my update function.

On top of that, I need to encode my request, and create a decoder for the JSON that Arango returns. I guess it's all for the sake of reliability. Elm keeps a strict boundary between it's own, controlled environment, and the evil outside (Javascript) world. The encoders and decoders are the border patrol, in this case. Ports are another example of this separation.

Arango wants my query in JSON form, in the request body. The Http library has a function jsonBody for this, so that's what I'll need. The jsonBody function expects an encoded JSON object, with keys "query" and, optionally, "batchSize". Putting it all together, we get something like:

To see what we will receive, we can do the request from curl :

curl -d @- -X POST http://root:<password>@localhost:8529/_db/test/_api/cursor

filling in password, and, after hitting enter, continue with

{"query": "FOR assy IN assemblies RETURN assy"}

and hitting ctrl-D (on Linux; on Windows, you press ctrl-Z).

Arango returns a JSON object, with key "result", containing a list of JSON objects. In most examples on Elm decoders, you'll find that JSON responses are decoded into Elm structs. In my case, I needed something more flexible, because I don't know in advance what keys my assemblies will contain. The Elm Dict package is the solution for this.

In my Model.elm file, I have defined Assembly as a type alias, simply as a Dict String String, meaning that both keys and values will be Strings. (How to handle different types of values, will be explained in a future story in this series.)

To get the response decoded took me, again, a lot of fiddling, studying examples on the internet, and trying to understand the messages of the compiler. Now that it's working, it looks simple:

We descend down the "result" field, expect a list there, which consists of string dictionaries. The pipe operator helps to show the structure, here.

While writing and debugging code, it often helps to put Debug.logcalls here and there. It helped me a lot to find out how data is flowing through my app.

The code to view the data can be found on github. Try it out, and once you have it working, try adding an integer, float or null field to one of your assemblies. You can use the Arango web interface: go to your "assemblies" collection, and just click on one of the items in the list.

Boom! No results on your screen, and this in your browser console:

error: BadPayload "Expecting a String at _.result[0].empty but instead got: null"

You can't just try and put anything in a string field in Elm! How I managed to solve this, is the subject of my next story.

Thanks for reading.