DEV Community

Alex Esoposting
Alex Esoposting

Posted on

Factor app - http requests

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*
;
Enter fullscreen mode Exit fullscreen mode

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
;
Enter fullscreen mode Exit fullscreen mode

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
;
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (0)