### Creating and Configuring an HTTP Request Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Example of creating a new HTTP GET request, setting host, path, and authorization header using `gleam/http/request` functions. ```gleam import gleam/http/request import gleam/http let req = request.new() |> request.set_method(http.Get) |> request.set_host("api.example.com") |> request.set_path("/users/1") |> request.set_header("authorization", "Bearer token") ``` -------------------------------- ### Gleam: Example Usage of simulate Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Provides an example of simulating a JSON HTTP response in a Lustre test using the `rsvp.simulate` function. It demonstrates creating a response object and applying an `expect_json` handler. ```gleam import rsvp import gleam/http/response import lustre/dev/simulate fn test_json_response() { let simulation = simulate.init(Model(data: Nil)) let resp = response.new(200) |> response.set_header("content-type", "application/json") |> response.set_body("{\"id\": 1, \"title\": \"Test\"}") let handler = rsvp.expect_json(decode_post(), PostFetched) let simulation = rsvp.simulate(simulation, response: resp, handler: handler) // Assertion on resulting state... Nil } ``` -------------------------------- ### Rsvp Error Handling Example Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md Demonstrates how to handle different Rsvp Error variants within a Lustre application's update function. This example shows pattern matching on Ok and various Error cases. ```gleam import rsvp type Msg { ResponseResult(Result(Data, rsvp.Error(String))) } fn update(model, msg) { case msg { ResponseResult(Ok(data)) -> {model, effect.none()} ResponseResult(Error(rsvp.BadBody)) -> {model, effect.none()} ResponseResult(Error(rsvp.BadUrl(url))) -> {model, effect.none()} ResponseResult(Error(rsvp.HttpError(response))) -> case response.status { 404 -> handle_not_found() 500 -> handle_server_error() _ -> handle_other_error(response.status) } ResponseResult(Error(rsvp.JsonError(decode_error))) -> {model, effect.none()} ResponseResult(Error(rsvp.NetworkError)) -> {model, effect.none()} ResponseResult(Error(rsvp.UnhandledResponse(response))) -> {model, effect.none()} } } ``` -------------------------------- ### Gleam: Example Usage of parse_relative_uri Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Demonstrates how to use `rsvp.parse_relative_uri` to resolve a relative URI. The example shows handling both successful resolution (`Ok(uri)`) and the error case (`Error(Nil)`) which indicates a server-side context. ```gleam import rsvp fn resolve_api_endpoint() { case rsvp.parse_relative_uri("/api/data") { Ok(uri) -> make_request(uri) Error(Nil) -> handle_server_side_context() } } ``` -------------------------------- ### Rsvp Handler Construction Examples Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md Illustrates how to construct Rsvp handlers using different helper functions like expect_json and expect_any_response. These handlers define how to process different types of HTTP responses. ```gleam import rsvp import gleam/dynamic/decode type User { User(id: Int, name: String) } type Msg { UserLoaded(Result(User, rsvp.Error(String))) UserFailed(Result(response.Response(String), rsvp.Error(String))) } fn decode_user() { use id <- decode.field("id", decode.int) use name <- decode.field("name", decode.string) decode.success(User(id, name)) } // JSON handler — expects 2xx + application/json fn user_handler() { rsvp.expect_json(decode_user(), UserLoaded) } // Custom handler — accepts any status fn flexible_handler() { rsvp.expect_any_response(UserFailed) } fn load_user(user_id: Int) { let url = "https://api.example.com/users/" <> int.to_string(user_id) rsvp.get(url, user_handler()) } ``` -------------------------------- ### Basic Request Pattern with rsvp Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Demonstrates the three-step process for making an rsvp request: defining a message type, creating a handler, and returning an effect. This example shows how to fetch data and handle success or error states. ```gleam import rsvp import gleam/dynamic/decode type Model { Model(status: String, data: Result(Data, String)) } type Msg { LoadData DataLoaded(Result(Data, rsvp.Error(String))) } fn decode_data() { use id <- decode.field("id", decode.int) use name <- decode.field("name", decode.string) decode.success(Data(id, name)) } fn init(_) { #(Model(status: "Ready", data: Error("Not loaded")), effect.none()) } fn update(model, msg) { case msg { LoadData -> let handler = rsvp.expect_json(decode_data(), DataLoaded) { {model with status: "Loading"}, rsvp.get("https://api.example.com/data", handler), } DataLoaded(Ok(data)) -> { {model with status: "Loaded", data: Ok(data)}, effect.none(), } DataLoaded(Error(err)) -> { {model with status: "Error", data: Error(error_to_string(err))}, effect.none(), } } } ``` -------------------------------- ### Gleam: Example Usage of expect_any_response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Demonstrates how to use `expect_any_response` to handle different HTTP status codes in a Gleam application. It shows setting up a message type and a handler function. ```gleam import rsvp import gleam/http/response type Msg { ResponseReceived(Result(response.Response(String), rsvp.Error(String))) } fn handle_any_response() { let handler = rsvp.expect_any_response(ResponseReceived) rsvp.get("https://api.example.com/data", handler) } fn update(model, msg) { case msg { ResponseReceived(Ok(response)) -> case response.status { 200 -> handle_success(response) 404 -> handle_not_found() 500 -> handle_server_error() _ -> handle_other(response.status) } ResponseReceived(Error(err)) -> handle_error(err) } } ``` -------------------------------- ### Creating and Configuring an HTTP Response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Example of creating a new HTTP response with status code 200, setting content-type header and a JSON body using `gleam/http/response`. ```gleam import gleam/http/response let resp = response.new(200) |> response.set_header("content-type", "application/json") |> response.set_body("{\"id\": 1}") ``` -------------------------------- ### rsvp.get Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Sends a GET request to a URL with a response handler. It returns an effect that dispatches the GET request when called by the Lustre runtime. ```APIDOC ## Function: get ### Description Sends a GET request to a URL with a response handler. It returns an effect that dispatches the GET request when called by the Lustre runtime. ### Signature ```gleam pub fn get(url: String, handler: Handler(String, message)) -> Effect(message) ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Body Parameters None ### Parameters Table | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | url | String | Yes | — | HTTP URL (absolute or relative). Relative URLs are resolved against the current page URL in browser contexts. Relative URLs fail on the Erlang target. | | handler | Handler(String, message) | Yes | — | Handler that processes the response and converts it to a message. | ### Return `Effect(message)` — An effect that sends the GET request when dispatched by the Lustre runtime. ### Errors Network errors, malformed URLs, and response handling errors are passed to the handler as `Error` variants: - `BadUrl(String)` — The URL is malformed and cannot be parsed. - `NetworkError` — Connection failed or network error occurred. - Other errors from the handler's processing logic. ### Example ```gleam import rsvp import gleam/dynamic/decode type Msg { GotData(Result(String, rsvp.Error(String))) } fn fetch_data() { let handler = rsvp.expect_text(GotData) rsvp.get("https://example.com/data", handler) } ``` ``` -------------------------------- ### Gleam JSON Decoder Composition Examples Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md Illustrates how to compose JSON decoders in Gleam for parsing JSON into Gleam values. This includes decoding fields, primitive types, lists, and optional values. ```gleam decode.field(name: String, decoder: Decoder(a)) decode.string decode.int decode.bool decode.float decode.list(decoder: Decoder(a)) decode.optional(decoder: Decoder(a)) decode.success(value: a) ``` -------------------------------- ### Add RSVP to Gleam Project Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/README.md Install the RSVP library version 2 into your Gleam project using the gleam add command. ```sh gleam add rsvp@2 ``` -------------------------------- ### Making a GET Request Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/module-overview.md Use this function to retrieve data from a URL without side effects. The URL must be an absolute URI. ```gleam rsvp.get("https://api.example.com/data", handler) ``` -------------------------------- ### Gleam JSON Construction Examples Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md Demonstrates how to construct JSON values in Gleam using various types like strings, integers, booleans, objects, arrays, and null. These functions are used for encoding JSON data. ```gleam json.string(String) json.int(Int) json.bool(Bool) json.object(List(#(String, Json))) json.array(List(Json)) json.null ``` -------------------------------- ### Send GET Request Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Sends a GET request to a URL. Use this for retrieving data. The handler processes the response and converts it to a message. Relative URLs are resolved against the current page URL in browser contexts but fail on the Erlang target. ```gleam import rsvp import gleam/dynamic/decode type Msg { GotData(Result(String, rsvp.Error(String))) } fn fetch_data() { let handler = rsvp.expect_text(GotData) rsvp.get("https://example.com/data", handler) } ``` -------------------------------- ### HTTP Methods Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/MANIFEST.txt RSVP supports standard HTTP methods for making requests. These include GET, POST, PUT, PATCH, and DELETE. ```APIDOC ## HTTP Methods ### Description These functions correspond to standard HTTP methods for making requests to a server. ### Methods - `get(uri, options)` - `post(uri, options)` - `put(uri, options)` - `patch(uri, options)` - `delete(uri, options)` ### Parameters - **uri** (string | Uri) - The URL or Uri to send the request to. - **options** (Options) - Optional configuration for the request, such as headers, body, and handlers. ### Response - Returns a `Result(Response(body), Error)` where `body` is typically JSON or text, depending on the handler used. ### Error Handling - Errors can include network failures, HTTP errors (4xx/5xx), malformed URLs, or issues with request/response bodies. ``` -------------------------------- ### Fetch User with Authorization Header Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Constructs a GET request to fetch user data, including an 'authorization' header with a Bearer token. This is used for authenticated API access. ```gleam import gleam/http import gleam/http/request type Msg { UserFetched(Result(User, rsvp.Error(String))) } fn fetch_user_with_token(token: String) { let req = request.new() |> request.set_method(http.Get) |> request.set_host("api.example.com") |> request.set_path("/user") |> request.set_header("authorization", "Bearer " <> token) let handler = rsvp.expect_json(decode_user(), UserFetched) rsvp.send(req, handler) } ``` -------------------------------- ### Decoding JSON with gleam/dynamic/decode Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Example of defining a decoder for a User type, extracting fields from JSON. Used with `expect_json` to decode response bodies. ```gleam import gleam/dynamic/decode fn decode_user() { use id <- decode.field("id", decode.int) use name <- decode.field("name", decode.string) decode.success(User(id, name)) } ``` -------------------------------- ### Simple GET Request with JSON Decoding Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/README.md Fetches user data from a specified URL and decodes the JSON response into a User type. Use this for APIs that return JSON and require specific data structures. ```gleam import rsvp import gleam/dynamic/decode type User { User(id: Int, name: String) } type Msg { UserLoaded(Result(User, rsvp.Error(String))) } fn decode_user() { use id <- decode.field("id", decode.int) use name <- decode.field("name", decode.string) decode.success(User(id, name)) } fn fetch_user(user_id: Int) { let url = "https://api.example.com/users/" <> int.to_string(user_id) let handler = rsvp.expect_json(decode_user(), UserLoaded) rsvp.get(url, handler) } ``` -------------------------------- ### Simulating Application State with lustre/dev/simulate Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Demonstrates how to test Lustre application logic using the `lustre/dev/simulate` module. This allows for simulating state changes and message dispatches without running the full application, facilitating unit testing. ```gleam import lustre/dev/simulate import rsvp fn test_response_handling() { let sim = simulate.init(Model(data: Nil)) let response = response.new(200) |> response.set_header("content-type", "application/json") |> response.set_body("{\"id\": 1}") let handler = rsvp.expect_json(decode_item(), ItemFetched) let sim = rsvp.simulate(sim, response:, handler:) let sim = simulate.update(sim, ItemFetched) // Assert on simulate.state(sim) } ``` -------------------------------- ### Typical Application Imports Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Import necessary modules when using Rsvp in a Lustre application. ```gleam import rsvp import gleam/dynamic/decode import gleam/json import gleam/http/response import lustre import lustre/effect ``` -------------------------------- ### Add Lustre Dependency Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Command to add Lustre to your project if it's not already included, as Rsvp depends on it. ```bash gleam add lustre ``` -------------------------------- ### Create a Logging Handler Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Wraps an existing handler to log the response before dispatching. This allows for observing request outcomes without altering the core handling logic. ```gleam import gleam/result // Create a handler that logs the response before dispatching fn with_logging( original_handler: rsvp.Handler(body, msg), log_fn: fn(Result(response.Response(body), rsvp.Error(body))) -> Nil, ) -> rsvp.Handler(body, msg) { rsvp.expect_any_response(fn(result) { log_fn(result) original_handler.run(result) }) } ``` -------------------------------- ### Creating a Custom Effect with lustre/effect Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Illustrates how to define and use custom side effects in Lustre applications using the `lustre/effect` module. This is typically done within the `init` and `update` functions to manage asynchronous operations or external interactions. ```gleam import lustre/effect fn init() { #(model, effect.none()) } fn update(model, msg) { case msg { FetchData -> #(model, fetch_effect()) } } fn fetch_effect() { rsvp.get(url, handler) } ``` -------------------------------- ### Testing Utilities Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/MANIFEST.txt The `simulate` function allows for mocking HTTP requests and responses, facilitating unit and integration testing. ```APIDOC ## Testing Utilities ### Description The `simulate` function is used for testing purposes, enabling the mocking of HTTP requests and responses. ### Method - `simulate(request, response)` ### Parameters - **request** (SimulationRequest) - Defines the expected incoming request. - **response** (SimulationResponse) - Defines the response to be returned by the mock. ### Usage This function is used within test cases to control the behavior of the RSVP client without making actual network calls. ``` -------------------------------- ### Simulate HTTP Error Handling Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Tests how HTTP errors, such as a 404 Not Found, are handled. This snippet demonstrates setting up a simulation for an HTTP error response. ```gleam import gleeunit/should import lustre/dev/simulate import rsvp import gleam/http/response fn test_http_error_handling() { let simulation = simulate.init(Model(data: Error("Not loaded"))) let response = response.new(404) let handler = rsvp.expect_ok_response(fn(result) { case result { Ok(resp) -> NotFoundError Error(rsvp.HttpError(resp)) -> HttpErrorReceived(resp.status) Error(err) -> OtherError } }) let simulation = rsvp.simulate(simulation, response:, handler:) let simulation = simulate.update(simulation, HttpErrorReceived(404)) // Assertion on resulting state... Nil } ``` -------------------------------- ### Setting HTTP Method with gleam/http Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Demonstrates how to set the HTTP method for a request using the `http.Method` enum. ```gleam import gleam/http request |> request.set_method(http.Post) |> request.set_method(http.Get) ``` -------------------------------- ### Accepting Any 2xx Response with expect_ok_response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Use expect_ok_response when the full response object (status, headers, body) is needed and only 2xx success is relevant. This handler accepts any content-type for 2xx responses. ```gleam type Msg { RequestSucceeded(Result(response.Response(String), rsvp.Error(String))) } fn create_resource(body: json.Json) { let handler = rsvp.expect_ok_response(RequestSucceeded) rsvp.post("https://api.example.com/resources", body, handler) } fn update(model, msg) { case msg { RequestSucceeded(Ok(response)) -> // Access response.status (should be 201), response.headers, response.body {model with created: response.status == 201} RequestSucceeded(Error(rsvp.HttpError(response))) -> // Server returned 4xx or 5xx handle_error_response(model, response) } } ``` -------------------------------- ### Erlang Target Implementation Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/module-overview.md This snippet shows the imports used for the Erlang target implementation of the Rsvp library. It utilizes gleam/httpc for making HTTP requests and gleam/erlang/process for process management. ```gleam @target(erlang) import gleam/erlang/process @target(erlang) import gleam/httpc ``` -------------------------------- ### Testing and Utility Functions Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/INDEX.md Includes utilities for testing HTTP interactions and parsing URLs, aiding in development and debugging. ```APIDOC ## Testing and Utility Functions ### Description Provides utilities for simulating HTTP requests for testing purposes and for parsing URL strings. ### Functions - **`simulate`** - **Purpose:** Simulates HTTP requests for testing. - **Usage:** Allows developers to test their application's interaction with the HTTP library without making actual network calls. - **`parse_relative_uri`** - **Purpose:** Parses a relative URI string. - **Usage:** Utility function for correctly handling and resolving relative URLs. ``` -------------------------------- ### Utilities Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/README.md Utility functions for testing and URL resolution. ```APIDOC ## Utilities - `simulate(simulation, response, handler)` — Test utilities - `parse_relative_uri(uri_string)` — Relative URI resolution ``` -------------------------------- ### JavaScript Target Implementation Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/module-overview.md This snippet shows the imports used for the JavaScript target implementation of the Rsvp library. It relies on the fetch API via gleam/fetch and handles asynchronous operations using gleam/javascript/promise. ```gleam @target(javascript) import gleam/fetch @target(javascript) import gleam/javascript/promise ``` -------------------------------- ### Dependency Tree Visualization Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Visual representation of Rsvp's dependency tree, showing direct and transitive dependencies. ```text rsvp/ ├── gleam_erlang │ └── gleam_stdlib ├── gleam_fetch │ ├── gleam_http │ │ └── gleam_stdlib │ ├── gleam_javascript │ │ └── gleam_stdlib │ └── gleam_stdlib ├── gleam_http │ └── gleam_stdlib ├── gleam_httpc │ ├── gleam_erlang │ │ └── gleam_stdlib │ ├── gleam_http │ │ └── gleam_stdlib │ └── gleam_stdlib ├── gleam_javascript │ └── gleam_stdlib ├── gleam_json │ └── gleam_stdlib ├── gleam_stdlib └── lustre ├── gleam_erlang │ └── gleam_stdlib ├── gleam_json │ └── gleam_stdlib ├── gleam_otp │ ├── gleam_erlang │ │ └── gleam_stdlib │ └── gleam_stdlib ├── gleam_stdlib └── houdini ``` -------------------------------- ### Fetch and Manage Todo List Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Loads a list of todos from an API, allows adding new todos, toggling their completion status, and deleting them. Handles loading states and API response errors. ```gleam import rsvp import gleam/dynamic/decode import gleam/json import gleam/list import gleam/http/response type Todo { Todo(id: Int, title: String, completed: Bool) } type Model { Model(todos: List(Todo), new_title: String, is_loading: Bool) } type Msg { LoadTodos TodosLoaded(Result(List(Todo), rsvp.Error(String))) TitleChanged(String) AddTodo TodoAdded(Result(Todo, rsvp.Error(String))) ToggleTodo(Int) TodoToggled(Int, Result(Todo, rsvp.Error(String))) DeleteTodo(Int) TodoDeleted(Int, Result(response.Response(String), rsvp.Error(String))) } fn decode_todo() { use id <- decode.field("id", decode.int) use title <- decode.field("title", decode.string) use completed <- decode.field("completed", decode.bool) decode.success(Todo(id, title, completed)) } fn init(_) { #(Model(todos: [], new_title: "", is_loading: True), load_todos()) } fn load_todos() { let handler = rsvp.expect_json(decode.list(decode_todo()), TodosLoaded) rsvp.get("https://api.example.com/todos", handler) } fn update(model, msg) { case msg { LoadTodos -> { {model with is_loading: True}, load_todos(), } TodosLoaded(Ok(todos)) -> { {model with todos:, is_loading: False}, effect.none(), } TodosLoaded(Error(_)) -> { {model with is_loading: False}, effect.none(), } TitleChanged(title) -> { {model with new_title: title}, effect.none(), } AddTodo if model.new_title != "" -> let body = json.object([#("title", json.string(model.new_title))]) let handler = rsvp.expect_json(decode_todo(), TodoAdded) { {model with new_title: ""}, rsvp.post("https://api.example.com/todos", body, handler), } TodoAdded(Ok(todo)) -> { {model with todos: list.append(model.todos, [todo])}, effect.none(), } TodoAdded(Error(_)) -> {model, effect.none()} ToggleTodo(id) -> let todo = list.find(model.todos, fn(t) { t.id == id }) case todo { Ok(Todo(_, title, completed)) -> let body = json.object([#("completed", json.bool(!completed))]) let handler = rsvp.expect_json(decode_todo(), TodoToggled(id, _)) { model, rsvp.put("https://api.example.com/todos/" <> int.to_string(id), body, handler), } Error(_) -> {model, effect.none()} } TodoToggled(id, Ok(updated_todo)) -> { { model with todos: list.map(model.todos, fn(t) { case t.id == id { True -> updated_todo False -> t } }), }, effect.none(), } TodoToggled(_, Error(_)) -> {model, effect.none()} DeleteTodo(id) -> let handler = rsvp.expect_ok_response(TodoDeleted(id, _)) { model, rsvp.delete( "https://api.example.com/todos/" <> int.to_string(id), json.object([]), handler, ), } TodoDeleted(id, Ok(_)) -> { {model with todos: list.filter(model.todos, fn(t) { t.id != id })}, effect.none(), } TodoDeleted(_, Error(_)) -> {model, effect.none()} _ -> {model, effect.none()} } } ``` -------------------------------- ### Parsing a URI String with gleam/uri Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Shows how to parse a URI string into a structured `Uri` type using the `gleam/uri` module. This is essential for working with URLs, extracting components like scheme, host, and path, and handling potential parsing errors. ```gleam import gleam/uri case uri.parse("https://api.example.com/users?id=1#section") { Ok(uri) -> // Use uri.scheme, uri.host, uri.path, etc. Error(_) -> // Handle parse error } ``` -------------------------------- ### Sending Binary Data with RSVP Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/module-overview.md Shows how to send a request with a binary body using `send_bits`. The response body is also received as a BitArray. ```gleam let req = request.new() |> request.set_method(http.Post) |> request.set_body(bit_string) rsvp.send_bits(req, handler) ``` -------------------------------- ### Main Module Imports in rsvp.gleam Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Imports standard library utilities, Lustre integration, and target-specific modules for Erlang and JavaScript. ```gleam // Standard library utilities import gleam / dynamic / decode import gleam / http import gleam / http / request.{type Request} import gleam / json.{type Json} import gleam / result import gleam / uri.{type Uri} // Lustre integration import lustre / dev / simulate.{type Simulation} as lustre_simulate import lustre / effect.{type Effect} // Erlang target (conditional) @target(erlang) import gleam / erlang / process @target(erlang) import gleam / http / response.{type Response} @target(erlang) import gleam / httpc // JavaScript target (conditional) @target(javascript) import gleam / fetch @target(javascript) import gleam / http / response.{type Response} @target(javascript) import gleam / javascript / promise ``` -------------------------------- ### Handler Type Parameter Relationships Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md Explains the type parameter relationships for the `Handler` type. ```APIDOC ## Handler Type Parameter Relationships In `Handler(body, message)`: - **body** typically matches the request body type passed to `send` or `send_bits`. For convenience functions like `get`, `post`, `put`, etc., the body is always `String`. - **message** is your application's message type. Each handler transforms an HTTP result into a message that your `update` function can handle. ``` -------------------------------- ### Optimistic UI Updates with Error Handling Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/errors.md Implement optimistic UI updates by first showing a loading state, then handling successful responses or various error types like NetworkError or generic errors by updating the UI accordingly. ```gleam fn update(model, msg) { case msg { ButtonClicked -> let updated_model = {model with loading: True} {updated_model, make_request()} RequestCompleted(Ok(data)) -> {model with data:, loading: False} RequestCompleted(Error(rsvp.NetworkError)) -> // Retry or show offline message {model with loading: False, error: "Offline"} RequestCompleted(Error(err)) -> {model with loading: False, error: error_to_string(err)} } } ``` -------------------------------- ### Setting Custom Content-Type Headers Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/module-overview.md Illustrates how to manually set a custom 'content-type' header for a request using the `send` function. ```gleam let req = request.new() |> request.set_header("content-type", "application/x-www-form-urlencoded") rsvp.send(req, handler) ``` -------------------------------- ### Encoding JSON Data with gleam/json Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/imports-and-dependencies.md Demonstrates how to encode various data types into a JSON object using the `gleam/json` module. This is useful for preparing data to be sent in API requests or stored in a JSON format. ```gleam import gleam/json let body = json.object([ #("name", json.string("Alice")), #("age", json.int(30)), #("active", json.bool(True)), ]) ``` -------------------------------- ### simulate Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Simulates an HTTP response within a Lustre test context. This function allows you to test handlers by providing a mock response and observing the dispatched message. ```APIDOC ## Function: simulate Simulates a response in a Lustre test context, running the handler and dispatching the resulting message. ```gleam pub fn simulate( simulation: Simulation(model, message), response response: Response(body), handler handler: Handler(body, message), ) -> Simulation(model, message) ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **simulation** (Simulation(model, message)) - Required - The active Lustre test simulation. - **response** (Response(body)) - Required - The HTTP response to simulate. Create with `response.new()` and configure the status, headers, and body. - **handler** (Handler(body, message)) - Required - The handler that processes the response. **Return:** `Simulation(model, message)` — The simulation with the handler's message dispatched. **Example:** ```gleam import rsvp import gleam/http/response import lustre/dev/simulate fn test_json_response() { let simulation = simulate.init(Model(data: Nil)) let resp = response.new(200) |> response.set_header("content-type", "application/json") |> response.set_body("{\"id\": 1, \"title\": \"Test\"}") let handler = rsvp.expect_json(decode_post(), PostFetched) let simulation = rsvp.simulate(simulation, response: resp, handler: handler) // Assertion on resulting state... Nil } ``` ``` -------------------------------- ### Gleam: Simulate HTTP Response in Lustre Test Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Simulates an HTTP response within a Lustre test context. This function runs a given handler with a specified response and updates the simulation state. ```gleam pub fn simulate( simulation: Simulation(model, message), response response: Response(body), handler handler: Handler(body, message), ) -> Simulation(model, message) ``` -------------------------------- ### Fetch and Decode JSON with rsvp.get Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/recipes/get-json.md Use `rsvp.get` to fetch JSON from a URL and `rsvp.expect_json` to decode it into a specific type. The handler function processes the decoded result. ```gleam import gleam/dynamic/decode import gleam/int import rsvp type Post { Post(user_id: Int, id: Int, title: String, completed: Bool) } type Msg { ApiReturnedPost(Result(Post, rsvp.Error(String))) } fn get_todo(id) { let url = "https://jsonplaceholder.typicode.com/todos/" <> int.to_string(id) let handler = rsvp.expect_json(decode_todo(), ApiReturnedPost) rsvp.get(url, handler) } fn decode_todo() { use user_id <- decode.field("userId", decode.int) use id <- decode.field("id", decode.int) use title <- decode.field("title", decode.string) use completed <- decode.field("completed", decode.bool) decode.success(Post(user_id:, id:, title:, completed:)) } ``` -------------------------------- ### Graceful Degradation with Fallback Data Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Fetch fresh data while keeping stale cached data available as a fallback. This pattern ensures the UI remains responsive even during network issues or other data loading errors. ```gleam type Model { Model( data: Data, is_stale: Bool, ) } fn update(model, msg) { case msg { RefreshData -> // Attempt to fetch fresh data but use cached data as fallback let handler = rsvp.expect_json(decode_data(), DataLoaded) {model, rsvp.get(url, handler)} DataLoaded(Ok(data)) -> {{model with data:, is_stale: False}, effect.none()} DataLoaded(Error(rsvp.NetworkError)) -> // Network unavailable; keep showing cached data {{model with is_stale: True}, show_offline_message()} DataLoaded(Error(err)) -> // Other error; keep cached data but show error message {{model with is_stale: True}, show_error_message(err)} } } ``` -------------------------------- ### Send Custom HTTP Request Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Use this function for fine-grained control over HTTP requests, including method, headers, and body. Configure the request object before passing it to this function. ```gleam import gleam/http import gleam/http/request import rsvp type Msg { UserFetched(Result(User, rsvp.Error(String))) } fn get_user_with_auth(token: String) { let req = request.new() |> request.set_method(http.Get) |> request.set_host("api.example.com") |> request.set_path("/user") |> request.set_header("authorization", "Bearer " <> token) let handler = rsvp.expect_json(decode_user(), UserFetched) rsvp.send(req, handler) } ``` -------------------------------- ### Simulate Successful Data Load Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Simulates a successful data load scenario for testing purposes. Ensures the data is loaded correctly after a simulated response. ```gleam import gleeunit/should import lustre/dev/simulate import rsvp import gleam/http/response fn test_successful_data_load() { let simulation = simulate.init(Model(data: Error("Not loaded"))) let response = response.new(200) |> response.set_header("content-type", "application/json") |> response.set_body("{\"id\": 1, \"name\": \"Test\"}") let handler = rsvp.expect_json(decode_data(), DataLoaded) let simulation = rsvp.simulate(simulation, response:, handler:) let simulation = simulate.update(simulation, DataLoaded) simulate.state(simulation).data |> should.be_ok() } ``` -------------------------------- ### send Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Sends a fully-configured HTTP request with a response handler, allowing fine-grained control over request properties. ```APIDOC ## Function: send Sends a fully-configured HTTP request with a response handler. Use this for fine-grained control over request properties like headers, method, and body. ```gleam pub fn send( request: Request(String), handler: Handler(String, message), ) -> Effect(message) ``` ### Parameters #### Path Parameters *None* #### Query Parameters *None* #### Request Body *None* ### Request Example ```gleam import gleam/http import gleam/http/request import rsvp type Msg { UserFetched(Result(User, rsvp.Error(String))) } fn get_user_with_auth(token: String) { let req = request.new() |> request.set_method(http.Get) |> request.set_host("api.example.com") |> request.set_path("/user") |> request.set_header("authorization", "Bearer " <> token) let handler = rsvp.expect_json(decode_user(), UserFetched) rsvp.send(req, handler) } ``` ### Response #### Success Response *None* #### Response Example *None* **Return:** `Effect(message)` — An effect that sends the request when dispatched by the Lustre runtime. ``` -------------------------------- ### expect_ok_response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Creates a handler that checks for a 2xx status code and returns the full response object. This is a general-purpose handler for when you need access to the entire response. ```APIDOC ## Function: expect_ok_response ### Description Creates a handler that checks for a 2xx status code and returns the full response object. ### Signature ```gleam pub fn expect_ok_response( handler: fn(Result(Response(body), Error(body))) -> message, ) -> Handler(body, message) ``` ### Parameters #### Parameters - **handler** (fn(Result(Response(body), Error(body))) -> message) - Required - A function that receives the full response or an error and returns a message. The response body type matches your request body type. ### Return `Handler(body, message)` — A handler that validates the status code is in the 2xx range. ### Error handling - `HttpError(Response(body))` — Status code is 4xx or 5xx. - `UnhandledResponse(Response(body))` — Status code is not 2xx but not 4xx/5xx (e.g., 3xx redirects). ### Example ```gleam import rsvp import gleam/http/response type Msg { RequestSucceeded(Result(response.Response(String), rsvp.Error(String))) } fn create_resource() { let handler = rsvp.expect_ok_response(RequestSucceeded) let body = json.object([#("name", json.string("Example"))]) rsvp.post("https://api.example.com/resources", body, handler) } ``` ``` -------------------------------- ### Gleam: Expect Any HTTP Response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/api-reference.md Creates a handler that accepts any HTTP response, regardless of status code. Use for custom status code handling. This handler still returns errors for network failures or response parsing errors. ```gleam pub fn expect_any_response( handler: fn(Result(Response(body), Error(body))) -> message, ) -> Handler(body, message) ``` -------------------------------- ### Custom and Binary Request Functions Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/INDEX.md Advanced functions for making custom HTTP requests and handling binary data, offering flexibility for complex scenarios. ```APIDOC ## Custom and Binary Request Functions ### Description Offers advanced capabilities for making arbitrary HTTP requests and handling binary data, providing flexibility for specialized use cases. ### Functions - **`send`** - **Purpose:** Sends a custom HTTP request. - **Usage:** Use for requests that do not fit the predefined convenience functions, allowing full control over the request details. - **`send_bits`** - **Purpose:** Handles requests with binary data for both request and response. - **Usage:** Ideal for non-textual data, such as file uploads or binary API responses. ``` -------------------------------- ### Simulation Type Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/types.md A testing context that simulates application state and message dispatch. It is part of the `lustre/dev/simulate` module. ```APIDOC ## Simulation Type ### Description A testing context that simulates application state and message dispatch. ### Usage Used by: The `simulate` function operates within a simulation to inject HTTP responses and test handlers. ``` -------------------------------- ### Create a Retry Handler Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Wraps a base handler to retry requests on specific network errors. It distinguishes between successful responses, retriable errors, and other errors. ```gleam // Create a handler that retries on specific errors fn with_retry( base_handler: fn(Result(a, rsvp.Error(String))) -> msg, should_retry: fn(rsvp.Error(String)) -> Bool, ) -> rsvp.Handler(String, msg) { rsvp.expect_any_response(fn(result) { case result { Ok(response) if response.status >= 200 && response.status < 300 -> // Assume successful 2xx response; try to decode base_handler(Ok(response.body)) Error(err) if should_retry(err) -> // Network error or timeout; signal retry RetryMessage Error(err) -> // Other error; pass through base_handler(Error(err)) Ok(response) -> // Non-2xx response; signal error base_handler(Error(rsvp.HttpError(response))) } }) } ``` -------------------------------- ### Custom Response Handling with expect_any_response Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/errors.md Use `expect_any_response` to manually parse responses, allowing for custom logic to handle specific content types or status codes, such as checking for JSON after a redirect. ```gleam import rsvp import gleam/http/response type Msg { DataFetched(Result(Data, rsvp.Error(String))) } fn fetch_with_fallback() { let handler = rsvp.expect_any_response(fn(result) { case result { Ok(response) if response.status >= 200 && response.status < 300 -> // Check content-type manually and decode case response.get_header(response, "content-type") { Ok("application/json") -> case json.parse(response.body, decode_data()) { Ok(data) -> DataFetched(Ok(data)) Err(err) -> DataFetched(Error(rsvp.JsonError(err))) } _ -> DataFetched(Error(rsvp.UnhandledResponse(response))) } Ok(response) -> DataFetched(Error(rsvp.HttpError(response))) Error(err) -> DataFetched(Error(err)) } }) rsvp.get(url, handler) } ``` -------------------------------- ### Cursor-Based Pagination Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Implement cursor-based pagination to fetch data in pages. Uses a cursor to request the next set of items, suitable for APIs that do not support offset-based pagination. ```gleam type Model { Model(items: List(Item), next_cursor: Option(String), is_loading: Bool) } type Msg { LoadMore ItemsLoaded(Result(ItemPage, rsvp.Error(String))) } type ItemPage { ItemPage(items: List(Item), next_cursor: Option(String)) } fn decode_item_page() { use items <- decode.field("items", decode.list(decode_item())) use next_cursor <- decode.optional(decode.field("next_cursor", decode.string)) decode.success(ItemPage(items, next_cursor)) } fn update(model, msg) { case msg { LoadMore -> let cursor_param = case model.next_cursor { Some(cursor) -> "?cursor=" <> cursor None -> "" } let url = "https://api.example.com/items" <> cursor_param let handler = rsvp.expect_json(decode_item_page(), ItemsLoaded) { {model with is_loading: True}, rsvp.get(url, handler), } ItemsLoaded(Ok(ItemPage(items, next_cursor))) -> { { model with items: list.append(model.items, items), next_cursor:, is_loading: False, }, effect.none(), } ItemsLoaded(Error(_)) -> { {model with is_loading: False}, effect.none(), } } } ``` -------------------------------- ### Handle NetworkError in Gleam Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/errors.md Demonstrates how to catch and handle a NetworkError in Gleam, showing UI updates and retry mechanisms. This is useful for providing user feedback and implementing retry logic for transient network issues. ```gleam import rsvp import gleam/http/response type Msg { DataFetched(Result(Data, rsvp.Error(String))) RetryFetch } fn update(model, msg) { case msg { DataFetched(Error(rsvp.NetworkError)) -> // Assume temporary network failure; show UI hint and allow retry { {model with error: "Network error. Check your connection.", show_offline_notification(), } RetryFetch -> // User clicked retry button; attempt request again {model, fetch_data()} } } // Implement retry logic with exponential backoff fn fetch_with_retry(url: String, max_attempts: Int) { fn do_retry(attempt: Int) { let handler = rsvp.expect_json(decode_data(), fn(result) { case result { Ok(data) -> DataReceived(Ok(data)) Error(rsvp.NetworkError) if attempt < max_attempts -> // Retry after a delay proportional to attempt number let delay_ms = int.min(1000 * int.power(2, attempt), 30000) RetryFetch(delay_ms) Error(err) -> DataReceived(Error(err)) } }) rsvp.get(url, handler) } do_retry(1) } ``` -------------------------------- ### Retry with Exponential Backoff Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/usage-patterns.md Implement retry logic for network requests that fail with specific error types. Uses exponential backoff to increase delay between retries, capped at 30 seconds. Retries are limited to 5 attempts. ```gleam type Model { Model( data: Result(Data, String), retry_count: Int, retry_timer: Option(ProcessRef), ) } type Msg { LoadData DataLoadAttempt(Int) DataLoaded(Result(Data, rsvp.Error(String))) } fn should_retry(err: rsvp.Error(String)) -> Bool { case err { rsvp.NetworkError -> True rsvp.HttpError(response) if response.status >= 500 -> True _ -> False } } fn calculate_backoff(attempt: Int) -> Int { // Exponential backoff: 1s, 2s, 4s, 8s, 16s (capped at 30s) let base_delay = 1000 // 1 second in milliseconds let max_delay = 30000 // 30 seconds let delay = base_delay * int.power(2, attempt - 1) int.min(delay, max_delay) } fn update(model, msg) { case msg { LoadData -> let handler = rsvp.expect_json(decode_data(), DataLoaded) { {model with retry_count: 0}, rsvp.get("https://api.example.com/data", handler), } DataLoaded(Ok(data)) -> { {model with data: Ok(data), retry_count: 0}, effect.none(), } DataLoaded(Error(err)) if should_retry(err) && model.retry_count < 5 -> let attempt = model.retry_count + 1 let delay = calculate_backoff(attempt) { {model with retry_count: attempt}, schedule_retry(delay, DataLoadAttempt(attempt)), } DataLoaded(Error(err)) -> { {model with data: Error("Failed to load data after retries")}, effect.none(), } DataLoadAttempt(attempt) -> let handler = rsvp.expect_json(decode_data(), DataLoaded) {model, rsvp.get("https://api.example.com/data", handler)} } } ``` -------------------------------- ### POST Request with JSON Body Source: https://github.com/hayleigh-dot-dev/rsvp/blob/main/_autodocs/README.md Creates a new user by sending JSON data in the request body. Suitable for APIs that accept JSON payloads for resource creation. ```gleam import rsvp import gleam/json type Msg { UserCreated(Result(User, rsvp.Error(String))) } fn create_user(name: String, email: String) { let body = json.object([ #("name", json.string(name)), #("email", json.string(email)), ]) let handler = rsvp.expect_json(decode_user(), UserCreated) rsvp.post("https://api.example.com/users", body, handler) } ```