vendure-developing
Vendure Development
Purpose
Entry point for all Vendure development tasks. Provides quick reference and guides to the vendure-expert agent for coordinated multi-domain guidance.
When NOT to Use
- Non-Vendure e-commerce platforms (Shopify, Magento, etc.)
- Basic TypeScript/NestJS without Vendure context
- Frontend-only React without Vendure Admin UI
Quick Start: Use the Agent
For comprehensive Vendure guidance, use the vendure-expert agent:
Task(subagent_type: "vendure-expert", prompt: "Your Vendure task here")
The agent coordinates 9 specialized skills:
- Plugin development (writing + reviewing)
- GraphQL API (writing + reviewing)
- Entity/Database (writing + reviewing)
- Admin UI (writing + reviewing)
- Delivery/shipping features (specialized)
Vendure Architecture Quick Reference
6 Core Domains
| Domain | Key Concepts |
|---|---|
| Plugins | @VendurePlugin, NestJS DI, lifecycle hooks |
| GraphQL | Dual APIs (Shop/Admin), RequestContext, gql template |
| Entities | VendureEntity, TypeORM, migrations, custom fields |
| Admin UI | React/Angular, UI DevKit, lazy loading |
| Strategies | InjectableStrategy, custom logic |
| Events | EventBus, transaction-safe subscriptions |
Plugin Scaffold
import { PluginCommonModule, VendurePlugin } from "@vendure/core";
@VendurePlugin({
imports: [PluginCommonModule],
providers: [MyService],
entities: [MyEntity],
adminApiExtensions: {
schema: gql`...`,
resolvers: [MyResolver],
},
})
export class MyPlugin {
static init(options: MyPluginOptions) {
this.options = options;
return MyPlugin;
}
}
GraphQL Resolver Pattern
import { Ctx, RequestContext, Query, Resolver } from "@vendure/core";
import { Allow, Permission } from "@vendure/core";
@Resolver()
export class MyResolver {
constructor(private myService: MyService) {}
@Query()
@Allow(Permission.ReadSettings)
async myQuery(@Ctx() ctx: RequestContext): Promise<MyType[]> {
return this.myService.findAll(ctx);
}
}
Entity Pattern
import { VendureEntity, DeepPartial } from "@vendure/core";
import { Entity, Column, ManyToOne } from "typeorm";
@Entity()
export class MyEntity extends VendureEntity {
constructor(input?: DeepPartial<MyEntity>) {
super(input);
}
@Column()
name: string;
@ManyToOne(() => OtherEntity)
relation: OtherEntity;
}
Admin UI Extension
// providers.ts
import { addNavMenuSection } from "@vendure/admin-ui/react";
export default [
addNavMenuSection(
{
id: "my-section",
label: "My Feature",
items: [
{
id: "my-page",
label: "My Page",
routerLink: ["/extensions/my-feature"],
icon: "cog",
},
],
},
"settings",
),
];
FORBIDDEN Patterns
Plugin Development
- Missing @VendurePlugin decorator
- Not using NestJS DI (@Injectable)
- Hardcoded values instead of plugin config
- Direct database access bypassing services
GraphQL
- Missing @Ctx() RequestContext parameter
- Bypassing @Allow() permission decorator
- Mixing Shop/Admin schema types
- Not using gql template literal
Entities
- Not extending VendureEntity
- Missing @Entity() decorator
- No migration file created
- Using
anytype
Admin UI
- Not lazy loading routes
- Hardcoded strings (not using i18n)
- Missing loading/error states
- Not handling permissions
REQUIRED Patterns
RequestContext Threading
// ALWAYS pass ctx through service calls
async myResolver(@Ctx() ctx: RequestContext) {
return this.service.findAll(ctx); // Pass ctx!
}
Permission Decorators
@Query()
@Allow(Permission.ReadCatalog) // ALWAYS specify permissions
async products(@Ctx() ctx: RequestContext) { }
Entity Input Types
// Use DeepPartial for constructor input
constructor(input?: DeepPartial<MyEntity>) {
super(input);
}
InputMaybe Handling
// Check BOTH undefined AND null for GraphQL inputs
if (input.field !== undefined && input.field !== null) {
entity.field = input.field;
}
Domain Skill Decision Tree
Task Type
│
├─> Creating/modifying plugin structure
│ └─> vendure-plugin-writing
│
├─> Reviewing plugin code
│ └─> vendure-plugin-reviewing
│
├─> Extending GraphQL schema or resolvers
│ └─> vendure-graphql-writing
│
├─> Reviewing GraphQL code
│ └─> vendure-graphql-reviewing
│
├─> Creating/modifying entities
│ └─> vendure-entity-writing
│
├─> Reviewing entity definitions
│ └─> vendure-entity-reviewing
│
├─> Building Admin UI components
│ └─> vendure-admin-ui-writing
│
├─> Reviewing Admin UI code
│ └─> vendure-admin-ui-reviewing
│
└─> Delivery/shipping features
└─> vendure-delivery-plugin
Common Patterns
Dual API Separation
// Admin API - full access
export const graphqlAdminSchema = gql`
extend type Query {
myAdminQuery: [MyType!]!
}
extend type Mutation {
updateMyType(input: UpdateInput!): MyType!
}
`;
// Shop API - customer-facing, read-only or limited
export const graphqlShopSchema = gql`
extend type Query {
myPublicQuery: [MyType!]!
}
`;
Service Pattern with RequestContext
@Injectable()
export class MyService {
constructor(private connection: TransactionalConnection) {}
async findAll(ctx: RequestContext): Promise<MyEntity[]> {
return this.connection.getRepository(ctx, MyEntity).find();
}
async create(ctx: RequestContext, input: CreateInput): Promise<MyEntity> {
const entity = new MyEntity(input);
return this.connection.getRepository(ctx, MyEntity).save(entity);
}
}
Transaction Decorator
@Mutation()
@Transaction() // Wrap in database transaction
@Allow(Permission.UpdateSettings)
async updateMyEntity(
@Ctx() ctx: RequestContext,
@Args() { input }: { input: UpdateInput }
): Promise<MyEntity> {
return this.service.update(ctx, input);
}
Examples
Example 1: Create a Simple Plugin
Task: Create a plugin that tracks product views.
Approach:
- Use vendure-plugin-writing for plugin scaffold
- Use vendure-entity-writing for ProductView entity
- Use vendure-graphql-writing for query extension
Result:
@VendurePlugin({
imports: [PluginCommonModule],
entities: [ProductViewEntity],
providers: [ProductViewService],
shopApiExtensions: {
schema: gql`
extend type Query {
productViews(productId: ID!): Int!
}
`,
resolvers: [ProductViewResolver],
},
})
export class ProductViewsPlugin {}
Example 2: Add Admin UI Page
Task: Add a settings page to manage plugin configuration.
Approach:
- Use vendure-admin-ui-writing for React components
- Register route and navigation item
- Use DataService for API calls
Result:
// pages/SettingsPage.tsx
export function SettingsPage() {
const dataService = useInjector(DataService);
const [settings, setSettings] = useState<Settings>();
useEffect(() => {
dataService
.query(GetSettingsDocument)
.stream$.subscribe((result) => setSettings(result.mySettings));
}, []);
return <SettingsForm settings={settings} />;
}
Example 3: Review Plugin Code
Task: Audit a plugin for security and best practices.
Approach:
- Use vendure-plugin-reviewing for plugin structure
- Use vendure-graphql-reviewing for resolver patterns
- Use vendure-entity-reviewing for database patterns
Checks:
- RequestContext passed through all service calls
- Permissions declared on all resolvers
- No direct database queries (use TransactionalConnection)
- Proper error handling
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| Entity not found | Not in plugin entities array | Add to @VendurePlugin({ entities: [] }) |
| Resolver not called | Not in resolvers array | Add to apiExtensions.resolvers |
| Permission denied | Missing @Allow decorator | Add @Allow(Permission.X) |
| TypeScript errors | Wrong import path | Import from @vendure/core |
| Admin UI not loading | Not lazy loaded | Use React.lazy() for routes |
External Documentation
Context7 (Recommended)
mcp__context7__get-library-docs
context7CompatibleLibraryID: "/vendure-ecommerce/vendure"
topic: "plugins" or "entities" or "graphql" etc.
Official Docs
Related Agent
For comprehensive multi-domain Vendure guidance, use the vendure-expert agent.
The agent coordinates all 9 Vendure skills and provides:
- Full plugin lifecycle guidance
- Security and quality audits
- Best practices enforcement
- Domain-specific patterns
More from meriley/claude-code-skills
obs-cpp-qt-patterns
C++ and Qt integration patterns for OBS Studio plugins. Covers Qt6 Widgets for settings dialogs, CMAKE_AUTOMOC, OBS frontend API, optional Qt builds with C fallbacks, and modal dialog patterns. Use when adding UI components or C++ features to OBS plugins.
56vendure-admin-ui-writing
Create Vendure Admin UI extensions with React components, route registration, navigation menus, and GraphQL integration. Handles useQuery, useMutation, useInjector patterns. Use when building Admin UI features for Vendure plugins.
33vendure-entity-writing
Define Vendure database entities extending VendureEntity, with TypeORM decorators, relations, custom fields, and channel-awareness. Use when creating database models in Vendure.
31vendure-graphql-writing
Extend Vendure GraphQL schema with custom types, queries, mutations, and resolvers. Handles RequestContext threading, permissions, and dual Shop/Admin API separation. Use when adding GraphQL endpoints to Vendure.
31vendure-plugin-writing
Create production-ready Vendure plugins with @VendurePlugin decorator, NestJS dependency injection, lifecycle hooks, and configuration patterns. Use when developing new Vendure plugins or extending existing ones.
29vendure-admin-ui-reviewing
Review Vendure Admin UI extensions for React pattern violations, missing hooks, improper state management, and UI anti-patterns. Use when reviewing Admin UI PRs or auditing UI quality.
26