Mint 🍃: Handling HTTP Requests
Tags: mintlang, frontend, webdev, javascript
Published: 2019-05-07 15:10:35

_This is the fourth post in a series that showcases the features of Mint, you can find the previous posts here:_
* _[Mint 🍃: Getting Started](/gdotdesign/getting-started-with-mint-3k2o)_
* _[Mint 🍃: Components](/gdotdesign/components-in-mint-4a4n)_
* _[Mint 🍃: Events and State of Components](/gdotdesign/mint-events-and-state-of-components-3j3a)_

In this post I will show you how to make **HTTP** requests to an API :)

## The Code

This is the full source code to fetch planets from the [Star Wars API](https://swapi.co) and display it in a table.

```
record Planet {
  population : String,
  gravity : String,
  climate : String,
  name : String,
}

record Data {
  results : Array(Planet),
  count : Number,
}

enum Status {
  Initial
  Loading
  Error(String)
  Ok(Data)  
}

store StarWars {
  state status : Status = Status::Initial

  fun load : Promise(Never, Void) {
    sequence {
      next { status = Status::Loading }

      response =
        "https://swapi.co/api/planets"
        |> Http.get()
        |> Http.send()

      object = 
        response.body
        |> Json.parse()
        |> Maybe.toResult("")

      decodedResults = 
        decode object as Data

      next { status = Status::Ok(decodedResults) }
    } catch Http.ErrorResponse => error {
      next { status = Status::Error("Something went wrong with the request.") }
    } catch Object.Error => error {
      next { status = Status::Error("The data is not what is expected.") }
    } catch String => error {
      next { status = Status::Error("Invalid JSON data.") }
    }
  }
}

routes {
  * {
    StarWars.load() 
  }
}

component Main {
  connect StarWars exposing { status } 

  fun render : Html {
    case (status) {
      Status::Initial => <div></div>
      Status::Loading => <div>"Loading..."</div>
      Status::Error message => <div><{ message }></div>
      Status::Ok data =>
        <table>
          <tr>
            <th>"Name"</th>
            <th>"Climate"</th>
            <th>"Gravity"</th>
            <th>"Population"</th>
          </tr>
          
          for (planet of data.results) {
            <tr>
              <td><{ planet.name }></td>
              <td><{ planet.climate }></td>
              <td><{ planet.gravity }></td>
              <td><{ planet.population }></td>
            </tr>
          }
      </table>
    }
  }
}
```

I will now explain it to you block by block.

## Modelling the data

In any typed programming language, the structure of data must be defined somehow:

```
record Planet {
  population : String,
  gravity : String,
  climate : String,
  name : String,
}

record Data {
  results : Array(Planet),
  count : Number,
}

enum Status {
  Initial
  Loading
  Error(String)
  Ok(Data)  
}
```

In Mint there are two constructs for defining data: 
* `record` - which defines an object with fixed named key / value pairs
* `enum` - which defines an [ADT](https://en.wikipedia.org/wiki/Algebraic_data_type) - 
  a type which represents a fix set of possibilities

In our example `Planet` and `Data` defines the data that comes from the API and the `Status` defines the possible states of the request.

## Defining the state

In Mint, global state is stored in a **store** (insert [Nicolas Cage meme](https://vignette.wikia.nocookie.net/criminal-case-grimsborough/images/0/05/You_Don%27t_Say_Meme.png) here) which is globally accessible and basically works like a Component where state is concerned. (`state` and `next` keywords [from the last article]((/gdotdesign/mint-events-and-state-of-components-3j3a)))

```
store StarWars {
  state status : Status = Status::Initial

  fun load : Promise(Never, Void) {
    ...
  }
}
```

## Handling the request

The handling of an HTTP request is done in a `sequence` block, which runs each expression in it **asynchronously** in sequence ([Cage again](https://vignette.wikia.nocookie.net/criminal-case-grimsborough/images/0/05/You_Don%27t_Say_Meme.png)) in the order they are written. 

What this means that it will **await all promises** `Promise(error, value)` and unbox the `value` in a variable for subsequent use or raise the `error` which is handled in a `catch` block.

```
sequence {
  next { status = Status::Loading }

  response =
    "https://swapi.co/api/planets"
    |> Http.get()
    |> Http.send()

  object = 
    response.body
    |> Json.parse()
    |> Maybe.toResult("")

  decodedResults = 
    decode object as Data

  next { status = Status::Ok(decodedResults) }
} catch Http.ErrorResponse => error {
  next { status = Status::Error("Something went wrong with the request.") }
} catch Object.Error => error {
  next { status = Status::Error("The data is not what is expected.") }
} catch String => error {
  next { status = Status::Error("Invalid JSON data.") }
}
```

The `Http` module contains functions to make `Http.get(url : String)` and send `Http.send(request : Http.Request)` HTTP requests.

The next part is to parse the `JSON` content into an `Object` and then `decode` it to the type we defined earlier, then we set the `status` to `Status::Ok` or `Status::Error` according to what happened.

## Routing

Mint has a built in system for handling routes which will be featured in a different article. 

In our case we define the `*` route which handles all non-handled routes, in the route we just load the data, which in practice means when the page is loaded:

```
routes {
  * {
    StarWars.load() 
  }
}
```

## Displaying the data

The last part is to display the data which we will do in the `Main` component:

```
component Main {
  connect StarWars exposing { status } 

  fun render : Html {
    case (status) {
      Status::Initial => <div></div>
      Status::Loading => <div>"Loading..."</div>
      Status::Error message => <div><{ message }></div>
      Status::Ok data =>
        <table>
          <tr>
            <th>"Name"</th>
            <th>"Climate"</th>
            <th>"Gravity"</th>
            <th>"Population"</th>
          </tr>
          
          for (planet of data.results) {
            <tr>
              <td><{ planet.name }></td>
              <td><{ planet.climate }></td>
              <td><{ planet.gravity }></td>
              <td><{ planet.population }></td>
            </tr>
          }
      </table>
    }
  }
}
```

To get the data from the store, first we need to connect the component to it using the `connect` keyword and `expose` the `status` state so it can be used in the scope of the component.

_Connecting a component to a store makes it so that the component re-renders when the data in the store changes._

Then based on the `status` we render different content:

* `Status::Initial` - we display nothing
* `Status::Loading` - we display a loading message
* `Status::Error message` - we display the error message
* `Status::Ok data` - we display the data

And there you have it, thank you for reading 🙏:

--------------


If you like to learn more about Mint check out the [guide](https://guide.mint-lang.com) 📖

In the next part I'm going to show how to [style elements with CSS](https://dev.to/gdotdesign/mint-styling-elements-295o) 😉 see you there 👋