API JSON calls with Haskell
Tags: haskell, http, conduit
Published: 2021-05-03 01:46:44

## What ?

A simple project showing how to make calls to an API and parsing JSON using HTTP Conduit and Aeson.

## Why ?

Because JSON is very popular and use it with Haskell is really cool.

## How ?

Here you have the sample code from basic GET calls to sending JSON via POST using httpbin.org as a reference:

```haskell
{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Network.HTTP.Simple

-- SIMPLE GET

simpleGet :: IO ()
simpleGet = do
    response <- httpLbs "http://httpbin.org/get"
    print (getResponseBody response)

-- CUSTOM REQUEST

request = setRequestMethod "GET"
    $ setRequestHost "httpbin.org"
    $ setRequestPath "/get"
    $ defaultRequest

customRequest :: IO ()
customRequest = do
    response <- httpLbs request
    print (getResponseBody response)

-- SIMPLE HTTPS GET

simpleHttpsGet :: IO ()
simpleHttpsGet = do
    response <- httpLbs "https://httpbin.org/get"
    print (getResponseBody response)

-- CUSTOM HTTPS REQUEST

httpsRequest = setRequestMethod "GET"
    $ setRequestHost "httpbin.org"
    $ setRequestPath "/get"
    $ setRequestSecure True
    $ setRequestPort 443
    $ defaultRequest

customHttpsRequest :: IO ()
customHttpsRequest = do
    response <- httpLbs httpsRequest
    print (getResponseBody response)

-- GENERIC JSON GET

genericJsonGet :: IO ()
genericJsonGet = do
    response <- httpJSON "http://httpbin.org/get" :: IO (Response Value)
    print (getResponseBody response)

-- PARSED JSON GET

data CallInfo = CallInfo String String

instance FromJSON CallInfo where
    parseJSON (Object v) = do
        origin <- v .: "origin"
        url <- v .: "url"
        return (CallInfo origin url)

parsedJsonGet :: IO ()
parsedJsonGet = do
    response <- httpJSON "http://httpbin.org/get" :: IO (Response CallInfo)
    case getResponseBody response of
        CallInfo origin url ->
            print ("Requesting " ++ url ++ " from " ++ origin)

-- SIMPLE POST

simplePost :: IO ()
simplePost = do
    response <- httpLbs "POST http://httpbin.org/post"
    print (getResponseBody response)


-- GENERIC JSON POST

genericJsonPost :: IO ()
genericJsonPost = do
    response <- httpJSON "POST http://httpbin.org/post" :: IO (Response Value)
    print (getResponseBody response)

-- PARSED JSON POST

data User = User String String

instance ToJSON User where
    toJSON (User email fullname) = object
        [ "email" .= email
        , "fullname" .= fullname ]

parsedJsonPost :: IO ()
parsedJsonPost = do
    let user = User "test@test.com" "Test User"
    let request = setRequestBodyJSON user "POST http://httpbin.org/post"
    response <- httpJSON request :: IO (Response Value)
    print (getResponseBody response)

-- MAIN

main :: IO ()
main = do
    simpleGet
    customRequest
    simpleHttpsGet
    customHttpsRequest
    genericJsonGet
    parsedJsonGet
    simplePost
    genericJsonPost
    parsedJsonPost
```

## Try it at home

For build and run the code you may clone the repo https://github.com/csaltos/hello-conduit and run these commands:

```bash
cd hello-conduit
cabal build
cabal exec -- hello-conduit
```

You can also use the interpreter with the following commands:

```bash
cabal install --lib aeson
cabal install --lib http-conduit
cd hello-conduit
ghci app/Main.hs
```

And then inside the interpreter you may run:

```ghci
:set -XOverloadedStrings

parsedJsonGet
```

And of course you can call the other samples, makes changes, reload and try and learn new things.

## Who ?

This sample is based on the tutorials at https://github.com/snoyberg/http-client/blob/master/TUTORIAL.md and https://www.fpcomplete.com/haskell/library/aeson/

Some documentation can be found at https://hackage.haskell.org/package/http-conduit-2.3.8/docs/Network-HTTP-Simple.html and https://hackage.haskell.org/package/aeson-0.11.1.0/docs/Data-Aeson.html