vendurehq/vendure

Open source headless commerce framework built with TypeScript, NestJS, React and GraphQL

8,055 stars TypeScript 11 components

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.

  1. Request Authentication — GraphQL requests are authenticated and wrapped in RequestContext containing user, channel, and permission information for subsequent operations
  2. API Resolution — GraphQL resolvers translate shop and admin queries into service method calls, applying channel-specific filtering and authorization checks [RequestContext]
  3. Business Logic Processing — Services like OrderService orchestrate business operations, updating entity states and triggering events while maintaining transactional consistency [RequestContext → Order]
  4. Database Persistence — TypeORM EntityManager handles database operations with multi-channel isolation, transaction support, and optimistic locking for concurrent updates [Order → Order]
  5. Event Broadcasting — Commerce events are emitted after successful operations, triggering registered plugin handlers for emails, webhooks, and other side effects [Order → EmailEventData]
  6. Plugin Processing — Plugin event handlers process triggered events, potentially modifying data or triggering additional operations like email sending or inventory updates [EmailEventData → EmailEventData]
  7. 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.

Order packages/common/src/generated-types.ts
GraphQL 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
Product packages/common/src/generated-types.ts
GraphQL 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
Customer packages/common/src/generated-types.ts
GraphQL 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
RequestContext 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
Asset packages/common/src/generated-types.ts
GraphQL 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
EmailEventData packages/email-plugin/src/types.ts
Event 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.

critical Environment unguarded

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
critical Shape unguarded

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
warning Temporal unguarded

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
critical Resource unguarded

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
warning Contract unguarded

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
warning Environment unguarded

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
warning Scale unguarded

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
critical Environment unguarded

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
critical Shape unguarded

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
warning Temporal unguarded

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

Main Database (database)
TypeORM-managed relational database storing all commerce entities with multi-channel support and transactional consistency
Job Queue (queue)
Asynchronous task queue for background processing of emails, imports, and other long-running operations
Asset Storage (file-store)
File storage system for uploaded assets with configurable backends (local filesystem, S3) and on-demand transformation cache
Session Store (cache)
Stores user sessions and authentication tokens with configurable TTL for both shop customers and admin users

Feedback Loops

Delays

Control Points

Technology Stack

TypeScript (runtime)
Primary language providing type safety across GraphQL schemas, entities, and business logic
NestJS (framework)
Backend framework providing dependency injection, decorators, and modular architecture for the core system
GraphQL (framework)
API layer for both shop and admin interfaces with automatic schema generation and type safety
TypeORM (database)
Database ORM handling entity relationships, migrations, and multi-database support with decorators
Angular (framework)
Frontend framework for the mature admin-ui package providing reactive forms and component architecture
React (framework)
Frontend framework for the modern dashboard package with hooks and functional components
Lerna (build)
Monorepo management for coordinating builds, versioning, and publishing across 16 packages
Sharp (library)
High-performance image processing for asset transformations in the asset-server-plugin

Key Components

Package Structure

core (app)
The main e-commerce backend engine providing GraphQL APIs, entity management, order processing, and plugin architecture.
admin-ui (app)
Angular-based administrative interface for managing products, orders, customers, and store configuration.
dashboard (app)
React-based next-generation admin dashboard with extensible form components and modern UI patterns.
cli (tooling)
Command-line interface for project scaffolding, code generation, and development workflows.
common (shared)
Shared TypeScript types, utilities, and generated GraphQL types used across all packages.
admin-ui-plugin (library)
Plugin that serves the Angular admin-ui as part of the core server with metrics and dashboard capabilities.
asset-server-plugin (library)
Plugin for serving and transforming assets (images, files) with resize, crop, and storage strategies.
email-plugin (library)
Plugin for handling transactional emails with template generation, event handlers, and multiple sending strategies.
create (tooling)
Project scaffolding tool for creating new Vendure applications with database setup and initial configuration.
dev-server (tooling)
Development server setup with test plugins and example configurations for Vendure development.
testing (library)
Testing utilities and helpers for writing Vendure plugins and applications.
ui-devkit (library)
Development toolkit for building custom admin UI extensions and components.
job-queue-plugin (library)
Plugin providing asynchronous job processing capabilities with queue management.
harden-plugin (library)
Security hardening plugin for production deployments.
telemetry-plugin (library)
Plugin for collecting usage analytics and telemetry data.
graphiql-plugin (library)
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 CodeSea

Related 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 .