GraphQL vs REST in Virto Commerce

Historically, Virto Commerce frontend solutions followed a layered approach typical of REST-based architectures. Frontend applications consumed REST APIs, while significant business logic, orchestration, and aggregation lived either in custom ASP.NET storefronts or intermediate “backend-for-frontend” layers. These layers communicated with Virto Commerce core modules, which themselves were modular and built around single-responsibility principles.
Over time, this approach revealed several structural problems::

  • Slow adaptation to business requirements: Customer-facing features require rapid updates. With REST, frontend teams depend on backend developers to customize endpoints. This involves coding in different environments - C# for the BFF (often monolithic) and modular platforms like Virto Commerce - leading to skill gaps and slower iterations.

  • Scalability issues across devices: Modern ecommerce spans web, mobile apps, kiosks, and native applications. REST’s rigid endpoints force duplicate logic across layers, making it hard to deliver consistent functionality. The three-layer architecture (frontend, BFF, core backend) complicates reusability and increases maintenance.

  • Team dependencies and bottlenecks: Frontend (e.g., JavaScript) and backend (e.g., C#) developers must synchronize, creating delays. For complex solutions, this escalates as scenarios grow, hindering local/global customizations and overall delivery.

  • Performance and reliability concerns: As technology advances, supporting diverse touchpoints demands more development effort. REST often leads to over-fetching data, network latency from multiple calls, and inefficient scaling, especially with legacy systems like ERPs.

These issues detected in Virto Commerce 4-5 years ago prompted a rethink: how to simplify, unify, and optimize the API layer for better agility and performance.

Virto Commerce xAPI

Virto Commerce introduced the concept of a high-performance Experience API:

  • A single, unified business-oriented API.
  • Designed specifically for frontend and client applications.
  • Decoupled from internal module boundaries.
  • Focused on business language, not technical services.

This Experience API became the natural place for GraphQL.

GraphQL, a query language for APIs developed by Facebook, addresses these pain points by providing a flexible, client-driven approach. In Virto Commerce’s “Experience API,” GraphQL replaces or complements REST for customer-facing interactions, consolidating business logic into modular, high-performance endpoints. It’s built on principles like single responsibility, platform-agnosticism, and extensibility, ensuring it’s not just a protocol but a business-oriented language.

Virto Commerce intentionally keeps both GraphQL and REST, each for different purposes.

GraphQL is used for: REST is used for:
* Frontend applications.
* Customer-facing experiences.
* Business workflows (browse, search, cart, checkout).
* Aggregated, contextual data.
* High-performance read/write scenarios.
* System integrations.
* ERP synchronization.
* Bulk data import/export.
* Back-office operations.
* Data migration and administrative APIs.

GraphQL treats APIs as a unified interface for all touchpoints ( Web storefronts and mobile apps, chatbots, IoT devices, marketplaces), decoupling them from underlying modules.

With REST, frontend teams often had to orchestrate multiple API calls and reshape data client-side. GraphQL moves this orchestration server-side. A frontend requests exactly the business data it needs and receives it in a single response.

GraphQL decouples frontend development from backend endpoint redesign. As business requirements evolve, frontend teams can adjust queries without waiting for new REST endpoints or payload changes, as long as the schema already exposes the needed fields.

Virto Commerce core modules are built on strict single-responsibility principles. GraphQL does not replace this modularity; instead, it sits above it.

GraphQL in Virto Commerce is designed as a high-performance, read-optimized API:

  • Responses are typically served within strict latency targets (around hundreds of milliseconds).
  • Indexed data, caching, and optimized resolvers are preferred over direct database access.
  • The API is designed to handle millions of requests from diverse clients without exposing backend fragility.

This is particularly important when shielding frontend applications from slower or legacy systems such as ERP integrations.

By moving frontend business logic into GraphQL-based Experience API modules:

  • Custom storefront backend layers are eliminated.
  • Load balancing, routing, and static content delivery are handled by standard infrastructure (CDN, NGINX, Azure Application Gateway).
  • Frontend applications become static assets served efficiently, while all dynamic behavior flows through the Experience API.

This reduces operational cost, simplifies maintenance, and standardizes deployment pipelines.

Core Components of GraphQL

GraphQL’s power lies in its structured, declarative nature. Here’s a breakdown of its fundamental elements:

  • Schema: The schema is the backbone of GraphQL, defining the API’s data structure in a strongly typed manner. It acts as a contract between client and server, specifying available types (e.g., objects like “Product” or “Order”), fields (e.g., “title” or “price”), and relationships. This self-documenting schema enables tools for auto-generating clients, validation, and introspection. Unlike REST’s often loosely defined endpoints, GraphQL schemas evolve predictably - new fields can be added without breaking existing queries - making it ideal for long-term API maintenance in ecommerce.

  • Queries: Queries are read-only operations used to fetch data. Clients specify exactly what data they need, and the server responds with a matching structure. For example, a query might request product details for a specific store and language context:

    query GetOrdersForApproval ($storeId: String!,  $cultureName: String!) {​
      ordersForApproval(storeId: $ storeId, cultureName: $cultureName) {​
        id​
        number​
        status​
        total {​
          amount​
          currency​
        }​
      }​
    }​
    

    This avoids REST’s over-fetching, ensuring efficient, targeted data retrieval.

  • Mutations: Mutations handle write operations, such as creating, updating, or deleting data. They follow the same request-response pattern as queries but perform side effects. For instance, a mutation could approve an order:

    mutation ApproveOrder($storeId: String!,  $cultureName: String!, $orderId: String!) {​
      approveOrder(storeId: $ storeId, cultureName: $cultureName, orderId: $orderId) {​
        id​
        number​
        status​
        approvedBy {​
          id​
          name​
        }​
        approvedDate​
      }  ​
    }
    

    Mutations return data, allowing clients to fetch updated results in one request, reducing the need for follow-up queries common in REST.

  • Subscriptions: Subscriptions enable real-time updates via WebSockets, pushing data to clients when events occur. This is perfect for dynamic e-commerce features like live inventory changes or order notifications:

    subscription OnOrderStatusChanged($userId: ID!) {​
      orderStatusChanged(userId: $userId) {​
        id​
        number​
        status​
        updatedDate​
      }​
    }​
    

    Unlike REST’s polling (which wastes resources), subscriptions provide efficient, event-driven real-time communication.

Multiregional Development

As Virto Commerce deployments grow globally, traffic patterns change. Frontend applications, customer portals, and integrations start generating load from different geographic regions, often far from the primary data center. In such setups, latency, availability, and resilience become just as important as functional correctness.

GraphQL multiregionality in Virto Commerce addresses these concerns by allowing the platform to serve read-heavy workloads locally, while still preserving data consistency and control over writes.

The key idea is simple: not all regions need write access, and not all requests need to hit the primary database.

Regional Roles: Read-Write and Read-Only Zones

In a multiregional setup, Virto Commerce distinguishes between regions based on their responsibilities:

  • Primary (read-write) region: The authoritative region where mutations are executed and where the master database resides.
  • Secondary (read-only) regions: The regions serve queries only. They are backed by replicated, read-only databases and are optimized for low-latency access close to users.

This separation is not hardcoded in application logic. Instead, it is achieved through deployment-time configuration, database replication, and traffic routing. No custom code is required to define regions or their roles: this is handled by IT administrators or solution architects as part of infrastructure setup.

How GraphQL Fits into This Model

GraphQL already enforces a strong conceptual distinction between queries (read operations) and mutations (write operations). Virto Commerce leverages this distinction directly at the infrastructure level.

  • GraphQL queries can be safely routed to read-only regions.
  • GraphQL mutations are always routed to the primary, read-write region.

Because the Experience API is the single business-facing entry point, routing decisions can be made consistently and transparently, without frontend or integration clients needing to understand database topology.

This is one of the key reasons multiregional routing was difficult to implement in earlier REST-centric architectures: REST endpoints often mixed reads and writes, making safe routing far more complex.

Traffic Routing and Operational Control

In practice, incoming traffic is routed at the infrastructure layer (for example, via load balancers or gateways):

  • Requests identified as GraphQL queries are directed to the nearest read-only region.
  • Requests containing mutations are forwarded to the primary region.

This approach dramatically reduces load on the primary database and improves response times for global users, especially for catalog browsing, search, and content-heavy pages.

Importantly, interactive or administrative actions are disabled in read-only regions. There are no background jobs, no Hangfire schedulers, and no administrative access that could accidentally attempt to modify data locally.

Handling “Exceptional” Write Scenarios from Read-Only Regions

A natural question arises: What if a request originates in a read-only region but still needs to trigger a write?

The Experience API provides a clear and controlled answer: in such cases, the operation must be executed asynchronously using a fire-and-forget pattern:

  1. The read-only region accepts the request.
  2. Instead of writing directly, it enqueues an asynchronous task (for example, via Hangfire).
  3. That task is executed in the primary read-write region.
  4. Data is updated centrally and then replicated back to read-only regions.

This preserves the integrity of the multiregional model:
read-only regions never write directly, yet business workflows remain possible.

To ensure that integration load does not negatively impact customer-facing performance, there’s a separaryion between:

  • Frontend applications (customer portals, storefronts, mobile apps) that are ideal candidates for GraphQL multiregionality. Their workloads are read-heavy, latency-sensitive, and benefit most from regional replicas.
  • Integrations (ERP sync, bulk imports, back-office operations) that often generate heavy load and typically rely on REST APIs. These workflows are better suited to centralized processing and are intentionally kept outside the GraphQL Experience API.

Earlier architectures struggled with multiregional routing because:

  • Backend logic was fragmented across storefronts and integration layers.
  • APIs were not clearly separated into read and write responsibilities.
  • Routing decisions required application-level awareness of database roles.

The Experience API changes this fundamentally:

  • All frontend access flows through a single, well-defined GraphQL endpoint.
  • Business logic and orchestration are centralized.
  • Infrastructure can make routing decisions based on request type, not business semantics.

As a result, multiregionality becomes an infrastructure concern, not an application burden.

Key Benefits of GraphQL Multiregionality in Virto Commerce

From an architectural and operational perspective, this approach delivers several advantages:

  • Lower latency for global users.
  • Reduced load on primary databases.
  • Clear separation of read and write responsibilities.
  • Safer operational model with fewer failure modes.
  • Scalability without rewriting business logic.

Perhaps most importantly, it allows Virto Commerce to scale geographically without sacrificing the clarity of its business API or the integrity of its modular platform design.

Conclusion

The following characteristics explain why GraphQL works as the foundation of the Virto Commerce Experience API:

Principle Explanation
Product-centric GraphQL is unapologetically built for front-end engineers. It aligns with how product features are designed, how screens are composed, and how business data is consumed. In a multiregional setup, this means frontend teams can focus on user experience without knowing where data physically lives.
Hierarchical Most product development revolves around hierarchical views (pages, sections, components). GraphQL queries mirror these UI hierarchies, which makes it natural to route read-heavy, tree-shaped queries to the nearest read-only region while preserving a consistent response structure.
Strongly typed Every GraphQL service defines a strict type system. Queries are validated before execution, and responses are predictable. This is critical in multiregional environments where requests may be served by different regions but must behave identically.
Client-specified responses GraphQL exposes capabilities, not fixed payloads. Clients explicitly control which fields they request. This minimizes payload size, reduces latency, and lowers database load - key factors when serving queries from geographically distributed read replicas.
Self-documenting GraphQL APIs describe themselves via their schema. This allows tooling, validation, and client generation to remain consistent across regions and deployments, even as infrastructure becomes more complex.