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.
Target API
Where we get the data from
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.
Our toolbox for today
Factor http.client and json.reader vocabularies
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: >string json>
.
Putting knowledge to use
Actual code time!
The word 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
;
Final vocabulary
Just code
I put this code in the nptimelapse.api
vocabulary generated with scaffold-work
.
! 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
;
Conclusion
A short chapter, wasn't it?
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.
Top comments (0)