Pharos: Build the App, Not the Bridge
Most dynamic web stacks make you build the same feature twice: once as the product the user sees, then again as transport code joining browser state to server state.
For Beacon Beats’ Add to cart button, that second implementation would often mean an API controller, request and response models, browser fetch, a client cart store, submit/loading/error state, router code, cache invalidation, and repeated authentication and CSRF wiring.
Pharos lets the application declare the interaction instead.
The browser submits intent
The product card is a normal form:
<form pharos-action="add_pack" pharos-target="cart-panel">
<input type="hidden" name="product_id" value="{id}">
<button class="button button-primary" type="submit">Add pack to cart</button>
</form>
It submits product identity—not price, currency, user, owner, total, or asset path. At build time Pharos resolves add_pack to the declared method and route, adds the CSRF field, and verifies that cart-panel is a declared response target.
The feature says what the action means
The cart feature reads the published product and current user’s cart, performs an idempotent relational upsert, then refreshes and renders the cart region:
actions:
add_pack:
path: /cart/items
method: POST
policy: customer
input:
product_id: {type: integer, required: true, minimum: 1}
transaction:
steps:
- require: {query: published_product}
- require: {query: current_cart}
- upsert:
entity: shopping_cart_item
unique_by: [cart_id, product_id]
values:
cart_id: query.current_cart.id
user_id: subject.user_id
product_id: query.published_product.id
price_minor: query.published_product.price_minor
currency: query.published_product.currency
response:
render: cart_summary
target: cart-panel
queries: [current_cart_items]
fallback: {redirect: /cart}
The returned fragment is ordinary app-owned HTML. Pharos runs the authoritative query after commit and renders its values into that fragment.
What happens on Add to cart
- Pharos matches the declared action and applies session, origin, CSRF, policy,
- It binds
product_idinto parameterized queries; browser values are never - It re-reads publish state, price, currency, and the current user’s cart.
- It commits the declared upsert.
- It re-runs
current_cart_itemsand renderscart_summary. - The framework interaction client replaces
cart-panel, restores focus, and
body-size, and input gates.
interpolated into SQL.
announces the update. A browser without enhancement follows /cart instead.
Open Network while using Beacon Beats. The mutation is one form POST returning HTML for the declared region. There is no app-written /api/cart/items, JSON serializer, client cart model, submit handler, router call, or refetch.
The mental model changes from “my frontend calls my backend” to “my app has a page, a form, a transaction, and a committed-state fragment.” The HTTP boundary still exists; Pharos owns the repetitive mechanics so it does not become a second application architecture you maintain.
What you can leave behind
Pharos reduces the amount of stack assembly each app has to carry. For many application surfaces, you no longer need to maintain:
- frontend frameworks just to get admin and API surfaces
- separate language runtimes for app serving
- application servers such as Gunicorn, uWSGI, or Passenger
- heavy ORM layers and database abstraction libraries
- frontend build chains such as webpack, Vite, or Babel
- dedicated email libraries for framework-level SMTP delivery
The result is a smaller application boundary: the Pharos runtime, a configured database backend when the app needs persistence, and a reverse proxy such as Nginx or Caddy for TLS and domain routing.
It still scales to real application work
Beacon Beats applies the same model to public catalog queries, URL filters, server-driven navigation, cart removal, atomic order and entitlement creation, My Library, protected range-capable downloads, and an owner dashboard. The only app-authored JavaScript controls audio playback because that behavior genuinely belongs in the browser.
Pharos does not make your domain decisions disappear. You still own entities, business rules, policies, templates, brand, assets, and deliberate integration APIs. Pharos owns the reusable request, validation, persistence, rendering, security, media, packaging, and operational machinery surrounding those decisions.
That makes the same approach useful for publishing, SaaS dashboards, booking, marketplaces, customer portals, internal tools, and other dynamic SPAs—not only commerce.
Next, continue into Build a Scheduling and Workflow App and the broader framework reference.