#include #include #include // ───────────────────────────────────────────── // 15 Minute Blog in MACH // ───────────────────────────────────────────── // Resources: // GET / → redirect to posts // GET /posts → list all posts // POST /posts → create a post // GET /posts/new → new post form // GET /posts/:id → show a post // PATCH /posts/:id → update a post // DEL /posts/:id → delete a post // GET /posts/:id/edit→ edit post form // ───────────────────────────────────────────── config mach() { return (config) { // ── Resources ────────────────────────────── .resources = {{ // Home: just redirect to the posts index {"home", "/", .get = {{ redirect((u){"posts"}) }} }, // Posts index + create {"posts", "/posts", .get = {{ query((d){ .set = "posts", .db = "blog_db", "select id, title, body, created_at " "from posts " "order by created_at desc;" }), render((r){ "{{< layout}}" "{{$body}}" "
" "

" "All Posts" "

" "" "New Post" "" "
" "{{#posts}}" "{{> post_card}}" "{{/posts}}" "{{^posts}}" "
" "

No posts yet.

" "" "Write your first post" "" "
" "{{/posts}}" "{{/body}}" "{{/layout}}" }) }}, .post = {{ validate((v){ .name = "title", .validation = "^.{1,200}$", .message = "Title is required (max 200 characters)" }), validate((v){ .name = "body", .validation = "^[\\s\\S]{1,50000}$", .message = "Body is required" }), query((d){ .db = "blog_db", "insert into posts (title, body) " "values ({{title}}, {{body}});" }), redirect((u){"posts"}) }} }, // New post form {"new_post", "/posts/new", .get = {{ render((r){ "{{< layout}}" "{{$body}}" "

" "New Post" "

" "
" "{{> post_form}}" "
" "" "← Back to posts" "" "{{/body}}" "{{/layout}}" }) }} }, // Single post: show, update, delete {"post", "/posts/:id", .get = {{ find((d){ .set = "post", .db = "blog_db", "select id, title, body, created_at " "from posts " "where id = {{id}};" }), render((r){ "{{< layout}}" "{{$body}}" "{{#post}}" "
" "

" "{{title}}" "

" "

" "Published {{created_at}}" "

" "
" "{{body}}" "
" "
" "
" "" "Edit" "" "
" "" "" "
" "" "← Back" "" "
" "{{/post}}" "{{/body}}" "{{/layout}}" }) }}, .patch = {{ validate((v){ .name = "title", .validation = "^.{1,200}$", .message = "Title is required (max 200 characters)" }), validate((v){ .name = "body", .validation = "^[\\s\\S]{1,50000}$", .message = "Body is required" }), query((d){ .db = "blog_db", "update posts " "set title = {{title}}, body = {{body}} " "where id = {{id}};" }), redirect((u){"post"}) }}, .delete = {{ query((d){ .db = "blog_db", "delete from posts " "where id = {{id}};" }), redirect((u){"posts"}) }}, .before = { validate((v){ .name = "id", .validation = "^\\d{1,10}$", .message = "Invalid post ID" }) } }, // Edit post form {"edit_post", "/posts/:id/edit", .get = {{ find((d){ .set = "post", .db = "blog_db", "select id, title, body " "from posts " "where id = {{id}};" }), render((r){ "{{< layout}}" "{{$body}}" "{{#post}}" "

" "Edit Post" "

" "
" "" "{{> post_form}}" "
" "" "← Back to post" "" "{{/post}}" "{{/body}}" "{{/layout}}" }) }}, .before = { validate((v){ .name = "id", .validation = "^\\d{1,10}$", .message = "Invalid post ID" }) } } }, // ── Shared Layout & Partials ─────────────── .context = { // Layout — the HTML shell every page inherits from {"layout", "" "" "" "" "" "MACH Blog" "{{> tailwind_script}}" "" "" "" "
" "{{$body}}{{/body}}" "
" "
" "Powered by MACH" "
" "" "" }, // Partial: post card for the index listing {"post_card", "" }, // Partial: reusable form fields for new + edit {"post_form", "
" "
" "" "" "
" "
" "" "" "
" "" "
" } }, // ── Error Handling ───────────────────────── .errors = { {http_not_found, { render((r){ "{{< layout}}" "{{$body}}" "
" "

404

" "

Post not found.

" "" "← Back to all posts" "" "
" "{{/body}}" "{{/layout}}" }) }}, {http_bad_request, { render((r){ .status = http_bad_request, "{{< layout}}" "{{$body}}" "
" "

" "Validation Error" "

" "

" "Please check your input and try again." "

" "" "← Back to all posts" "" "
" "{{/body}}" "{{/layout}}" }) }} } }, // ── Database ─────────────────────────────── .databases = {{ .engine = sqlite_db, .name = "blog_db", .connect = "file:blog.db?mode=rwc", .migrations = { "CREATE TABLE posts (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "title TEXT NOT NULL," "body TEXT NOT NULL," "created_at TEXT NOT NULL " "DEFAULT (strftime('%Y-%m-%d %H:%M', 'now'))" ");" }, .seeds = { "INSERT INTO posts (title, body) VALUES" "('Hello World', " " 'Welcome to the MACH blog! This post was created by the " "database seed. MACH is a high-performance, declarative web " "framework for C that lets you build full CRUD apps in a " "single file with zero boilerplate.')," "('Why Declarative C?', " " 'Traditional C web development means manual memory management, " "string wrangling, and security pitfalls at every turn. MACH " "flips the script: you declare resources, pipelines, and " "templates. The runtime handles allocation, prepared statements, " "XSS escaping, and async I/O. You get the speed of C with the " "ergonomics of a modern framework.');" } }}, // ── Modules ──────────────────────────────── .modules = {sqlite, tailwind} }; }