Build a hybrid product
Launch one product with a customer app and a metered API.
Outcome
Customers get a hosted app and an API under one subscription. Use this when the UI and API share plans, route grants, and usage.
Prerequisites
- A Farther Shore business repo and authenticated CLI
- A public HTTPS backend
Define the product
Add the API route and both surfaces to the single default-exported fs.business() result in business/:
import * as fs from "@farthershore/business";
const requests = fs.requests();
const api = fs.backend("api", {
transport: { mode: "direct" },
default: true,
});
const listJobs = fs.route("/v1/jobs", {
get: { backend: api, costs: [requests.fixed(1)] },
});
const createJob = fs.route("/v1/jobs/create", {
post: { backend: api, costs: [requests.fixed(1)] },
});
const deleteJob = fs.route("/v1/jobs/{id}", {
delete: { backend: api, costs: [requests.fixed(1)] },
});
const managedJobs = fs.group("managed-jobs", [listJobs, createJob, deleteJob]);
fs.plan("starter", {
kind: fs.plan.kind.free,
grants: [managedJobs],
// A free plan carries no economics — bound it structurally, otherwise it is
// an uncapped invitation to spend your money.
limits: [requests.perMinute(60)],
});
export default fs.business();
Build, then push to a preview branch:
farthershore build --format json
git push -u origin HEAD:env/hybrid-preview
farthershore backend create acme \
--env hybrid-preview \
--name "Acme API (preview)" \
--slug api \
--transport direct \
--origin-url https://preview-api.example.com \
--default \
--idempotency-key <persisted-backend-create-attempt-key> \
--format json
The managed repo starts without sample frontend code. Add the custom
frontend/ Vite application, install @farthershore/farthershore-js, and push
it on the same preview branch before using farthershore frontend status.
Verify
Open the preview portal, subscribe with a test persona on starter, load the
app, and call POST /v1/jobs/create. Then run:
farthershore usage summary acme --format json
Common failures
MANAGED_BY_CODE: editbusiness/; do not retry a contract write through the API.- API request denied: confirm the subscriber has
starterand the route matches exactly. - App works but API fails: check the backend status and origin separately.
Recover
Revert the business commit and push the preview branch again. Production is unchanged until explicitly published.
Next steps
See frontend setup, backend setup, and access-aware UI.
Agent prompt
In this Farther Shore repo, add a frontend plus metered API using the existing functional business program. Read
AGENTS.md, preserve existing plans, runfarthershore build --format json, and report the preview test commands. Do not publish production.