Cloudflare Workers with Quill

Quill makes it easy to build Cloudflare Workers — serverless functions that run at the edge, close to your users. With the worker on fetch syntax, you can build a full API in 15 lines of readable code.

Getting Started

The fastest way to start is with the scaffold command:

quill worker my-api
cd my-api
npm install

This creates a project with:

Build and run locally:

npm run dev

This compiles your Quill code to JavaScript and starts a local Wrangler dev server.

Manual setup

If you prefer to set things up yourself:

  1. Create a worker.quill file
  2. Compile it: quill build worker.quill
  3. Create a wrangler.toml pointing to worker.js
  4. Run with npx wrangler dev worker.js

Your First Worker

A minimal Cloudflare Worker in Quill:

worker on fetch with request:
  respond "Hello from Quill!"

This compiles to a standard Cloudflare Worker using ES modules:

export default {
  async fetch(request) {
    return new Response("Hello from Quill!");
  }
};

Routing

Use the standard URL API and if statements for routing:

worker on fetch with request:
  url is new URL(request.url)
  path is url.pathname

  if path is "/":
    respond html "<h1>Welcome!</h1>"

  if path is "/about":
    respond html "<h1>About Us</h1>"

  respond "Not found" status 404

You can use starts with for prefix matching by using JavaScript's startsWith method in expressions.

JSON APIs

Return JSON responses with the respond json keyword:

worker on fetch with request:
  url is new URL(request.url)
  path is url.pathname

  if path is "/api/hello":
    name is url.searchParams.get("name")
    if name is nothing:
      name is "World"
    respond json { message: "Hello, {name}!" }

  if path is "/api/status":
    respond json { status: "ok", version: "1.0.0" }

  respond json { error: "Not found" } status 404

The respond json keyword automatically:

Response types

Quill syntax Content-Type
respond "text" text/plain (default)
respond json { ... } application/json
respond html "<h1>...</h1>" text/html

Add a status code with the status keyword:

respond "Not found" status 404
respond json { error: "Unauthorized" } status 401

Complete JSON API Example

Here is a full example of a JSON API worker that manages a list of tasks. It handles GET and POST requests with proper error handling:

-- task-api.quill
-- A complete JSON API for managing tasks

worker on fetch with request env:
  url is new URL(request.url)
  path is url.pathname
  method is request.method

  -- GET /api/tasks -- list all tasks
  if path is "/api/tasks" and method is "GET":
    tasks is await env.TASKS_KV.list()
    items are []
    for each key in tasks.keys:
      value is await env.TASKS_KV.get(key.name)
      push(items, parseJSON(value))
    respond json { tasks: items, count: length(items) }

  -- POST /api/tasks -- create a new task
  if path is "/api/tasks" and method is "POST":
    body is await request.json()
    if body.title is nothing:
      respond json { error: "title is required" } status 400
    id is uuid()
    task is { id: id, title: body.title, done: no }
    await env.TASKS_KV.put(id, toJSON(task))
    respond json task status 201

  -- GET /api/health -- health check
  if path is "/api/health":
    respond json { status: "ok", timestamp: now() }

  -- Catch-all for unknown routes
  respond json { error: "Not found", path: path } status 404

This worker uses KV storage to persist tasks. See the KV Storage section below for how to configure it.

KV Storage

Cloudflare Workers KV is a key-value store available at the edge. To use it, add the env parameter to your worker handler:

worker on fetch with request env:
  url is new URL(request.url)
  path is url.pathname

  if path is "/get":
    key is url.searchParams.get("key")
    value is await env.MY_KV.get(key)
    if value is nothing:
      respond json { error: "Key not found" } status 404
    respond json { key: key, value: value }

  if path is "/set":
    key is url.searchParams.get("key")
    val is url.searchParams.get("value")
    await env.MY_KV.put(key, val)
    respond json { success: yes }

  respond "Not found" status 404

You also need to configure the KV binding in wrangler.toml:

name = "my-worker"
main = "worker.js"
compatibility_date = "2024-01-01"

[[kv_namespaces]]
binding = "MY_KV"
id = "your-namespace-id"

Create a KV namespace with the Wrangler CLI:

npx wrangler kv:namespace create "MY_KV"

This will output the namespace ID to paste into your wrangler.toml. See the Cloudflare KV documentation for more details.

Deployment

Deploy your worker to Cloudflare's global network:

Step-by-step deployment

# 1. Install wrangler (if not already installed)
npm install -g wrangler

# 2. Log in to your Cloudflare account
npx wrangler login

# 3. Build your Quill code to JavaScript
quill build worker.quill

# 4. Test locally before deploying
npx wrangler dev worker.js

# 5. Deploy to Cloudflare's network
npx wrangler deploy

Or if you used quill worker to scaffold your project, simply run:

# Build and deploy in one step
npm run deploy

Useful wrangler commands

Command What it does
npx wrangler dev Start a local dev server for testing
npx wrangler deploy Deploy to production
npx wrangler tail View live logs from your deployed worker
npx wrangler kv:namespace create "MY_KV" Create a new KV namespace
npx wrangler secret put API_KEY Add a secret environment variable

Your worker will be available at https://your-worker.your-subdomain.workers.dev.

For custom domains, see the Cloudflare Workers routing documentation.

Next Steps