name: blog-posttrigger: - type: http path: /blog/:slug methods: [GET] public: true responses: html: {enabled: true} templateEngine: liquidflow: # Fetch post by slug - agent: fetch-post operation: data config: backend: d1 binding: DB query: | SELECT title, content, published_at, author FROM posts WHERE slug = ? AND published = 1 params: [${input.params.slug}] # Render HTML - operation: html config: template: | <!DOCTYPE html> <html> <head> <title>{{ fetch-post[0].title }}</title> </head> <body> <article> <h1>{{ fetch-post[0].title }}</h1> <p><em>By {{ fetch-post[0].author }} on {{ fetch-post[0].published_at }}</em></p> <div>{{ fetch-post[0].content }}</div> </article> <a href="/">← Back to home</a> </body> </html> data: fetch-post: ${fetch-post}output: format: html rawBody: ${html.output}
Visit: http://localhost:8787/blog/my-first-post★ Insight ─────────────────────────────────────
The :slug in the path becomes available as ${input.params.slug}. This is how you build dynamic routes with path parameters.
─────────────────────────────────────────────────
Handle both displaying a form (GET) and processing submissions (POST) in one ensemble.Create ensembles/pages/contact.yaml:
name: contact-formtrigger: - type: http path: /contact methods: [GET, POST] public: true rateLimit: requests: 3 window: 60 # 3 submissions per minute responses: html: {enabled: true}flow: # Only validate on POST - agent: validate condition: ${metadata.method === 'POST'} operation: code config: handler: | const errors = [] if (!input.email || !input.email.includes('@')) { errors.push('Valid email required') } if (!input.message || input.message.length < 10) { errors.push('Message must be at least 10 characters') } return { valid: errors.length === 0, errors } # Send email if valid - agent: send-email condition: ${validate.valid} operation: email config: provider: sendgrid to: [support@example.com] subject: "Contact form: ${input.name}" body: ${input.message} from: ${input.email} # Render HTML (different for GET vs POST) - operation: html config: template: | <!DOCTYPE html> <html> <head> <title>Contact Us</title> <style> body { font-family: system-ui; max-width: 600px; margin: 50px auto; } input, textarea { width: 100%; padding: 10px; margin: 10px 0; } button { padding: 10px 20px; background: #0066cc; color: white; border: none; } .error { color: red; } .success { color: green; padding: 20px; border: 2px solid green; } </style> </head> <body> <h1>Contact Us</h1> {% if metadata.method == 'GET' %} <form method="POST"> <input name="name" placeholder="Your name" required> <input name="email" type="email" placeholder="Your email" required> <textarea name="message" placeholder="Your message" rows="5" required></textarea> <button type="submit">Send Message</button> </form> {% elsif validate.valid %} <div class="success"> <h2>Thank you!</h2> <p>We received your message and will respond soon.</p> <a href="/">← Back to home</a> </div> {% else %} <div class="error"> <h2>Please fix these errors:</h2> <ul> {% for error in validate.errors %} <li>{{ error }}</li> {% endfor %} </ul> <a href="/contact">← Try again</a> </div> {% endif %} </body> </html>output: format: html rawBody: ${html.output}
★ Insight ─────────────────────────────────────
One ensemble handles both GET (show form) and POST (submit). Use ${metadata.method} to check which HTTP method was used. Add rate limiting to prevent spam!
─────────────────────────────────────────────────
name: sitemap-xmltrigger: - type: http path: /sitemap.xml methods: [GET] public: true cache: enabled: true ttl: 3600 # Cache for 1 hourflow: # Fetch all published posts - agent: fetch-posts operation: data config: backend: d1 binding: DB query: "SELECT slug, updated_at FROM posts WHERE published = 1" # Generate XML - operation: code config: handler: | const posts = input.fetchPosts || [] const urls = posts.map(post => ` <url> <loc>https://yourdomain.com/blog/${post.slug}</loc> <lastmod>${post.updated_at}</lastmod> </url>` ).join('\n') return { output: `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://yourdomain.com/</loc> <priority>1.0</priority> </url>${urls}</urlset>` }output: format: xml rawBody: ${code.output}
★ Insight ─────────────────────────────────────
Your sitemap is generated dynamically from the database! As you add blog posts, they automatically appear in the sitemap. Add caching to avoid querying the database on every request.
─────────────────────────────────────────────────