Skip to content

Marketplace API

The sample project shipped with Reqloom, and the one used throughout these docs. It models a B2C marketplace — vendors list products, customers buy them, admins handle refunds.

Terminal window
git clone https://github.com/Mirzabaig313/Reqloom
reqloom lint --project Reqloom/samples/marketplace
Terminal window
LINT OK — 3 actors, 5 resources, 27 operations. No errors.

Linting needs no server, so this works offline. It’s the fastest way to confirm your install is sound.

samples/marketplace/
├── reqloom.yaml
├── environments/
│ └── local.yaml
├── actors/
│ ├── admin.yaml
│ ├── vendor.yaml
│ └── customer.yaml
└── resources/
├── products.yaml
├── cart.yaml
├── orders.yaml
├── refunds.yaml
└── reviews.yaml
reqloom.yaml
version: 1
name: MarketplaceAPI
description: B2C marketplace backend (sample)
default_environment: local
imports:
- environments/*.yaml
- actors/*.yaml
- resources/*.yaml

Note the resource ids come from name: inside each file, so resources/orders.yaml declares name: order and its operations are order.create, order.pay, and so on — singular.

Each is an identity with its own login. admin and vendor use email/password; customer uses an OTP chain.

actors/vendor.yaml
name: vendor
description: Marketplace vendor with email/password auth
auth:
strategy: simple
method: POST
path: /api/v1/auth/vendor/login
body:
email: "{{env.vendor_email}}"
password: "{{env.vendor_password}}"
expect_status: 200
extract:
token: $.data.accessToken
refresh_token: $.data.refreshToken
vendor_id: $.data.user.id
session:
ttl: 15m
refresh:
method: POST
path: /api/v1/auth/refresh
body:
refresh_token: "{{vendor.refresh_token}}"
extract:
token: $.data.accessToken
inject:
headers:
Authorization: "Bearer {{vendor.token}}"

Two details worth copying into your own schemas. The login extracts vendor_id as well as the token, so operation paths can use {{vendor.vendor_id}} without a separate “who am I” request. And the refresh block means an expired session costs one request instead of a full re-login — extractions merge, so vendor_id survives.

environments/local.yaml
name: local
variables:
baseUrl: http://localhost:3000
admin_email: admin@marketplace.test
admin_password: !secret ADMIN_PASSWORD
vendor_email: vendor@marketplace.test
vendor_password: !secret VENDOR_PASSWORD
customer_email: customer@marketplace.test
test_phone: "+15555550100"

Emails are committed; passwords are !secret, so they come from your OS keychain and never touch the repo. Note test_phone is quoted — unquoted it would be mangled into a number.

Each resource declares what must exist before it can be used. cart.add_item is a good example of both dependency kinds at once:

resources/cart.yaml
name: cart
description: Customer's shopping cart (one cart per customer)
operations:
add_item:
method: POST
path: /api/v1/cart/items
actor: customer
depends_on: [product.publish]
body:
product_id: "{{product.product_id}}"
quantity: 2
expect_status: 200
extract:
cart_item_id: $.data.id
cart_total: $.data.cart_total

{{product.product_id}} is an implicit dependency — it needs whichever operation extracts that value. depends_on: [product.publish] is explicit: publishing returns nothing this operation reads, but an unpublished product can’t be added to a cart.

refund.approve declares exactly one dependency, and gets six more from the graph:

Terminal window
reqloom run refund.approve --project samples/marketplace
Terminal window
Loaded project: MarketplaceAPI (3 actors, 5 resources)
Running: refund.approve (chain of 7 steps, env=local)
[1] Running: product.create (attempt 1)
[2] Running: product.publish (attempt 1)
[3] Running: cart.add_item (attempt 1)
[4] Running: order.create (attempt 1)
[5] Running: order.pay (attempt 1)
[6] Running: refund.request (attempt 1)
[7] Running: refund.approve (attempt 1)

Three actors are involved — vendor for steps 1–2, customer for 3–6, admin for 7 — and their three logins don’t appear in the numbering, because a login happens inside the step that needs it.

Trace it backwards and the whole chain is one edge per file:

StepNeedsBecause
refund.approverefund.requestcan’t approve what doesn’t exist
refund.requestorder.paycan’t refund an unpaid order
order.payorder.createcan’t pay a nonexistent order
order.createcart.add_itemcan’t order an empty cart
cart.add_itemproduct.publishcan’t buy an unpublished product
product.publishproduct.createcan’t publish nothing

The sample points at http://localhost:3000 and the repo doesn’t include a backend, so reqloom run will fail on connection unless you point it somewhere real. That’s expected — lint is the offline check.

To actually run it, point baseUrl at an API implementing these routes:

Terminal window
reqloom run refund.approve --project samples/marketplace \
--var baseUrl=https://your-api.example.com
PatternWhereWhy
Extract an id at loginevery actorAvoids a “who am I” request
refresh blockactors/vendor.yamlExpiry costs one request, not a re-login
!secret for passwordsenvironments/local.yamlSchema stays committable
Explicit depends_on for state changescart.add_itemPublish returns nothing but must happen
Separate resource per lifecyclerefunds.yamlA refund has its own id and states
Quoted phone numberenvironments/local.yamlStops YAML retyping it
list_for_customer / list_for_vendororders.yamlSame endpoint shape, different actor
Terminal window
reqloom lint --project samples/marketplace # confirm what parses

Then open resources/orders.yaml — seven operations, and the widest spread of depends_on edges in the project. resources/reviews.yaml is the smallest and a good first read.