What’s a Website in Ensemble?
In Ensemble, websites are built from ensembles with HTTP triggers:- Each route = one ensemble file
- Pages render HTML using templates
- APIs return JSON data
- Static files (robots.txt, sitemap.xml) are ensembles too
Quick Example: Hello World Page
Createensembles/pages/hello.yaml:
pnpm run build && pnpm run dev
Visit: http://localhost:8787/hello
That’s it! You just created a web page.
Website Structure
Organize your site by feature:trigger: {type: http}.
Example 1: Homepage with Dynamic Content
Let’s fetch blog posts from a database and display them. Createensembles/pages/home.yaml:
http://localhost:8787/
Example 2: Dynamic Blog Post Page
Createensembles/pages/blog-post.yaml:
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.
─────────────────────────────────────────────────
Example 3: Contact Form (GET + POST)
Handle both displaying a form (GET) and processing submissions (POST) in one ensemble. Createensembles/pages/contact.yaml:
${metadata.method} to check which HTTP method was used. Add rate limiting to prevent spam!
─────────────────────────────────────────────────
Example 4: JSON API Endpoint
Not everything needs to be HTML. Createensembles/api/users.yaml:
Example 5: Static Files (robots.txt, sitemap.xml)
Even “static” files are ensembles - but they can be dynamic!robots.txt
Createensembles/static/robots.yaml:
sitemap.xml (Dynamic from Database)
Createensembles/static/sitemap.yaml:
Template Engines
Ensemble supports three template engines:1. Liquid (Default - Recommended)
2. Handlebars
3. Simple (String Interpolation)
Authentication
Protect routes with authentication:public: true.
CORS for APIs
Enable cross-origin requests:Best Practices
1. Organize by Feature
✅ Good:2. Use Path Parameters
✅ Good:3. Add Rate Limiting to Forms
4. Cache Expensive Operations
5. Validate Input
Testing Your Website
Createtests/pages.test.ts:
pnpm test
Deployment
Deploy to Cloudflare Workers:What You Built
In this guide, you created:- ✅ Homepage with dynamic database content
- ✅ Blog post pages with URL parameters
- ✅ Contact form with validation and email
- ✅ JSON API with authentication
- ✅ Dynamic sitemap.xml from database
- ✅ Static robots.txt file
Next Steps
Triggers
Deep dive into triggers
HTML Operation
Advanced HTML rendering
Data Operation
Database operations
Email Operation
Send emails
Troubleshooting
Page not found (404)
Page not found (404)
Problem: Visiting
/hello returns 404Fixes:- Rebuild:
pnpm run build(ensembles are discovered at build time) - Check path in ensemble matches URL:
path: /hello - Ensure ensemble is in
ensembles/directory
Template variables not rendering
Template variables not rendering
Problem: Page shows
{{ post.title }} literallyFixes:- Set template engine:
templateEngine: liquid - Check data is passed:
data: { post: ${fetch-post} } - Verify variable names match template
Form submission not working
Form submission not working
Problem: POST request fails or does nothingFixes:
- Add POST to methods:
methods: [GET, POST] - Check condition uses correct metadata:
${metadata.method === 'POST'} - Verify form action matches path:
<form method="POST" action="/contact">
Database query returns empty
Database query returns empty
Problem:
${fetch-posts} is empty arrayFixes:- Check binding matches wrangler.toml:
binding: DB - Verify database has data:
wrangler d1 execute DB --command "SELECT * FROM posts" - Run migrations:
wrangler d1 migrations apply DB

