Skip to main content

GraphQL schema

Schema declared in etc/schema.graphqls. Resolvers under Model/Resolver/.

ProductInterface extensions

Every product type (simple, configurable, virtual, downloadable, bundle, grouped) gains four fields:

interface ProductInterface {
preorder_enabled: Boolean
preorder_eligible: Boolean
preorder_available_from: String # ISO 8601
preorder_message: String # placeholder-substituted
}

preorder_eligible differs from preorder_enabled in that it accounts for current stock state, the global config switch, the "Allow when in stock" flag, and customer overrides. Use preorder_eligible for "should I show the pre-order UI?" and preorder_enabled for "is this product configured for pre-order at all?"

Resolvers: Model/Resolver/Product/PreorderEligibleResolver and PreorderMessageResolver.

Queries

byte8PreorderEligibility(sku: String!)

Detailed eligibility check for a specific SKU — including child SKUs of configurables.

query GetPreorderEligibility($sku: String!) {
byte8PreorderEligibility(sku: $sku) {
eligible
deposit_amount
deposit_type # PERCENT | FIXED | FULL
balance_due
available_from
message
max_qty
}
}

Returns eligible: false (and skips the rest) when the product can't be pre-ordered for any reason. Resolves customer / per-product / global override hierarchy server-side.

Resolver: Model/Resolver/PreorderEligibilityResolver.

customer.preorders

Customer-scoped — requires a customer Bearer token.

query MyPreorders {
customer {
preorders {
total_count
items {
increment_id
order_increment_id
status
deposit_paid
balance_due
currency
available_from
created_at
product {
name
url_key
sku
image { url label }
}
actions {
can_cancel
can_pay_balance
pay_balance_url
}
}
}
}
}

Resolver: Model/Resolver/CustomerPreordersResolver.

Mutations

byte8CompletePreorder(preorder_id: Int!)

Triggers manual completion — equivalent to the customer clicking "Pay balance" on the My Pre-orders page. Returns the URL to redirect them to (the completion checkout).

mutation CompletePreorder($id: Int!) {
byte8CompletePreorder(preorder_id: $id) {
success
redirect_url
completion_order_increment_id
}
}

Resolver: Model/Resolver/CompletePreorderResolver.

byte8CancelPreorder(preorder_id: Int!)

Customer-initiated cancellation, subject to status / authority checks. Reverts MSI reservation, refunds deposit if configured.

mutation CancelPreorder($id: Int!) {
byte8CancelPreorder(preorder_id: $id) {
success
refund_amount
}
}

Authentication

OperationAuth
ProductInterface fieldsNone — public, cacheable
byte8PreorderEligibilityNone — public, but per-customer overrides need a token to apply
customer.preordersCustomer token required
byte8CompletePreorderCustomer token required, must own the pre-order
byte8CancelPreorderCustomer token required, must own the pre-order

Caching

ProductInterface extensions follow Magento's standard product cache tags — pre-order eligibility is invalidated when the product is saved, when stock changes for that SKU, or when global config flips.

byte8PreorderEligibility is uncached by default — it's hit on PDP loads and needs to be live with stock state. If your traffic warrants caching, add a small cache layer in your storefront (Apollo client, Next.js fetch cache) keyed on SKU + stock-id.

customer.preorders is uncacheable — different per customer, changes on every action.

REST equivalents

For non-GraphQL clients, REST endpoints are declared in etc/webapi.xml:

  • GET /V1/byte8-preorder/eligibility/:sku — public eligibility check
  • GET /V1/customers/me/preorders — customer-scoped listing
  • POST /V1/byte8-preorder/:id/complete — trigger completion
  • POST /V1/byte8-preorder/:id/cancel — cancel

Same resolvers behind both surfaces.