Factor app - http requests
Tags: esoteric, concatenative, tutorial, factor
Published: 2021-11-08 13:58:32

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.
```Factor
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.
```Factor
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`.
```Factor
! 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.