vendurehq/vendure
Open source headless commerce framework built with TypeScript, NestJS, React and GraphQL
Manages headless e-commerce operations through GraphQL APIs with configurable plugins and admin interface
Shop requests flow through GraphQL resolvers to services that interact with the database through TypeORM entities. Admin operations follow the same pattern but with additional authorization checks. Plugins extend functionality by registering event handlers, API extensions, and custom services that integrate with the core data flow. Emails are triggered by events, processed through template generation, and sent asynchronously. Assets are uploaded, transformed on-demand, and served through configurable storage strategies.
Under the hood, the system uses 3 feedback loops, 4 data pools, 4 control points to manage its runtime behavior.
A 11-component fullstack. 2732 files analyzed. Data flows through 7 distinct pipeline stages.
How Data Flows Through the System
Shop requests flow through GraphQL resolvers to services that interact with the database through TypeORM entities. Admin operations follow the same pattern but with additional authorization checks. Plugins extend functionality by registering event handlers, API extensions, and custom services that integrate with the core data flow. Emails are triggered by events, processed through template generation, and sent asynchronously. Assets are uploaded, transformed on-demand, and served through configurable storage strategies.
- Request Authentication — GraphQL requests are authenticated and wrapped in RequestContext containing user, channel, and permission information for subsequent operations
- API Resolution — GraphQL resolvers translate shop and admin queries into service method calls, applying channel-specific filtering and authorization checks [RequestContext]
- Business Logic Processing — Services like OrderService orchestrate business operations, updating entity states and triggering events while maintaining transactional consistency [RequestContext → Order]
- Database Persistence — TypeORM EntityManager handles database operations with multi-channel isolation, transaction support, and optimistic locking for concurrent updates [Order → Order]
- Event Broadcasting — Commerce events are emitted after successful operations, triggering registered plugin handlers for emails, webhooks, and other side effects [Order → EmailEventData]
- Plugin Processing — Plugin event handlers process triggered events, potentially modifying data or triggering additional operations like email sending or inventory updates [EmailEventData → EmailEventData]
- Asset Handling — Asset uploads are processed by AssetServerPlugin, which stores files using configured strategies and provides on-demand image transformation via Sharp [Asset → Asset]
Data Models
The data structures that flow between stages — the contracts that hold the system together.
packages/common/src/generated-types.tsGraphQL type with id: ID, createdAt: DateTime, updatedAt: DateTime, code: String, state: String, customer: Customer, lines: OrderLine[], payments: Payment[], fulfillments: Fulfillment[]
Created from cart items, progresses through states (AddingItems → ArrangingPayment → PaymentAuthorized → PaymentSettled → PartiallyFulfilled → Fulfilled), managed by state machines
packages/common/src/generated-types.tsGraphQL type with id: ID, name: String, slug: String, description: String, variants: ProductVariant[], facetValues: FacetValue[], assets: Asset[], customFields: JSON
Created in admin interface, organized with variants and facets, indexed for search, referenced by order lines when purchased
packages/common/src/generated-types.tsGraphQL type with id: ID, firstName: String, lastName: String, emailAddress: String, addresses: Address[], orders: Order[], user: User
Registered during checkout or account creation, linked to orders and addresses, managed through admin interface with notes and history
packages/core/Contains channel: Channel, user: User, session: Session, languageCode: LanguageCode, isAuthorized: boolean, permissions: Permission[]
Created for each request, carries user context and channel information throughout the request lifecycle, used for authorization and localization
packages/common/src/generated-types.tsGraphQL type with id: ID, name: String, source: String, preview: String, width: Int, height: Int, type: AssetType, fileSize: Int
Uploaded through admin interface, processed by asset-server-plugin for resizing and optimization, referenced by products and other entities
packages/email-plugin/src/types.tsEvent object with ctx: RequestContext, data: resolved from EmailEventHandler.loadData(), containing recipient, subject, templateVars, and attachments
Generated when commerce events occur (order placed, password reset), processed by event handlers, rendered with templates, and sent via configured email sender
Hidden Assumptions
Things this code relies on but never validates. These are the things that cause silent failures when the system changes.
loadAppConfig() function expects admin UI config to be available at a default endpoint (typically /admin-api/app-config) and returns valid configuration data without validation
If this fails: If the config endpoint is unavailable, returns malformed JSON, or has wrong schema, the admin UI fails to bootstrap with only a console.log error, leaving users with a blank page and no actionable error message
packages/admin-ui/src/main.ts:loadAppConfig
MetricSummaryInput types array contains only valid metric type strings that match existing MetricCalculation instances, and interval is a valid MetricInterval enum value
If this fails: Invalid metric types are silently skipped in the loop, returning incomplete metrics data without warning - users receive partial results believing they got complete metrics
packages/asset-server-plugin/src/service/metrics.service.ts:getMetrics
endOfDay(new Date()) creates cache keys assuming consistent timezone across all servers and that system clock is synchronized for cache hits
If this fails: In multi-server deployments with different timezones or clock skew, identical metric requests generate different cache keys, causing cache misses and unnecessary recalculation of expensive metrics
packages/asset-server-plugin/src/service/metrics.service.ts:getMetrics
The loadData method expects to load all orders for metrics calculation without pagination or memory limits, assuming total order count fits in memory
If this fails: For large stores with millions of orders, attempting to load all orders at once causes out-of-memory errors or database timeout failures during metrics calculation
packages/asset-server-plugin/src/service/metrics.service.ts:loadData
registerCommands expects cliCommands array to contain valid Commander.js command definitions with proper structure (name, description, action functions)
If this fails: Malformed command definitions cause the CLI to crash during registration phase with cryptic Commander.js errors, making the entire CLI unusable until commands are fixed
packages/cli/src/cli.ts:registerCommands
vendureDashboardPlugin expects the Vendure API at 'https://demo.vendure.io:443' to be accessible and respond with valid GraphQL schema during Storybook build
If this fails: If the demo API is down or returns invalid responses, Storybook build fails completely, preventing any local development or documentation generation for the dashboard components
packages/dashboard/.storybook/main.ts:vendureDashboardPlugin
defineDashboardExtension assumes route paths like '/form-inputs-test' are globally unique across all loaded dashboard extensions without collision detection
If this fails: Multiple extensions defining the same route path results in unpredictable routing behavior where only one extension's component loads, potentially breaking critical admin functionality
packages/dashboard/e2e/fixtures/form-inputs-test-dashboard/index.tsx:defineDashboardExtension
RUN_JOB_QUEUE environment variable, when set to '1', assumes JobQueueService is properly configured and all required queue backends are available
If this fails: If job queue backend (Redis, database) is unavailable but RUN_JOB_QUEUE=1, the server starts successfully but jobs silently fail to process, leaving orders unprocessed and emails unsent
packages/dev-server/index.ts:process.env.RUN_JOB_QUEUE
EmailEventHandler.loadData() callback is expected to return data that matches the generic type R, but there's no runtime validation of the returned data structure
If this fails: If loadData returns incompatible data types (wrong shape, missing properties), email template rendering fails silently or produces incorrect emails using undefined template variables
packages/email-plugin/src/types.ts:EventWithAsyncData
GlobalTemplateVarsFn async function assumes RequestContext and Injector remain valid throughout the async execution and that injector.get() calls don't fail
If this fails: If RequestContext expires or services are not properly registered during template rendering, email generation fails with dependency injection errors, blocking all email sending
packages/email-plugin/src/types.ts:GlobalTemplateVarsFn
System Behavior
How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.
Data Pools
TypeORM-managed relational database storing all commerce entities with multi-channel support and transactional consistency
Asynchronous task queue for background processing of emails, imports, and other long-running operations
File storage system for uploaded assets with configurable backends (local filesystem, S3) and on-demand transformation cache
Stores user sessions and authentication tokens with configurable TTL for both shop customers and admin users
Feedback Loops
- Order State Machine (training-loop, reinforcing) — Trigger: Order state transition events. Action: Updates order state, triggers payment processing, inventory updates, and fulfillment operations. Exit: Order reaches final state (Cancelled, Fulfilled).
- Email Retry Loop (retry, balancing) — Trigger: Email sending failure. Action: JobQueueService retries email sending with exponential backoff. Exit: Maximum retry attempts reached or successful send.
- Asset Transform Cache (cache-invalidation, reinforcing) — Trigger: Asset transformation request. Action: Generate transformed image if not cached, store result for future requests. Exit: Cache entry expires or asset is deleted.
Delays
- Email Processing Queue (async-processing, ~configurable via job queue settings) — Emails are sent asynchronously to avoid blocking order processing operations
- Asset Transformation (cache-ttl, ~varies by image size and complexity) — First request for transformed image generates it on-demand, subsequent requests served from cache
- Database Connection Pool (queue-drain, ~milliseconds to seconds under high load) — Concurrent requests may wait for available database connections during peak traffic
Control Points
- Plugin Configuration (architecture-switch) — Controls: Which plugins are loaded and their configuration options. Default: defined in VendureConfig
- Channel Selection (runtime-toggle) — Controls: Multi-tenant behavior, product visibility, and pricing per sales channel. Default: determined by request headers
- Email Template Selection (feature-flag) — Controls: Which email templates are used for different event types and languages. Default: configured per email handler
- Asset Storage Strategy (architecture-switch) — Controls: Whether assets are stored locally or in cloud storage (S3). Default: configured in plugin options
Technology Stack
Primary language providing type safety across GraphQL schemas, entities, and business logic
Backend framework providing dependency injection, decorators, and modular architecture for the core system
API layer for both shop and admin interfaces with automatic schema generation and type safety
Database ORM handling entity relationships, migrations, and multi-database support with decorators
Frontend framework for the mature admin-ui package providing reactive forms and component architecture
Frontend framework for the modern dashboard package with hooks and functional components
Monorepo management for coordinating builds, versioning, and publishing across 16 packages
High-performance image processing for asset transformations in the asset-server-plugin
Key Components
- GraphQL Resolvers (adapter) — Expose shop and admin APIs by translating GraphQL queries/mutations into service method calls, handling authentication and authorization per request context
packages/core/ - OrderService (orchestrator) — Coordinates order lifecycle from creation through fulfillment, managing state transitions, payment processing, and inventory updates
packages/core/ - EntityManager (store) — Handles database operations using TypeORM, providing repository pattern access to entities with transaction support and multi-channel isolation
packages/core/ - PluginRegistry (registry) — Manages plugin lifecycle during application bootstrap, registering services, API extensions, and configuration options from loaded plugins
packages/core/ - AdminUiPlugin (adapter) — Serves the Angular admin interface as static files while providing metrics API endpoints for dashboard widgets and analytics
packages/admin-ui-plugin/ - AssetServerPlugin (processor) — Handles asset upload, storage, and on-demand image transformation using configurable storage strategies (local, S3) and Sharp for image processing
packages/asset-server-plugin/ - EmailEventListener (processor) — Listens for commerce events and triggers email generation pipeline, resolving templates and data before sending via configured email sender
packages/email-plugin/ - JobQueueService (scheduler) — Manages asynchronous task processing with configurable queue backends, handling job scheduling, retries, and progress tracking
packages/job-queue-plugin/ - VendureCLI (orchestrator) — Provides command-line interface for project scaffolding, code generation, and development workflows using Commander.js for command parsing
packages/cli/src/cli.ts - AdminUIApp (gateway) — Bootstraps Angular application with dynamic configuration loading, connecting to Vendure GraphQL APIs for admin operations
packages/admin-ui/src/main.ts - +1 more components
Package Structure
The main e-commerce backend engine providing GraphQL APIs, entity management, order processing, and plugin architecture.
Angular-based administrative interface for managing products, orders, customers, and store configuration.
React-based next-generation admin dashboard with extensible form components and modern UI patterns.
Command-line interface for project scaffolding, code generation, and development workflows.
Shared TypeScript types, utilities, and generated GraphQL types used across all packages.
Plugin that serves the Angular admin-ui as part of the core server with metrics and dashboard capabilities.
Plugin for serving and transforming assets (images, files) with resize, crop, and storage strategies.
Plugin for handling transactional emails with template generation, event handlers, and multiple sending strategies.
Project scaffolding tool for creating new Vendure applications with database setup and initial configuration.
Development server setup with test plugins and example configurations for Vendure development.
Testing utilities and helpers for writing Vendure plugins and applications.
Development toolkit for building custom admin UI extensions and components.
Plugin providing asynchronous job processing capabilities with queue management.
Security hardening plugin for production deployments.
Plugin for collecting usage analytics and telemetry data.
Plugin that provides GraphiQL interface for exploring the GraphQL API during development.
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated Fullstack Repositories
Frequently Asked Questions
What is vendure used for?
Manages headless e-commerce operations through GraphQL APIs with configurable plugins and admin interface vendurehq/vendure is a 11-component fullstack written in TypeScript. Data flows through 7 distinct pipeline stages. The codebase contains 2732 files.
How is vendure architected?
vendure is organized into 4 architecture layers: Core Engine, Plugin System, Admin Interfaces, Shared Infrastructure. Data flows through 7 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.
How does data flow through vendure?
Data moves through 7 stages: Request Authentication → API Resolution → Business Logic Processing → Database Persistence → Event Broadcasting → .... Shop requests flow through GraphQL resolvers to services that interact with the database through TypeORM entities. Admin operations follow the same pattern but with additional authorization checks. Plugins extend functionality by registering event handlers, API extensions, and custom services that integrate with the core data flow. Emails are triggered by events, processed through template generation, and sent asynchronously. Assets are uploaded, transformed on-demand, and served through configurable storage strategies. This pipeline design reflects a complex multi-stage processing system.
What technologies does vendure use?
The core stack includes TypeScript (Primary language providing type safety across GraphQL schemas, entities, and business logic), NestJS (Backend framework providing dependency injection, decorators, and modular architecture for the core system), GraphQL (API layer for both shop and admin interfaces with automatic schema generation and type safety), TypeORM (Database ORM handling entity relationships, migrations, and multi-database support with decorators), Angular (Frontend framework for the mature admin-ui package providing reactive forms and component architecture), React (Frontend framework for the modern dashboard package with hooks and functional components), and 2 more. A focused set of dependencies that keeps the build manageable.
What system dynamics does vendure have?
vendure exhibits 4 data pools (Main Database, Job Queue), 3 feedback loops, 4 control points, 3 delays. The feedback loops handle training-loop and retry. These runtime behaviors shape how the system responds to load, failures, and configuration changes.
What design patterns does vendure use?
6 design patterns detected: Plugin Architecture, GraphQL API Layer, Event-Driven Side Effects, Multi-Channel Architecture, State Machine Pattern, and 1 more.
Analyzed on April 20, 2026 by CodeSea. Written by Karolina Sarna.