diff --git a/README.md b/README.md index 3092a74..b267c2b 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,9 @@ Builds a todo app one concept at a time. See the [Reference](#reference) for ful * [2. Show Data](#2-show-data) * [3. Accept Input](#3-accept-input) * [4. Nested Data](#4-nested-data) -* [5. Tasks](#5-tasks) -* [6. Modules and Events](#6-modules-and-events) -* [7. Calling APIs](#7-calling-apis) +* [5. Calling APIs](#5-calling-apis) +* [6. Tasks](#6-tasks) +* [7. Modules and Events](#7-modules-and-events) ### 1. Pages and Templates @@ -404,7 +404,65 @@ Register the migration and add a `todo` resource: Both queries in one `sqlite_query()` call run concurrently. `join()` lifts `comments` inside each `todo_data` record, so the template reaches `{{#comments}}` from within `{{#todo_data}}`. `.must_exist = true` returns 404 when the id matches nothing. See [join](#join) and [query](#query). -### 5. Tasks +### 5. Calling APIs + +`fetch()` calls external HTTP services like a query calls a database. JSON parses into context tables; multiple items in one `fetch()` run concurrently. + +Show the responses on the home page: + +**`home.mustache.html`** +```diff + + + +
+- {{$body}} +-

Welcome

+- {{/body}} ++ {{$body}} ++

Welcome

++ {{#weather}} ++

{{city}}: {{precision:temp_c:0}}°C, {{conditions}}

++ {{/weather}} ++ {{#quote}} ++
{{content}}, {{author}}
++ {{/quote}} ++ {{/body}} +
+ + +``` + +Fetch both services concurrently before rendering: + +**`main.c`** +```diff + #include + #include + + mach(main){ + sqlite_database( + .name = "todos_db", + .connect = "file:todos.db?mode=rwc", + .migrations = {"create_todos_table", "create_comments_table"}, + .seeds = {"seed_todos"} + ); + + resource("home", "/", + .get = { ++ fetch( ++ {"https://api.quotes.dev/random", "quote"}, ++ {"https://api.weather.dev/now", "weather"} ++ ), + mustache("home", "home_s"), + respond("home_s") + } + ); +``` + +Both requests run concurrently under one `fetch()` call. The JSON parses into context tables the template walks with `{{#quote}}` and `{{#weather}}`. `fetch()` also supports other verbs, headers, request bodies, and interpolated URLs. See [fetch](#fetch). + +### 6. Tasks A task is a named, reusable pipeline. Define it once with optional `.cron`; dispatch durable background runs with `dispatch_task("name")` (from `dispatch.h`). @@ -455,6 +513,10 @@ Register the migration, define the tasks, dispatch them from the POST: + resource("home", "/", .get = { + fetch( + {"https://api.quotes.dev/random", "quote"}, + {"https://api.weather.dev/now", "weather"} + ), mustache("home", "home_s"), respond("home_s") } @@ -494,7 +556,7 @@ Register the migration, define the tasks, dispatch them from the POST: `.cron` and `dispatch_task(...)` both run the task on a task reactor, off the request reactors, so the POST returns immediately. Dispatched tasks are durable: a crash mid-task resumes on the next boot. To hand values to a task, list them under `.accepts`; `notify_new_todo` pulls in `title` that way. `dispatch_task()` comes from `dispatch.h`. See [Task Pipelines](#task-pipelines). -### 6. Modules and Events +### 7. Modules and Events Split features into modules that talk through pub/sub events. A module is a folder with a matching `.c` file (`todos/todos.c`) declaring `mach(todos){ ... }`, which registers the module's resources, databases, tasks, and subscribers. Assets in the folder belong to the module. `main.c` composes modules by `#include`ing each `.c` file; the include is all that is needed. @@ -554,6 +616,10 @@ This step moves todos into its own module and adds an `activity` module that rec - resource("home", "/", .get = { + fetch( + {"https://api.quotes.dev/random", "quote"}, + {"https://api.weather.dev/now", "weather"} + ), mustache("home", "home_s"), respond("home_s") } @@ -602,6 +668,12 @@ Add an Activity link to the shared nav:
{{$body}}

Welcome

+ {{#weather}} +

{{city}}: {{precision:temp_c:0}}°C, {{conditions}}

+ {{/weather}} + {{#quote}} +
{{content}}, {{author}}
+ {{/quote}} {{/body}}
@@ -739,65 +811,6 @@ mach(activity){ When the POST calls `emit("todo_created")`, MACH propagates the keys named in `publish(...).with` (`title`) to every subscriber. The `activity` module writes its row with no direct link to the publisher. Events are durable: undelivered ones replay after a crash. Adding a third subscriber is a new file with its own `subscribe(...)`; the publisher does not change. See [Modules and Composition](#modules-and-composition) and [Event Pipelines](#event-pipelines). -### 7. Calling APIs - -`fetch()` calls external HTTP services like a query calls a database. JSON parses into context tables; multiple items in one `fetch()` run concurrently. - -Show the responses on the home page: - -**`home.mustache.html`** -```diff - - - -
-- {{$body}} --

Welcome

-- {{/body}} -+ {{$body}} -+

Welcome

-+ {{#weather}} -+

{{city}}: {{precision:temp_c:0}}°C, {{conditions}}

-+ {{/weather}} -+ {{#quote}} -+
{{content}}, {{author}}
-+ {{/quote}} -+ {{/body}} -
- - -``` - -Fetch both services concurrently before rendering: - -**`main.c`** -```diff - #include - #include "todos/todos.c" - #include "activity/activity.c" - - mach(main){ -- resource("home", "/", -- .get = { -- mustache("home", "home_s"), -- respond("home_s") -- } -- ); -+ resource("home", "/", -+ .get = { -+ fetch( -+ {"https://api.quotes.dev/random", "quote"}, -+ {"https://api.weather.dev/now", "weather"} -+ ), -+ mustache("home", "home_s"), -+ respond("home_s") -+ } -+ ); - } -``` - -Both requests run concurrently under one `fetch()` call. The JSON parses into context tables the template walks with `{{#quote}}` and `{{#weather}}`. `fetch()` also supports other verbs, headers, request bodies, and interpolated URLs. See [fetch](#fetch). - --- ## Reference