Daniel Bark

← Back to blog

Published 2024-10-07 by Daniel Bark

0
0

First time trying out Gleam

Installation

Following the official docs I got started with: brew install gleam

Then setting up the project gleam new http_proxy_gleam

We get some code in src/http_proxy_gleam.gleam

import gleam/io

pub fn main() {
  io.println("Hello from http_proxy_gleam!")
}

Then first test run gleam run // Hello from http_proxy_gleam!

I like to make sure I have a working state before i mess everything up.

Now lets add some packages

gleam add \
  wisp@1.1 \
  mist@2.0 \
  gleam_http@3.6 \
  gleam_erlang@0.25

And get some Hello world from the http server:

pub fn main() {
  let assert Ok(_) =
    wisp_mist.handler(
      fn(_) {
        wisp.html_response(
          string_builder.from_string(
            "<body style=\"color:pink;\">Hello, World!</body>",
          ),
          200,
        )
      },
      "secret_key",
    )
    |> mist.new
    |> mist.port(8000)
    |> mist.start_http

  process.sleep_forever()
}

Not that strange to me. The secret key is used sign cookies and other sensitive data and should of course come from an env var in real life.

Now let break out the inline req handler function to:

pub type Context {
  Context
}

pub fn handle_request(_: Request, _: Context) -> Response {
  wisp.html_response(
    string_builder.from_string(
      "<body style=\"color:pink;\">Hello, World!</body>",
    ),
    200,
  )
}

Wisp support passing along a context with request to keep a Db connection or other data you might need. Lets see if we can do something basic like sending back the req headers.

pub fn handle_request(req: Request, _: Context) -> Response {
  let headers = req.headers
  let header_list =
    headers
    |> iterator.from_list
    |> iterator.map(fn(header) {
      let #(key, value) = header
      "<div>" <> key <> ":" <> value <> "</div>"
    })
    |> iterator.to_list

  let headers_string = string.concat(header_list)

  wisp.html_response(
    string_builder.from_string("<div>Request Headers:</div>" <> headers_string),
    200,
  )
}

Here we get to try out some iterator pipeline operations that Gleam brags about :)

Now lets try fetching data from somewhere and returning it. First we install an http client: gleam add gleam_httpc@3

pub fn handle_request(_: Request, _: Context) -> Response {
  let data = result.unwrap(fetch_some_data(), "Error")
  wisp.html_response(string_builder.from_string(data), 200)
}

pub fn fetch_some_data() {
  // Prepare a HTTP request record
  let assert Ok(base_req) =
    request.to("https://test-api.service.hmrc.gov.uk/hello/world")

  let req =
    request.prepend_header(base_req, "accept", "application/vnd.hmrc.1.0+json")

  // Send the HTTP request to the server
  use resp <- result.try(httpc.send(req))

  // We get a response record back
  resp.status
  |> should.equal(200)

  resp
  |> response.get_header("content-type")
  |> should.equal(Ok("application/json"))

  resp.body
  |> should.equal("{\"message\":\"Hello World\"}")

  Ok(resp.body)
}

And we do get a {"message":"Hello World"} back from test-api.service.hmrc.gov.uk and then back from out server.

The first hour of Gleam was not that bad. The iterator patterns remind me of Rust. The STD lib reminds me of Go since methods dont live on the types.

Thanks for reading :)

Written by Daniel Bark

← Back to blog