From ff365d98125205a367b939b7a266351bdd404f1c Mon Sep 17 00:00:00 2001
From: Nick Ricketts
Date: Mon, 15 Jun 2026 10:50:06 -0500
Subject: [PATCH] MaCH repo
---
README.md | 141 +++++++++++++++++++++++++++++-------------------------
1 file changed, 77 insertions(+), 64 deletions(-)
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}}
+-
++ {{/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}}