Integrations & channels
GraphQL API
Generated from docs/plan/core/graphql-api/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
A GraphQL interface alongside REST, allowing field-level query selection and resolving N+1 request problems. Both APIs share the same authorization, audit log, and rate limiting. Developers query products, orders, customers, collections, and inventory in a single request.
The problem
Developers want flexible queries but the API is REST-only; they fetch too much data or make N+1 requests. A single product page requires 5+ REST calls (product, variants, images, inventory per warehouse, reviews); GraphQL does it in one.
What it does
- GraphQL schema covering Products, Orders, Customers, Collections, Inventory, Webhooks
- Query root for all entities with cursor-based pagination
- Field-level filtering and sorting (products by collection, price range, availability)
- Mutations for creating/updating products, orders, customers (same auth as REST)
- Subscriptions for real-time order/product changes via WebSocket
- Batch query support with DataLoader to prevent N+1 database queries
- Field-level permission enforcement (same role/scope rules as REST)
- Audit logging for all mutations (same trail as REST mutations)
- Full-text search via GraphQL root query
- Introspection endpoint with schema documentation
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- File uploads via GraphQL (reason: REST multipart is sufficient; GraphQL file handling adds complexity without merchant value)
- Relay-style connection spec (reason: AstroBaaS cursors don’t align with Relay; cursor pagination is sufficient)
- Subscription authentication via token refresh (reason: bearer token at connection time is core; refresh belongs to paid OAuth addon)
- Real-time sync across multiple clients (reason: subscription events are single-user; multi-user sync is paid sync service)
- Custom resolver logic per shop (reason: plugins own business logic; schema is fixed for all shops)
Data model
none
API
- POST /graphql
- WS /graphql
- GET /graphql/schema.json
- GET /graphql/docs
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
GraphQL playground in admin (Apollo Sandbox embed). Schema documentation linked from API docs. Mutation audit trail visible in existing audit log interface. Rate limit tracking per query complexity (not just per IP). Query performance metrics (slow query log).
The seam — why this is core
Core owns the GraphQL schema, resolver implementations, and authorization layer. Core provides a standard schema with no per-shop customization. Paid integrations can add custom types via plugins that hook schema building.
Core owns the interface + honest GraphQL implementation; query flexibility is infrastructure, not a per-country obligation or support commitment.
Dependencies
- Existing REST API (must fully parallel it in data model)
- WebSocket support in runtime
- Authorization system (same roles/scopes as REST)
- Audit logging system
- Database query profiling (for N+1 detection)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- A GraphQL query for a product and its 10 variants makes a single database query (verified with query plan, no N+1 detected)
- A GraphQL mutation returns 401 when the API key lacks the required scope
- A GraphQL subscription receives a real-time event within 500ms of the mutation that triggers it
- The introspection schema lists all fields available in the corresponding REST endpoint
- A GraphQL query with nested fragments (e.g., products { variants { inventory { warehouse } } }) fetches data in a single pass
- Field-level permissions work: a customer cannot query order totals they lack access to
- Audit log entries for GraphQL mutations are structurally identical to REST mutations (same timestamp, user, scope)
Risks
If N+1 prevention is not implemented, a popular query (e.g., products { variants { inventory { warehouse } } }) can overwhelm the database. If subscriptions don’t time out, idle connections leak memory. If schema docs fall out of sync with REST, developers trust wrong docs and client code diverges. If mutations don’t return the same validation errors as REST, clients behave differently between APIs.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: included (GraphQL + REST); Magento: N/A |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.