1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|

# 05 HTTP Requests
> **Note**: this guide is written for Lustre v4. The latest stable release of
> Lustre is v3. To follow along with this guide, you need to _manually_ edit your
> `gleam.toml` and change the required version of lustre to `"4.0.0-rc.2"`.
Up until now, we've seen Lustre applications constructed with the `lustre.simple`
constructor. These kinds of applications are great for introducing the Model-View-Update
(MVU) pattern, but for most real-world applications we'll need a way to talk to
the outside world.
Lustre's runtime includes _managed effects_, which allow us to perform side effects
like HTTP requests and communicate the results back to our application's `update`
function. To learn more about Lustre's effect system and why it's useful, check
out the [side effects guide](https://hexdocs.pm/lustre/guide/side-effects.html),
or the docs for the [lustre/effect module](https://hexdocs.pm/lustre/lustre/effect.html)
For now, we will focus on how to send HTTP requests in a Lustre application: a
pretty important thing to know!
## Moving on from `lustre.simple`
From now on, the rest of these examples will use a different application constructor:
[`lustre.application`]. Let's compare the type of both functions:
```gleam
pub fn simple(
init: fn(flags) -> model,
update: fn(model, msg) -> model,
view: fn(model) -> Element(msg),
) -> App(flags, model, msg)
pub fn application(
init: fn(flags) -> #(model, Effect(msg)),
update: fn(model, msg) -> #(model, Effect(msg)),
view: fn(model) -> Element(msg),
) -> App(flags, model, msg)
```
All that's changed is the return type of our `init` and `update` functions. Instead
of returning just a new model, they now return a tuple containing both a model and
any side effects we want the runtime to perform.
You'll notice that running a Lustre app with side effects _changes the signature_
of our [`init`](src/app.gleam#L43) and [`update`](src/app.gleam#L54) functions.
Instead of returning just a model, we return a tuple containing both a model an
an `Effect(Msg)` value. The effect value specifies any further updates we might
want the Lustre runtime to execute before the next invocation of the `view`
function.
> **Note**: notice how the type of `view` remains the same. In Lustre, your `view`
> is always a [_pure function_](https://en.wikipedia.org/wiki/Pure_function) that
> takes a model and returns the UI to be rendered: we never perform side effects
> in the `view` function itself.
## HTTP requests as side effects
The community library [`lustre_http`](https://hexdocs.pm/lustre_http/) gives us
a way to model HTTP requests as Lustre `Effect`s. Crucially, when we call
`lustre_http.get` we are _not_ performing the request! We're constructing a
description of the side effect that we can hand off to the Lustre runtime to
perform.
```gleam
fn get_quote() -> Effect(Msg) {
let url = "https://api.quotable.io/random"
let decoder =
dynamic.decode2(
Quote,
dynamic.field("author", dynamic.string),
dynamic.field("content", dynamic.string),
)
lustre_http.get(url, lustre_http.expect_json(decoder, GotQuote))
}
```
To construct HTTP requests, we need a few different things:
- The `url` to send the request to.
- A description of what we _expect_ the result to be. There are a few options:
`expect_anything`, `expect_text`, `expect_json`. In this example we say we're
expecting a JSON response and provide a decode.
- A long with what we expect the response to be, we also need to provide a way
to turn that response into a `Msg` value that our `update` function can handle.
The same applies for post requests too, but there you also need to provide the
JSON body of the request.
## Getting help
If you're having trouble with Lustre or not sure what the right way to do
something is, the best place to get help is the [Gleam Discord server](https://discord.gg/Fm8Pwmy).
You could also open an issue on the [Lustre GitHub repository](https://github.com/lustre-labs/lustre/issues).
While our docs are still a work in progress, the official [Elm guide](https://guide.elm-lang.org)
is also a great resource for learning about the Model-View-Update architecture
and the kinds of patterns that Lustre is built around.
|