/ examples / crud / README.md
README.md
 1  # CRUD Example (Phase 6)
 2  
 3  This example shows how Phase 6 APIs fit together for an ergonomic app-facing flow:
 4  
 5  - `lightspeed/router` resolves paths to views and typed event decoders.
 6  - `lightspeed/event` enforces event names and decodes typed payloads.
 7  - `lightspeed/form` binds form fields into typed values.
 8  - `lightspeed/component/helpers` builds navigation/subscription/patch commands.
 9  
10  ```gleam
11  import lightspeed/component/helpers
12  import lightspeed/event
13  import lightspeed/form
14  import lightspeed/router
15  
16  type Msg {
17    SaveTodo(id: Int, title: String)
18    DeleteTodo(id: Int)
19  }
20  
21  fn todo_decoder(
22    inbound: event.InboundEvent,
23    params: List(router.RouteParam),
24  ) -> Result(Msg, event.DecodeError) {
25    case inbound.name {
26      "delete" ->
27        case params {
28          [router.RouteParam("id", id_text)] ->
29            case form.int(form.parse_payload("id=" <> id_text), "id") {
30              Ok(id) -> Ok(DeleteTodo(id))
31              Error(error) -> Error(event.InvalidForm(error))
32            }
33          _ -> Error(event.InvalidForm(form.MissingField("id")))
34        }
35  
36      _ ->
37        event.decode_form(inbound, "save", fn(payload) {
38          case params {
39            [router.RouteParam("id", id_text)] ->
40              case form.int(form.parse_payload("id=" <> id_text), "id") {
41                Error(error) -> Error(error)
42                Ok(id) ->
43                  case form.require(payload, "title") {
44                    Error(error) -> Error(error)
45                    Ok(title) -> Ok(SaveTodo(id, title))
46                  }
47              }
48            _ -> Error(form.MissingField("id"))
49          }
50        })
51    }
52  }
53  
54  fn app_router() {
55    router.new("todos_not_found")
56    |> router.add("/todos/:id/edit", "todos_edit", todo_decoder)
57  }
58  
59  fn update(model, msg) {
60    case msg {
61      SaveTodo(_, _) ->
62        #(model, [helpers.navigate("/todos")])
63  
64      DeleteTodo(_) ->
65        #(model, [helpers.navigate("/todos")])
66    }
67  }
68  ```
69  
70  This flow lets application code stay in typed Gleam while runtime internals remain separate.
71