The app I described in the previous tutorial is supposed to fetch data about a game from some server. Before writing any code to process this data it would be nice to know what kind of data we're dealing with and how to find it.
The server we're trying to talk to is a simple HTTPS server with an impossible to remember address of
a766-95-160-157-211.ngrok.io. Most of its endpoints provide nice HTML pages, very useful for the browser but entirely useless for us. What we need can be found under
/api/game/<game-nr> which is supposed to provide us with all data relevant to the given game number in JSON format.
To summarise: we need to make an HTTP GET request to the given address and receive a JSON in return.
Communicating over HTTP can be a huge pain because of the amount of things you need to worry about like headers or where to put data into the request. Fortunately Factor will do most of that for us so that we only need to use exactly one word from each of these libraries.
For GET requests there is a word
http-get, but it's not what we need there as it will raise an error if the request is not successful.
http-get is actually a wrapper over the word
htpp-get* which just performs a request and returns whatever the outcome is.
To decode JSON into meaningful Factor objects there is a word
json> which converts a JSON string to a Factor object. If the request is successful then
http-get* leaves returned data as a byte array. We then just need to convert it to a string to be able to extract Factor objects out of it. Our json extracting pipeline is two words:
http-get* has a stack effect
( url -- response data ). To modify it to fetch game data we need to provide it with the correct url joined with the game number.
USE: http.client CONSTANT: api-url "https://a766-95-160-157-211.ngrok.io/api/game/" : get-game* ( game-id-string -- response data ) api-url prepend http-get* ;
Now that's not enough. Notice that
get-game* returns two values: response and data. If everything is ok then
code slot of the response will be 200 and data will be a byte array representing a JSON with game data. A lot of things can go wrong though: the client may be offline, the server may be offline, the game id may be incorrect and so on.
For these cases we need a wrapper word for
get-game* that will check for any HTTP errors. That word will then extract any relevant information - either game data or HTTP error code - and return it for further interpretation by the app. HTTP error codes start from 400 and go up so this check is very simple. If it's lower than 400 we can use the words
>string json> to convert response data to string and then to a Factor object. The function should also return a flag indicating whether the request was successful.
USE: json.reader : get-game ( game-id-string -- error/game-object f/t ) get-game* swap code>> dup 400 < [ drop >string json> t ] [ nip f ] if ;
I put this code in the
nptimelapse.api vocabulary generated with
! Copyright (C) 2021 Aleksander Sabak. ! See http://factorcode.org/license.txt for BSD license. USING: kernel strings accessors http.client json.reader ; IN: nptimelapse.api CONSTANT: api-url "https://a766-95-160-157-211.ngrok.io/api/game/" : get-game* ( game-id-string -- response data ) api-url prepend http-get* ; : get-game ( game-id-string -- error/game-object f/t ) get-game* swap code>> dup 400 < [ drop >string json> t ] [ nip f ] if ;
Fetching data from a HTTP server turned out to be quite easy given Factor's high-level vocabularies. I chose the easiest part for the beginning though, so don't expect everything to go as smoothly from now on. We are about to get into some UI stuff, and that's never easy in any language.