Examples

Services

About

An app of two services: the shop, and the billing it calls.

quillo run services/app.qwe one process

quillo run services/app.qwe --split a process for each service

Either way, open http://localhost:8080. The code does not change: in one process, the shop calls billing directly; split, over HTTP, signed with QUILLO_SERVICE_SECRET (--split makes one). To run billing on a machine of its own, start it alone and tell the others where it is:

QUILLO_SERVICE_SECRET=... quillo run billing.qwe --port 8081 --base /billing --as-service QUILLO_SERVICE_SECRET=... quillo run app.qwe --service-url billing=http://10.0.0.5:8081

Run it

$ quillo run services/app.qwe --watch

services/app.qwe

services/app.qwe
service /shop = "shop.qwe"service /billing = "billing.qwe"route GET / = <html>    <head><title>Services</title></head>    <body>        <h1>Two services</h1>        <p><a href="/shop">The shop</a> sells mugs; <a href="/billing">billing</a> keeps the invoices.</p>        <p>Billed so far: {billing.total() ?? "billing is not answering"} cents.</p>    </body></html>

services/shop.qwe

services/shop.qwe
// The shop: a page to buy from, which bills through the billing service.// `billing.invoice(...)` reads as a call of a function; where billing runs// (this process, or a server of its own) is not the shop's concern. When// it runs elsewhere, a call may take two seconds, and is tried once more if// it did not get through.service billing = "billing.qwe" { timeout: "2s", retries: 1 }let PRICE = 1200route GET / = <html>    <head><title>Shop</title></head>    <body>        <h1>A mug, {PRICE} cents</h1>        <form method="post" action="/buy">            <input name="name" placeholder="Your name"/>            <button>Buy</button>        </form>    </body></html>route POST /buy {    let inv = billing.invoice(request.form.name ?? "", PRICE, "a mug")    if is_err(inv) { reply 400 <p>Not bought: {inv.error}</p> }    let mine = billing.invoices_of(inv.customer) ?? []    <html><body>        <h1>Thank you, {inv.customer}</h1>        <p>Invoice #{inv.id}: {inv.cents} cents. You have {len(mine)} with us.</p>        <p><a href="/">Buy another</a></p>    </body></html>}

services/billing.qwe

services/billing.qwe
// Billing: invoices, as a service of its own. Other programs call its// `server fn`s (`billing.invoice(...)`); its page lists what it billed.// See app.qwe for running it in one process or as a server of its own.let invoices = []server fn invoice(customer: str, cents: int, note: str = "") -> map ! str {    if trim(customer) == "" { return err("An invoice is for someone") }    if cents <= 0 { return err("An invoice is for something") }    let inv = { id: len(invoices) + 1, customer: trim(customer), cents, note }    invoices = invoices + [inv]    inv}server fn invoices_of(customer: str) -> list = invoices | filter((i) => i.customer == customer)server fn total() -> int = invoices | reduce(0, (sum, i) => sum + i.cents)route GET / = <html>    <head><title>Billing</title></head>    <body>        <h1>Billing</h1>        <p>{len(invoices)} invoices, {total()} cents in all.</p>        <ul>{for i in invoices => <li>#{i.id} · {i.customer} · {i.cents} cents {i.note}</li>}</ul>    </body></html>

See services/ on GitHub

Thread
13Useful? Like it, or add to its thread below.

Thread 0 posts

On Services: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.