@vendure/docs 0.0.0-202601161541

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (968) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/manifest.d.ts +3 -0
  6. package/dist/manifest.d.ts.map +1 -0
  7. package/dist/manifest.js +857 -0
  8. package/dist/manifest.js.map +1 -0
  9. package/docs/guides/core-concepts/auth/admin-role.webp +0 -0
  10. package/docs/guides/core-concepts/auth/admin.webp +0 -0
  11. package/docs/guides/core-concepts/auth/customer.webp +0 -0
  12. package/docs/guides/core-concepts/auth/index.md +557 -0
  13. package/docs/guides/core-concepts/auth/roles.webp +0 -0
  14. package/docs/guides/core-concepts/channels/channel-token.webp +0 -0
  15. package/docs/guides/core-concepts/channels/channels.webp +0 -0
  16. package/docs/guides/core-concepts/channels/channels_currencies_diagram.png +0 -0
  17. package/docs/guides/core-concepts/channels/channels_diagram.png +0 -0
  18. package/docs/guides/core-concepts/channels/channels_prices_diagram.png +0 -0
  19. package/docs/guides/core-concepts/channels/default-currency.webp +0 -0
  20. package/docs/guides/core-concepts/channels/index.md +154 -0
  21. package/docs/guides/core-concepts/channels/variant-prices.webp +0 -0
  22. package/docs/guides/core-concepts/collections/collection-filters.webp +0 -0
  23. package/docs/guides/core-concepts/collections/collections.webp +0 -0
  24. package/docs/guides/core-concepts/collections/filter-inheritance.webp +0 -0
  25. package/docs/guides/core-concepts/collections/index.mdx +125 -0
  26. package/docs/guides/core-concepts/customers/customer.webp +0 -0
  27. package/docs/guides/core-concepts/customers/index.mdx +21 -0
  28. package/docs/guides/core-concepts/email/email-plugin-flow.webp +0 -0
  29. package/docs/guides/core-concepts/email/index.mdx +155 -0
  30. package/docs/guides/core-concepts/images-assets/asset-flow.webp +0 -0
  31. package/docs/guides/core-concepts/images-assets/asset-tags.webp +0 -0
  32. package/docs/guides/core-concepts/images-assets/index.mdx +42 -0
  33. package/docs/guides/core-concepts/money/index.mdx +246 -0
  34. package/docs/guides/core-concepts/orders/custom-order-ui.webp +0 -0
  35. package/docs/guides/core-concepts/orders/index.md +366 -0
  36. package/docs/guides/core-concepts/orders/order-process.webp +0 -0
  37. package/docs/guides/core-concepts/orders/order.webp +0 -0
  38. package/docs/guides/core-concepts/payment/index.md +410 -0
  39. package/docs/guides/core-concepts/payment/payment-method.webp +0 -0
  40. package/docs/guides/core-concepts/payment/payment_sequence_one_step.png +0 -0
  41. package/docs/guides/core-concepts/payment/payment_sequence_two_step.png +0 -0
  42. package/docs/guides/core-concepts/products/facets.webp +0 -0
  43. package/docs/guides/core-concepts/products/index.mdx +59 -0
  44. package/docs/guides/core-concepts/products/product-relations.webp +0 -0
  45. package/docs/guides/core-concepts/products/products-variants.webp +0 -0
  46. package/docs/guides/core-concepts/promotions/index.md +324 -0
  47. package/docs/guides/core-concepts/shipping/index.md +225 -0
  48. package/docs/guides/core-concepts/shipping/shipping-method.webp +0 -0
  49. package/docs/guides/core-concepts/stock-control/global-stock-control.webp +0 -0
  50. package/docs/guides/core-concepts/stock-control/index.md +233 -0
  51. package/docs/guides/core-concepts/stock-control/stock-levels.webp +0 -0
  52. package/docs/guides/core-concepts/taxes/index.mdx +76 -0
  53. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/01-create-space.webp +0 -0
  54. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/02-space-access-keys.webp +0 -0
  55. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/03-create-app.webp +0 -0
  56. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/04-configure-server.webp +0 -0
  57. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/05-open-server-settings.webp +0 -0
  58. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/06-admin-app-route.webp +0 -0
  59. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/07-open-app.webp +0 -0
  60. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/deploy-to-do-app-platform.webp +0 -0
  61. package/docs/guides/deployment/deploy-to-digital-ocean-app-platform/index.md +246 -0
  62. package/docs/guides/deployment/deploy-to-google-cloud-run/deploy-to-gcr.webp +0 -0
  63. package/docs/guides/deployment/deploy-to-google-cloud-run/index.md +53 -0
  64. package/docs/guides/deployment/deploy-to-northflank/01-create-template-screen.webp +0 -0
  65. package/docs/guides/deployment/deploy-to-northflank/02-paste-config.webp +0 -0
  66. package/docs/guides/deployment/deploy-to-northflank/03-run-template.webp +0 -0
  67. package/docs/guides/deployment/deploy-to-northflank/04-find-project.webp +0 -0
  68. package/docs/guides/deployment/deploy-to-northflank/05-server-service.webp +0 -0
  69. package/docs/guides/deployment/deploy-to-northflank/06-find-url.webp +0 -0
  70. package/docs/guides/deployment/deploy-to-northflank/deploy-to-northflank.webp +0 -0
  71. package/docs/guides/deployment/deploy-to-northflank/index.md +606 -0
  72. package/docs/guides/deployment/deploy-to-railway/01-new-service.webp +0 -0
  73. package/docs/guides/deployment/deploy-to-railway/02-env-vars.webp +0 -0
  74. package/docs/guides/deployment/deploy-to-railway/03-test-server.webp +0 -0
  75. package/docs/guides/deployment/deploy-to-railway/deploy-to-railway.webp +0 -0
  76. package/docs/guides/deployment/deploy-to-railway/index.md +202 -0
  77. package/docs/guides/deployment/deploy-to-render/01-create-db.webp +0 -0
  78. package/docs/guides/deployment/deploy-to-render/02-env-group.webp +0 -0
  79. package/docs/guides/deployment/deploy-to-render/03-db-connection.webp +0 -0
  80. package/docs/guides/deployment/deploy-to-render/04-link-env-group.webp +0 -0
  81. package/docs/guides/deployment/deploy-to-render/05-server-url.webp +0 -0
  82. package/docs/guides/deployment/deploy-to-render/deploy-to-render.webp +0 -0
  83. package/docs/guides/deployment/deploy-to-render/index.md +221 -0
  84. package/docs/guides/deployment/deploying-admin-ui.md +130 -0
  85. package/docs/guides/deployment/getting-data-into-production.md +50 -0
  86. package/docs/guides/deployment/horizontal-scaling.md +75 -0
  87. package/docs/guides/deployment/production-configuration/env-var-ui.webp +0 -0
  88. package/docs/guides/deployment/production-configuration/index.md +140 -0
  89. package/docs/guides/deployment/server-resource-requirements.md +29 -0
  90. package/docs/guides/deployment/using-docker.md +193 -0
  91. package/docs/guides/developer-guide/cache/cache-service.webp +0 -0
  92. package/docs/guides/developer-guide/cache/index.mdx +327 -0
  93. package/docs/guides/developer-guide/channel-aware/index.md +117 -0
  94. package/docs/guides/developer-guide/cli/add-command.webp +0 -0
  95. package/docs/guides/developer-guide/cli/index.md +418 -0
  96. package/docs/guides/developer-guide/cli/migrate-command.webp +0 -0
  97. package/docs/guides/developer-guide/cli/schema-command.webp +0 -0
  98. package/docs/guides/developer-guide/configuration/index.md +226 -0
  99. package/docs/guides/developer-guide/custom-fields/custom-fields-data-table.webp +0 -0
  100. package/docs/guides/developer-guide/custom-fields/custom-fields-ui.webp +0 -0
  101. package/docs/guides/developer-guide/custom-fields/index.md +1423 -0
  102. package/docs/guides/developer-guide/custom-permissions/index.md +186 -0
  103. package/docs/guides/developer-guide/custom-strategies-in-plugins/index.mdx +467 -0
  104. package/docs/guides/developer-guide/database-entity/index.md +136 -0
  105. package/docs/guides/developer-guide/dataloaders/index.md +137 -0
  106. package/docs/guides/developer-guide/db-subscribers/index.md +115 -0
  107. package/docs/guides/developer-guide/error-handling/index.mdx +324 -0
  108. package/docs/guides/developer-guide/events/index.mdx +406 -0
  109. package/docs/guides/developer-guide/extend-graphql-api/index.md +501 -0
  110. package/docs/guides/developer-guide/has-custom-fields/index.md +174 -0
  111. package/docs/guides/developer-guide/importing-data/index.md +488 -0
  112. package/docs/guides/developer-guide/importing-data/reindex.webp +0 -0
  113. package/docs/guides/developer-guide/logging/index.md +77 -0
  114. package/docs/guides/developer-guide/migrating-from-v1/breaking-api-changes.md +213 -0
  115. package/docs/guides/developer-guide/migrating-from-v1/database-migration.md +39 -0
  116. package/docs/guides/developer-guide/migrating-from-v1/index.md +41 -0
  117. package/docs/guides/developer-guide/migrating-from-v1/storefront-migration.md +30 -0
  118. package/docs/guides/developer-guide/migrations/index.md +197 -0
  119. package/docs/guides/developer-guide/migrations/migration.webp +0 -0
  120. package/docs/guides/developer-guide/nest-devtools/index.md +74 -0
  121. package/docs/guides/developer-guide/nest-devtools/nest-devtools-bootstrap-perf.webp +0 -0
  122. package/docs/guides/developer-guide/nest-devtools/nest-devtools-graph.webp +0 -0
  123. package/docs/guides/developer-guide/overview/Vendure_docs-architecture.webp +0 -0
  124. package/docs/guides/developer-guide/overview/index.md +40 -0
  125. package/docs/guides/developer-guide/plugins/index.mdx +806 -0
  126. package/docs/guides/developer-guide/rest-endpoint/index.md +99 -0
  127. package/docs/guides/developer-guide/scheduled-tasks/index.md +298 -0
  128. package/docs/guides/developer-guide/security/index.md +264 -0
  129. package/docs/guides/developer-guide/settings-store/index.mdx +553 -0
  130. package/docs/guides/developer-guide/stand-alone-scripts/index.md +119 -0
  131. package/docs/guides/developer-guide/strategies-configurable-operations/collection-filters-args.webp +0 -0
  132. package/docs/guides/developer-guide/strategies-configurable-operations/collection-filters.webp +0 -0
  133. package/docs/guides/developer-guide/strategies-configurable-operations/index.mdx +373 -0
  134. package/docs/guides/developer-guide/testing/index.md +254 -0
  135. package/docs/guides/developer-guide/the-api-layer/Vendure_docs-api_request.webp +0 -0
  136. package/docs/guides/developer-guide/the-api-layer/index.mdx +474 -0
  137. package/docs/guides/developer-guide/the-service-layer/index.mdx +311 -0
  138. package/docs/guides/developer-guide/translatable/index.md +224 -0
  139. package/docs/guides/developer-guide/translations/index.md +165 -0
  140. package/docs/guides/developer-guide/updating/index.md +79 -0
  141. package/docs/guides/developer-guide/uploading-files/index.md +220 -0
  142. package/docs/guides/developer-guide/worker-job-queue/Vendure_docs-job-queue-2.webp +0 -0
  143. package/docs/guides/developer-guide/worker-job-queue/Vendure_docs-job-queue-3.webp +0 -0
  144. package/docs/guides/developer-guide/worker-job-queue/Vendure_docs-job-queue.webp +0 -0
  145. package/docs/guides/developer-guide/worker-job-queue/index.mdx +522 -0
  146. package/docs/guides/developer-guide/worker-job-queue/worker-job-queue.webp +0 -0
  147. package/docs/guides/extending-the-admin-ui/add-actions-to-pages/index.md +233 -0
  148. package/docs/guides/extending-the-admin-ui/add-actions-to-pages/ui-extensions-actionbar-dropdown.webp +0 -0
  149. package/docs/guides/extending-the-admin-ui/add-actions-to-pages/ui-extensions-actionbar.webp +0 -0
  150. package/docs/guides/extending-the-admin-ui/adding-ui-translations/index.md +113 -0
  151. package/docs/guides/extending-the-admin-ui/adding-ui-translations/ui-translations-01.webp +0 -0
  152. package/docs/guides/extending-the-admin-ui/admin-ui-theming-branding/index.md +133 -0
  153. package/docs/guides/extending-the-admin-ui/alerts/alerts-01.webp +0 -0
  154. package/docs/guides/extending-the-admin-ui/alerts/index.md +56 -0
  155. package/docs/guides/extending-the-admin-ui/bulk-actions/bulk-actions-screenshot.webp +0 -0
  156. package/docs/guides/extending-the-admin-ui/bulk-actions/index.md +78 -0
  157. package/docs/guides/extending-the-admin-ui/creating-detail-views/index.md +332 -0
  158. package/docs/guides/extending-the-admin-ui/creating-list-views/index.md +331 -0
  159. package/docs/guides/extending-the-admin-ui/custom-data-table-components/custom-data-table-location.webp +0 -0
  160. package/docs/guides/extending-the-admin-ui/custom-data-table-components/custom-data-table.webp +0 -0
  161. package/docs/guides/extending-the-admin-ui/custom-data-table-components/index.md +111 -0
  162. package/docs/guides/extending-the-admin-ui/custom-detail-components/detail-component.webp +0 -0
  163. package/docs/guides/extending-the-admin-ui/custom-detail-components/index.md +198 -0
  164. package/docs/guides/extending-the-admin-ui/custom-form-inputs/index.md +285 -0
  165. package/docs/guides/extending-the-admin-ui/custom-form-inputs/ui-extensions-custom-field-default.webp +0 -0
  166. package/docs/guides/extending-the-admin-ui/custom-form-inputs/ui-extensions-custom-field-slider.webp +0 -0
  167. package/docs/guides/extending-the-admin-ui/custom-timeline-components/index.md +79 -0
  168. package/docs/guides/extending-the-admin-ui/custom-timeline-components/timeline-entry.webp +0 -0
  169. package/docs/guides/extending-the-admin-ui/dashboard-widgets/dashboard-widgets.webp +0 -0
  170. package/docs/guides/extending-the-admin-ui/dashboard-widgets/index.md +149 -0
  171. package/docs/guides/extending-the-admin-ui/defining-routes/index.md +777 -0
  172. package/docs/guides/extending-the-admin-ui/defining-routes/route-area.webp +0 -0
  173. package/docs/guides/extending-the-admin-ui/defining-routes/ui-extensions-greeter.webp +0 -0
  174. package/docs/guides/extending-the-admin-ui/getting-started/index.md +656 -0
  175. package/docs/guides/extending-the-admin-ui/getting-started/provider-extension-points.webp +0 -0
  176. package/docs/guides/extending-the-admin-ui/nav-menu/index.md +97 -0
  177. package/docs/guides/extending-the-admin-ui/nav-menu/nav-menu-id.webp +0 -0
  178. package/docs/guides/extending-the-admin-ui/nav-menu/ui-extensions-navbar.webp +0 -0
  179. package/docs/guides/extending-the-admin-ui/page-tabs/index.md +44 -0
  180. package/docs/guides/extending-the-admin-ui/page-tabs/ui-extensions-tabs.webp +0 -0
  181. package/docs/guides/extending-the-admin-ui/ui-library/buttons.webp +0 -0
  182. package/docs/guides/extending-the-admin-ui/ui-library/card.webp +0 -0
  183. package/docs/guides/extending-the-admin-ui/ui-library/form-inputs.webp +0 -0
  184. package/docs/guides/extending-the-admin-ui/ui-library/icons.webp +0 -0
  185. package/docs/guides/extending-the-admin-ui/ui-library/index.md +356 -0
  186. package/docs/guides/extending-the-admin-ui/ui-library/layout.webp +0 -0
  187. package/docs/guides/extending-the-admin-ui/using-other-frameworks/index.md +205 -0
  188. package/docs/guides/extending-the-admin-ui/using-other-frameworks/ui-extensions-cra.jpg +0 -0
  189. package/docs/guides/extending-the-dashboard/alerts/alert.webp +0 -0
  190. package/docs/guides/extending-the-dashboard/alerts/index.md +89 -0
  191. package/docs/guides/extending-the-dashboard/creating-pages/detail-pages.md +316 -0
  192. package/docs/guides/extending-the-dashboard/creating-pages/index.md +90 -0
  193. package/docs/guides/extending-the-dashboard/creating-pages/list-pages.md +206 -0
  194. package/docs/guides/extending-the-dashboard/creating-pages/tabbed-page-example.webp +0 -0
  195. package/docs/guides/extending-the-dashboard/creating-pages/tabbed-pages.md +144 -0
  196. package/docs/guides/extending-the-dashboard/custom-form-components/color-picker.webp +0 -0
  197. package/docs/guides/extending-the-dashboard/custom-form-components/dev-mode.webp +0 -0
  198. package/docs/guides/extending-the-dashboard/custom-form-components/example-currency-input.webp +0 -0
  199. package/docs/guides/extending-the-dashboard/custom-form-components/example-email-input.webp +0 -0
  200. package/docs/guides/extending-the-dashboard/custom-form-components/example-slug-input.webp +0 -0
  201. package/docs/guides/extending-the-dashboard/custom-form-components/example-tags-input.webp +0 -0
  202. package/docs/guides/extending-the-dashboard/custom-form-components/form-component-examples.mdx +445 -0
  203. package/docs/guides/extending-the-dashboard/custom-form-components/index.md +465 -0
  204. package/docs/guides/extending-the-dashboard/custom-form-components/locator.webp +0 -0
  205. package/docs/guides/extending-the-dashboard/custom-form-components/relation-selectors.md +687 -0
  206. package/docs/guides/extending-the-dashboard/customizing-pages/action-bar-button.webp +0 -0
  207. package/docs/guides/extending-the-dashboard/customizing-pages/action-bar-dropdown.webp +0 -0
  208. package/docs/guides/extending-the-dashboard/customizing-pages/action-bar-items.md +272 -0
  209. package/docs/guides/extending-the-dashboard/customizing-pages/custom-widget.webp +0 -0
  210. package/docs/guides/extending-the-dashboard/customizing-pages/customizing-detail-pages.md +129 -0
  211. package/docs/guides/extending-the-dashboard/customizing-pages/customizing-list-pages.md +93 -0
  212. package/docs/guides/extending-the-dashboard/customizing-pages/customizing-login-page.md +66 -0
  213. package/docs/guides/extending-the-dashboard/customizing-pages/history-entries.md +43 -0
  214. package/docs/guides/extending-the-dashboard/customizing-pages/history-entry.webp +0 -0
  215. package/docs/guides/extending-the-dashboard/customizing-pages/index.md +10 -0
  216. package/docs/guides/extending-the-dashboard/customizing-pages/insights-widgets.md +57 -0
  217. package/docs/guides/extending-the-dashboard/customizing-pages/login-page.webp +0 -0
  218. package/docs/guides/extending-the-dashboard/customizing-pages/page-blocks.md +244 -0
  219. package/docs/guides/extending-the-dashboard/data-fetching/index.md +126 -0
  220. package/docs/guides/extending-the-dashboard/data-fetching/type-inference.webp +0 -0
  221. package/docs/guides/extending-the-dashboard/deployment/index.md +200 -0
  222. package/docs/guides/extending-the-dashboard/extending-overview/dev-mode.webp +0 -0
  223. package/docs/guides/extending-the-dashboard/extending-overview/index.md +228 -0
  224. package/docs/guides/extending-the-dashboard/extending-overview/location-id.webp +0 -0
  225. package/docs/guides/extending-the-dashboard/getting-started/detail-view.webp +0 -0
  226. package/docs/guides/extending-the-dashboard/getting-started/index.md +194 -0
  227. package/docs/guides/extending-the-dashboard/getting-started/list-view-empty.webp +0 -0
  228. package/docs/guides/extending-the-dashboard/getting-started/list-view-full.webp +0 -0
  229. package/docs/guides/extending-the-dashboard/getting-started/page-block.webp +0 -0
  230. package/docs/guides/extending-the-dashboard/getting-started/test-page.webp +0 -0
  231. package/docs/guides/extending-the-dashboard/localization/index.md +94 -0
  232. package/docs/guides/extending-the-dashboard/migration/index.md +1902 -0
  233. package/docs/guides/extending-the-dashboard/navigation/dev-mode-nav.webp +0 -0
  234. package/docs/guides/extending-the-dashboard/navigation/index.md +322 -0
  235. package/docs/guides/extending-the-dashboard/navigation/unauthenticated-page.webp +0 -0
  236. package/docs/guides/extending-the-dashboard/tech-stack/index.md +395 -0
  237. package/docs/guides/extending-the-dashboard/theming/index.md +180 -0
  238. package/docs/guides/extending-the-dashboard/theming/show-colour-value-inspection.gif +0 -0
  239. package/docs/guides/getting-started/graphql-intro/index.mdx +572 -0
  240. package/docs/guides/getting-started/installation/app-screens.webp +0 -0
  241. package/docs/guides/getting-started/installation/index.md +234 -0
  242. package/docs/guides/getting-started/try-the-api/graphiql-docs.jpeg +0 -0
  243. package/docs/guides/getting-started/try-the-api/graphiql.jpeg +0 -0
  244. package/docs/guides/getting-started/try-the-api/index.mdx +238 -0
  245. package/docs/guides/how-to/cms-integration-plugin/index.mdx +2083 -0
  246. package/docs/guides/how-to/codegen/index.md +280 -0
  247. package/docs/guides/how-to/configurable-products/index.md +138 -0
  248. package/docs/guides/how-to/digital-products/index.mdx +485 -0
  249. package/docs/guides/how-to/digital-products/product-variant.webp +0 -0
  250. package/docs/guides/how-to/digital-products/shipping-method.webp +0 -0
  251. package/docs/guides/how-to/github-oauth-authentication/index.mdx +360 -0
  252. package/docs/guides/how-to/google-oauth-authentication/index.mdx +494 -0
  253. package/docs/guides/how-to/multi-vendor-marketplaces/aggregate-order.webp +0 -0
  254. package/docs/guides/how-to/multi-vendor-marketplaces/index.md +197 -0
  255. package/docs/guides/how-to/paginated-list/index.mdx +360 -0
  256. package/docs/guides/how-to/publish-plugin/index.mdx +402 -0
  257. package/docs/guides/how-to/s3-asset-storage/index.mdx +572 -0
  258. package/docs/guides/how-to/telemetry/grafana-logs.webp +0 -0
  259. package/docs/guides/how-to/telemetry/grafana-trace.webp +0 -0
  260. package/docs/guides/how-to/telemetry/index.md +260 -0
  261. package/docs/guides/how-to/telemetry/jaeger-trace.webp +0 -0
  262. package/docs/guides/storefront/active-order/index.mdx +201 -0
  263. package/docs/guides/storefront/checkout-flow/index.mdx +428 -0
  264. package/docs/guides/storefront/codegen/index.mdx +157 -0
  265. package/docs/guides/storefront/connect-api/index.mdx +677 -0
  266. package/docs/guides/storefront/customer-accounts/index.mdx +389 -0
  267. package/docs/guides/storefront/customer-accounts/pw-reset.webp +0 -0
  268. package/docs/guides/storefront/customer-accounts/verification.webp +0 -0
  269. package/docs/guides/storefront/listing-products/index.mdx +711 -0
  270. package/docs/guides/storefront/navigation-menu/index.mdx +157 -0
  271. package/docs/guides/storefront/order-workflow/index.md +231 -0
  272. package/docs/guides/storefront/order-workflow/order_class_diagram.png +0 -0
  273. package/docs/guides/storefront/order-workflow/order_state_diagram.png +0 -0
  274. package/docs/guides/storefront/product-detail/index.mdx +295 -0
  275. package/docs/guides/storefront/storefront-starters/angular-storefront.webp +0 -0
  276. package/docs/guides/storefront/storefront-starters/index.mdx +69 -0
  277. package/docs/guides/storefront/storefront-starters/next-storefront.webp +0 -0
  278. package/docs/guides/storefront/storefront-starters/qwik-storefront.webp +0 -0
  279. package/docs/guides/storefront/storefront-starters/remix-storefront.webp +0 -0
  280. package/docs/reference/admin-ui-api/action-bar/action-bar-context.md +82 -0
  281. package/docs/reference/admin-ui-api/action-bar/action-bar-dropdown-menu-item.md +93 -0
  282. package/docs/reference/admin-ui-api/action-bar/action-bar-item.md +106 -0
  283. package/docs/reference/admin-ui-api/action-bar/action-bar-location-id.md +17 -0
  284. package/docs/reference/admin-ui-api/action-bar/add-action-bar-dropdown-menu-item.md +38 -0
  285. package/docs/reference/admin-ui-api/action-bar/add-action-bar-item.md +36 -0
  286. package/docs/reference/admin-ui-api/action-bar/index.md +5 -0
  287. package/docs/reference/admin-ui-api/action-bar/page-location-id.md +61 -0
  288. package/docs/reference/admin-ui-api/action-bar/router-link-definition.md +16 -0
  289. package/docs/reference/admin-ui-api/alerts/alert-config.md +88 -0
  290. package/docs/reference/admin-ui-api/alerts/alert-context.md +52 -0
  291. package/docs/reference/admin-ui-api/alerts/index.md +5 -0
  292. package/docs/reference/admin-ui-api/alerts/register-alert.md +23 -0
  293. package/docs/reference/admin-ui-api/bulk-actions/bulk-action.md +229 -0
  294. package/docs/reference/admin-ui-api/bulk-actions/index.md +5 -0
  295. package/docs/reference/admin-ui-api/bulk-actions/register-bulk-action.md +63 -0
  296. package/docs/reference/admin-ui-api/components/asset-picker-dialog-component.md +160 -0
  297. package/docs/reference/admin-ui-api/components/chip-component.md +63 -0
  298. package/docs/reference/admin-ui-api/components/currency-input-component.md +174 -0
  299. package/docs/reference/admin-ui-api/components/data-table-component.md +238 -0
  300. package/docs/reference/admin-ui-api/components/data-table2component.md +354 -0
  301. package/docs/reference/admin-ui-api/components/datetime-picker-component.md +262 -0
  302. package/docs/reference/admin-ui-api/components/dropdown-component.md +86 -0
  303. package/docs/reference/admin-ui-api/components/facet-value-selector-component.md +155 -0
  304. package/docs/reference/admin-ui-api/components/index.md +5 -0
  305. package/docs/reference/admin-ui-api/components/object-tree-component.md +86 -0
  306. package/docs/reference/admin-ui-api/components/order-state-label-component.md +41 -0
  307. package/docs/reference/admin-ui-api/components/product-variant-selector-component.md +75 -0
  308. package/docs/reference/admin-ui-api/components/rich-text-editor-component.md +108 -0
  309. package/docs/reference/admin-ui-api/components/zone-selector-component.md +124 -0
  310. package/docs/reference/admin-ui-api/custom-detail-components/custom-detail-component-config.md +41 -0
  311. package/docs/reference/admin-ui-api/custom-detail-components/custom-detail-component-location-id.md +36 -0
  312. package/docs/reference/admin-ui-api/custom-detail-components/custom-detail-component.md +36 -0
  313. package/docs/reference/admin-ui-api/custom-detail-components/index.md +5 -0
  314. package/docs/reference/admin-ui-api/custom-detail-components/register-custom-detail-component.md +69 -0
  315. package/docs/reference/admin-ui-api/custom-history-entry-components/customer-history-entry-component.md +32 -0
  316. package/docs/reference/admin-ui-api/custom-history-entry-components/history-entry-component.md +57 -0
  317. package/docs/reference/admin-ui-api/custom-history-entry-components/history-entry-config.md +35 -0
  318. package/docs/reference/admin-ui-api/custom-history-entry-components/index.md +5 -0
  319. package/docs/reference/admin-ui-api/custom-history-entry-components/order-history-entry-component.md +32 -0
  320. package/docs/reference/admin-ui-api/custom-history-entry-components/register-history-entry-component.md +79 -0
  321. package/docs/reference/admin-ui-api/custom-input-components/default-inputs.md +1034 -0
  322. package/docs/reference/admin-ui-api/custom-input-components/form-input-component.md +56 -0
  323. package/docs/reference/admin-ui-api/custom-input-components/index.md +5 -0
  324. package/docs/reference/admin-ui-api/custom-input-components/register-form-input-component.md +71 -0
  325. package/docs/reference/admin-ui-api/custom-table-components/custom-column-component.md +32 -0
  326. package/docs/reference/admin-ui-api/custom-table-components/data-table-component-config.md +48 -0
  327. package/docs/reference/admin-ui-api/custom-table-components/index.md +5 -0
  328. package/docs/reference/admin-ui-api/custom-table-components/register-data-table-component.md +55 -0
  329. package/docs/reference/admin-ui-api/dashboard-widgets/dashboard-widget-config.md +60 -0
  330. package/docs/reference/admin-ui-api/dashboard-widgets/index.md +5 -0
  331. package/docs/reference/admin-ui-api/dashboard-widgets/register-dashboard-widget.md +27 -0
  332. package/docs/reference/admin-ui-api/dashboard-widgets/set-dashboard-widget-layout.md +22 -0
  333. package/docs/reference/admin-ui-api/dashboard-widgets/widget-layout-definition.md +16 -0
  334. package/docs/reference/admin-ui-api/directives/if-multichannel-directive.md +41 -0
  335. package/docs/reference/admin-ui-api/directives/if-permissions-directive.md +43 -0
  336. package/docs/reference/admin-ui-api/directives/index.md +5 -0
  337. package/docs/reference/admin-ui-api/index.md +13 -0
  338. package/docs/reference/admin-ui-api/list-detail-views/base-detail-component.md +168 -0
  339. package/docs/reference/admin-ui-api/list-detail-views/base-entity-resolver.md +54 -0
  340. package/docs/reference/admin-ui-api/list-detail-views/base-list-component.md +155 -0
  341. package/docs/reference/admin-ui-api/list-detail-views/detail-component-with-resolver.md +52 -0
  342. package/docs/reference/admin-ui-api/list-detail-views/index.md +5 -0
  343. package/docs/reference/admin-ui-api/list-detail-views/typed-base-detail-component.md +51 -0
  344. package/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md +125 -0
  345. package/docs/reference/admin-ui-api/nav-menu/add-nav-menu-item.md +52 -0
  346. package/docs/reference/admin-ui-api/nav-menu/add-nav-menu-section.md +47 -0
  347. package/docs/reference/admin-ui-api/nav-menu/index.md +5 -0
  348. package/docs/reference/admin-ui-api/nav-menu/nav-menu-item.md +66 -0
  349. package/docs/reference/admin-ui-api/nav-menu/nav-menu-section.md +83 -0
  350. package/docs/reference/admin-ui-api/nav-menu/navigation-types.md +38 -0
  351. package/docs/reference/admin-ui-api/pipes/asset-preview-pipe.md +41 -0
  352. package/docs/reference/admin-ui-api/pipes/duration-pipe.md +45 -0
  353. package/docs/reference/admin-ui-api/pipes/file-size-pipe.md +38 -0
  354. package/docs/reference/admin-ui-api/pipes/has-permission-pipe.md +51 -0
  355. package/docs/reference/admin-ui-api/pipes/index.md +5 -0
  356. package/docs/reference/admin-ui-api/pipes/locale-currency-name-pipe.md +47 -0
  357. package/docs/reference/admin-ui-api/pipes/locale-currency-pipe.md +54 -0
  358. package/docs/reference/admin-ui-api/pipes/locale-date-pipe.md +48 -0
  359. package/docs/reference/admin-ui-api/pipes/locale-language-name-pipe.md +47 -0
  360. package/docs/reference/admin-ui-api/pipes/locale-region-name-pipe.md +47 -0
  361. package/docs/reference/admin-ui-api/pipes/time-ago-pipe.md +44 -0
  362. package/docs/reference/admin-ui-api/react-components/action-bar.md +36 -0
  363. package/docs/reference/admin-ui-api/react-components/card.md +36 -0
  364. package/docs/reference/admin-ui-api/react-components/cds-icon.md +34 -0
  365. package/docs/reference/admin-ui-api/react-components/form-field.md +42 -0
  366. package/docs/reference/admin-ui-api/react-components/index.md +5 -0
  367. package/docs/reference/admin-ui-api/react-components/link.md +34 -0
  368. package/docs/reference/admin-ui-api/react-components/page-block.md +36 -0
  369. package/docs/reference/admin-ui-api/react-components/page-detail-layout.md +36 -0
  370. package/docs/reference/admin-ui-api/react-components/rich-text-editor.md +42 -0
  371. package/docs/reference/admin-ui-api/react-extensions/index.md +5 -0
  372. package/docs/reference/admin-ui-api/react-extensions/react-custom-detail-component-config.md +41 -0
  373. package/docs/reference/admin-ui-api/react-extensions/react-data-table-component-config.md +48 -0
  374. package/docs/reference/admin-ui-api/react-extensions/register-react-custom-detail-component.md +23 -0
  375. package/docs/reference/admin-ui-api/react-extensions/register-react-data-table-component.md +55 -0
  376. package/docs/reference/admin-ui-api/react-extensions/register-react-form-input-component.md +26 -0
  377. package/docs/reference/admin-ui-api/react-extensions/register-react-route-component-options.md +18 -0
  378. package/docs/reference/admin-ui-api/react-extensions/register-react-route-component.md +22 -0
  379. package/docs/reference/admin-ui-api/react-hooks/index.md +5 -0
  380. package/docs/reference/admin-ui-api/react-hooks/use-detail-component-data.md +39 -0
  381. package/docs/reference/admin-ui-api/react-hooks/use-form-control.md +36 -0
  382. package/docs/reference/admin-ui-api/react-hooks/use-injector.md +39 -0
  383. package/docs/reference/admin-ui-api/react-hooks/use-lazy-query.md +74 -0
  384. package/docs/reference/admin-ui-api/react-hooks/use-mutation.md +62 -0
  385. package/docs/reference/admin-ui-api/react-hooks/use-page-metadata.md +36 -0
  386. package/docs/reference/admin-ui-api/react-hooks/use-query.md +59 -0
  387. package/docs/reference/admin-ui-api/react-hooks/use-rich-text-editor.md +30 -0
  388. package/docs/reference/admin-ui-api/react-hooks/use-route-params.md +29 -0
  389. package/docs/reference/admin-ui-api/routes/index.md +5 -0
  390. package/docs/reference/admin-ui-api/routes/register-route-component-options.md +28 -0
  391. package/docs/reference/admin-ui-api/routes/register-route-component.md +57 -0
  392. package/docs/reference/admin-ui-api/services/data-service.md +142 -0
  393. package/docs/reference/admin-ui-api/services/index.md +5 -0
  394. package/docs/reference/admin-ui-api/services/modal-service.md +199 -0
  395. package/docs/reference/admin-ui-api/services/notification-service.md +126 -0
  396. package/docs/reference/admin-ui-api/tabs/index.md +5 -0
  397. package/docs/reference/admin-ui-api/tabs/page-tab-config.md +70 -0
  398. package/docs/reference/admin-ui-api/tabs/register-page-tab.md +38 -0
  399. package/docs/reference/admin-ui-api/ui-devkit/admin-ui-extension.md +417 -0
  400. package/docs/reference/admin-ui-api/ui-devkit/compile-ui-extensions.md +22 -0
  401. package/docs/reference/admin-ui-api/ui-devkit/helpers.md +39 -0
  402. package/docs/reference/admin-ui-api/ui-devkit/index.md +5 -0
  403. package/docs/reference/admin-ui-api/ui-devkit/ui-devkit-client.md +160 -0
  404. package/docs/reference/admin-ui-api/ui-devkit/ui-extension-build-command.md +16 -0
  405. package/docs/reference/admin-ui-api/ui-devkit/ui-extension-compiler-options.md +118 -0
  406. package/docs/reference/admin-ui-api/ui-devkit/ui-extension-compiler-process-argument.md +16 -0
  407. package/docs/reference/core-plugins/admin-ui-plugin/admin-ui-plugin-options.md +67 -0
  408. package/docs/reference/core-plugins/admin-ui-plugin/index.md +94 -0
  409. package/docs/reference/core-plugins/asset-server-plugin/asset-server-options.md +106 -0
  410. package/docs/reference/core-plugins/asset-server-plugin/cache-config.md +36 -0
  411. package/docs/reference/core-plugins/asset-server-plugin/hashed-asset-naming-strategy.md +47 -0
  412. package/docs/reference/core-plugins/asset-server-plugin/image-transform-mode.md +21 -0
  413. package/docs/reference/core-plugins/asset-server-plugin/image-transform-preset.md +57 -0
  414. package/docs/reference/core-plugins/asset-server-plugin/image-transform-strategy.md +144 -0
  415. package/docs/reference/core-plugins/asset-server-plugin/index.md +189 -0
  416. package/docs/reference/core-plugins/asset-server-plugin/local-asset-storage-strategy.md +74 -0
  417. package/docs/reference/core-plugins/asset-server-plugin/preset-only-strategy.md +118 -0
  418. package/docs/reference/core-plugins/asset-server-plugin/s3asset-storage-strategy.md +225 -0
  419. package/docs/reference/core-plugins/asset-server-plugin/sharp-asset-preview-strategy.md +118 -0
  420. package/docs/reference/core-plugins/dashboard-plugin/dashboard-plugin-options.md +43 -0
  421. package/docs/reference/core-plugins/dashboard-plugin/index.md +106 -0
  422. package/docs/reference/core-plugins/elasticsearch-plugin/elasticsearch-options.md +695 -0
  423. package/docs/reference/core-plugins/elasticsearch-plugin/index.md +193 -0
  424. package/docs/reference/core-plugins/email-plugin/email-event-handler-with-async-data.md +33 -0
  425. package/docs/reference/core-plugins/email-plugin/email-event-handler.md +299 -0
  426. package/docs/reference/core-plugins/email-plugin/email-event-listener.md +42 -0
  427. package/docs/reference/core-plugins/email-plugin/email-generator.md +78 -0
  428. package/docs/reference/core-plugins/email-plugin/email-plugin-options.md +154 -0
  429. package/docs/reference/core-plugins/email-plugin/email-plugin-types.md +276 -0
  430. package/docs/reference/core-plugins/email-plugin/email-send-event.md +34 -0
  431. package/docs/reference/core-plugins/email-plugin/email-sender.md +92 -0
  432. package/docs/reference/core-plugins/email-plugin/email-utils.md +54 -0
  433. package/docs/reference/core-plugins/email-plugin/index.md +309 -0
  434. package/docs/reference/core-plugins/email-plugin/template-loader.md +96 -0
  435. package/docs/reference/core-plugins/email-plugin/transport-options.md +241 -0
  436. package/docs/reference/core-plugins/graphiql-plugin/index.md +89 -0
  437. package/docs/reference/core-plugins/harden-plugin/default-vendure-complexity-estimator.md +30 -0
  438. package/docs/reference/core-plugins/harden-plugin/harden-plugin-options.md +106 -0
  439. package/docs/reference/core-plugins/harden-plugin/index.md +169 -0
  440. package/docs/reference/core-plugins/index.md +5 -0
  441. package/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-plugin.md +211 -0
  442. package/docs/reference/core-plugins/job-queue-plugin/bull-mqjob-queue-strategy.md +93 -0
  443. package/docs/reference/core-plugins/job-queue-plugin/bull-mqplugin-options.md +142 -0
  444. package/docs/reference/core-plugins/job-queue-plugin/index.md +5 -0
  445. package/docs/reference/core-plugins/job-queue-plugin/pub-sub-job-queue-strategy.md +65 -0
  446. package/docs/reference/core-plugins/job-queue-plugin/pub-sub-plugin.md +36 -0
  447. package/docs/reference/core-plugins/payments-plugin/braintree-plugin.md +350 -0
  448. package/docs/reference/core-plugins/payments-plugin/index.md +5 -0
  449. package/docs/reference/core-plugins/payments-plugin/mollie-plugin.md +209 -0
  450. package/docs/reference/core-plugins/payments-plugin/stripe-plugin.md +355 -0
  451. package/docs/reference/core-plugins/sentry-plugin/index.md +158 -0
  452. package/docs/reference/core-plugins/sentry-plugin/sentry-plugin-options.md +32 -0
  453. package/docs/reference/core-plugins/sentry-plugin/sentry-service.md +47 -0
  454. package/docs/reference/core-plugins/stellate-plugin/index.md +277 -0
  455. package/docs/reference/core-plugins/stellate-plugin/purge-rule.md +91 -0
  456. package/docs/reference/core-plugins/stellate-plugin/stellate-plugin-options.md +65 -0
  457. package/docs/reference/core-plugins/stellate-plugin/stellate-service.md +71 -0
  458. package/docs/reference/core-plugins/telemetry-plugin/get-sdk-configuration.md +92 -0
  459. package/docs/reference/core-plugins/telemetry-plugin/index.md +131 -0
  460. package/docs/reference/core-plugins/telemetry-plugin/otel-logger.md +102 -0
  461. package/docs/reference/core-plugins/telemetry-plugin/register-method-hooks.md +46 -0
  462. package/docs/reference/core-plugins/telemetry-plugin/telemetry-plugin-options.md +77 -0
  463. package/docs/reference/dashboard/components/asset-gallery.md +127 -0
  464. package/docs/reference/dashboard/components/asset-picker-dialog.md +76 -0
  465. package/docs/reference/dashboard/components/channel-chip.md +22 -0
  466. package/docs/reference/dashboard/components/detail-page-button.md +49 -0
  467. package/docs/reference/dashboard/components/facet-value-chip.md +22 -0
  468. package/docs/reference/dashboard/components/facet-value-selector.md +85 -0
  469. package/docs/reference/dashboard/components/index.md +5 -0
  470. package/docs/reference/dashboard/components/permission-guard.md +63 -0
  471. package/docs/reference/dashboard/components/vendure-image.md +199 -0
  472. package/docs/reference/dashboard/detail-views/detail-page.md +91 -0
  473. package/docs/reference/dashboard/detail-views/index.md +5 -0
  474. package/docs/reference/dashboard/detail-views/use-detail-page.md +234 -0
  475. package/docs/reference/dashboard/detail-views/use-generated-form.md +105 -0
  476. package/docs/reference/dashboard/extensions-api/action-bar.md +66 -0
  477. package/docs/reference/dashboard/extensions-api/alerts.md +78 -0
  478. package/docs/reference/dashboard/extensions-api/data-tables.md +55 -0
  479. package/docs/reference/dashboard/extensions-api/define-dashboard-extension.md +142 -0
  480. package/docs/reference/dashboard/extensions-api/detail-forms.md +81 -0
  481. package/docs/reference/dashboard/extensions-api/form-components.md +188 -0
  482. package/docs/reference/dashboard/extensions-api/history-entries.md +246 -0
  483. package/docs/reference/dashboard/extensions-api/index.md +5 -0
  484. package/docs/reference/dashboard/extensions-api/login.md +113 -0
  485. package/docs/reference/dashboard/extensions-api/navigation.md +128 -0
  486. package/docs/reference/dashboard/extensions-api/page-blocks.md +143 -0
  487. package/docs/reference/dashboard/extensions-api/routes.md +69 -0
  488. package/docs/reference/dashboard/extensions-api/widgets.md +61 -0
  489. package/docs/reference/dashboard/form-components/affixed-input.md +34 -0
  490. package/docs/reference/dashboard/form-components/boolean-input.md +22 -0
  491. package/docs/reference/dashboard/form-components/checkbox-input.md +22 -0
  492. package/docs/reference/dashboard/form-components/date-time-input.md +22 -0
  493. package/docs/reference/dashboard/form-components/form-field-wrapper.md +80 -0
  494. package/docs/reference/dashboard/form-components/index.md +5 -0
  495. package/docs/reference/dashboard/form-components/money-input.md +23 -0
  496. package/docs/reference/dashboard/form-components/number-input.md +22 -0
  497. package/docs/reference/dashboard/form-components/password-input.md +22 -0
  498. package/docs/reference/dashboard/form-components/rich-text-input.md +22 -0
  499. package/docs/reference/dashboard/form-components/slug-input.md +57 -0
  500. package/docs/reference/dashboard/form-components/text-input.md +13 -0
  501. package/docs/reference/dashboard/form-components/textarea-input.md +22 -0
  502. package/docs/reference/dashboard/form-components/translatable-form-field-wrapper.md +70 -0
  503. package/docs/reference/dashboard/hooks/index.md +5 -0
  504. package/docs/reference/dashboard/hooks/use-alerts.md +65 -0
  505. package/docs/reference/dashboard/hooks/use-auth.md +84 -0
  506. package/docs/reference/dashboard/hooks/use-channel.md +72 -0
  507. package/docs/reference/dashboard/hooks/use-custom-field-config.md +24 -0
  508. package/docs/reference/dashboard/hooks/use-display-locale.md +32 -0
  509. package/docs/reference/dashboard/hooks/use-drag-and-drop.md +22 -0
  510. package/docs/reference/dashboard/hooks/use-local-format.md +30 -0
  511. package/docs/reference/dashboard/hooks/use-paginated-list.md +29 -0
  512. package/docs/reference/dashboard/hooks/use-permissions.md +25 -0
  513. package/docs/reference/dashboard/hooks/use-sorted-languages.md +30 -0
  514. package/docs/reference/dashboard/hooks/use-ui-language-loader.md +18 -0
  515. package/docs/reference/dashboard/hooks/use-widget-filters.md +16 -0
  516. package/docs/reference/dashboard/list-views/bulk-actions.md +199 -0
  517. package/docs/reference/dashboard/list-views/data-table-cell-component.md +45 -0
  518. package/docs/reference/dashboard/list-views/data-table.md +209 -0
  519. package/docs/reference/dashboard/list-views/index.md +5 -0
  520. package/docs/reference/dashboard/list-views/list-page.md +493 -0
  521. package/docs/reference/dashboard/list-views/paginated-list-data-table.md +303 -0
  522. package/docs/reference/dashboard/page-layout/index.md +54 -0
  523. package/docs/reference/dashboard/page-layout/page-action-bar.md +62 -0
  524. package/docs/reference/dashboard/page-layout/page-block.md +137 -0
  525. package/docs/reference/dashboard/page-layout/page-title.md +22 -0
  526. package/docs/reference/dashboard/page-layout/page.md +100 -0
  527. package/docs/reference/dashboard/page-layout/use-page-block.md +32 -0
  528. package/docs/reference/dashboard/vite-plugin/index.md +5 -0
  529. package/docs/reference/dashboard/vite-plugin/vendure-dashboard-plugin.md +356 -0
  530. package/docs/reference/graphql-api/_index.md +16 -0
  531. package/docs/reference/graphql-api/admin/_index.md +13 -0
  532. package/docs/reference/graphql-api/admin/enums.md +1142 -0
  533. package/docs/reference/graphql-api/admin/input-types.md +4631 -0
  534. package/docs/reference/graphql-api/admin/mutations.md +1985 -0
  535. package/docs/reference/graphql-api/admin/object-types.md +4515 -0
  536. package/docs/reference/graphql-api/admin/queries.md +760 -0
  537. package/docs/reference/graphql-api/shop/_index.md +13 -0
  538. package/docs/reference/graphql-api/shop/enums.md +1072 -0
  539. package/docs/reference/graphql-api/shop/input-types.md +1192 -0
  540. package/docs/reference/graphql-api/shop/mutations.md +431 -0
  541. package/docs/reference/graphql-api/shop/object-types.md +3406 -0
  542. package/docs/reference/graphql-api/shop/queries.md +247 -0
  543. package/docs/reference/index.mdx +38 -0
  544. package/docs/reference/links.webp +0 -0
  545. package/docs/reference/typescript-api/_index.md +13 -0
  546. package/docs/reference/typescript-api/assets/asset-naming-strategy.md +56 -0
  547. package/docs/reference/typescript-api/assets/asset-options.md +58 -0
  548. package/docs/reference/typescript-api/assets/asset-preview-strategy.md +45 -0
  549. package/docs/reference/typescript-api/assets/asset-storage-strategy.md +84 -0
  550. package/docs/reference/typescript-api/assets/default-asset-naming-strategy.md +39 -0
  551. package/docs/reference/typescript-api/assets/index.md +5 -0
  552. package/docs/reference/typescript-api/auth/auth-options.md +165 -0
  553. package/docs/reference/typescript-api/auth/authentication-strategy.md +97 -0
  554. package/docs/reference/typescript-api/auth/bcrypt-password-hashing-strategy.md +38 -0
  555. package/docs/reference/typescript-api/auth/cookie-options.md +108 -0
  556. package/docs/reference/typescript-api/auth/default-password-validation-strategy.md +44 -0
  557. package/docs/reference/typescript-api/auth/default-session-cache-strategy.md +73 -0
  558. package/docs/reference/typescript-api/auth/default-verification-token-strategy.md +47 -0
  559. package/docs/reference/typescript-api/auth/external-authentication-service.md +98 -0
  560. package/docs/reference/typescript-api/auth/in-memory-session-cache-strategy.md +63 -0
  561. package/docs/reference/typescript-api/auth/index.md +5 -0
  562. package/docs/reference/typescript-api/auth/native-authentication-strategy.md +58 -0
  563. package/docs/reference/typescript-api/auth/noop-session-cache-strategy.md +51 -0
  564. package/docs/reference/typescript-api/auth/password-hashing-strategy.md +45 -0
  565. package/docs/reference/typescript-api/auth/password-validation-strategy.md +43 -0
  566. package/docs/reference/typescript-api/auth/permission-definition.md +269 -0
  567. package/docs/reference/typescript-api/auth/session-cache-strategy.md +275 -0
  568. package/docs/reference/typescript-api/auth/superadmin-credentials.md +36 -0
  569. package/docs/reference/typescript-api/auth/verification-token-strategy.md +45 -0
  570. package/docs/reference/typescript-api/cache/cache-config.md +44 -0
  571. package/docs/reference/typescript-api/cache/cache-service.md +76 -0
  572. package/docs/reference/typescript-api/cache/cache-strategy.md +103 -0
  573. package/docs/reference/typescript-api/cache/default-cache-plugin.md +73 -0
  574. package/docs/reference/typescript-api/cache/index.md +79 -0
  575. package/docs/reference/typescript-api/cache/redis-cache-plugin.md +85 -0
  576. package/docs/reference/typescript-api/cache/redis-cache-strategy.md +75 -0
  577. package/docs/reference/typescript-api/cache/request-context-cache-service.md +54 -0
  578. package/docs/reference/typescript-api/cache/self-refreshing-cache.md +152 -0
  579. package/docs/reference/typescript-api/cache/sql-cache-strategy.md +87 -0
  580. package/docs/reference/typescript-api/common/admin-ui/admin-ui-app-config.md +43 -0
  581. package/docs/reference/typescript-api/common/admin-ui/admin-ui-app-dev-mode-config.md +47 -0
  582. package/docs/reference/typescript-api/common/admin-ui/admin-ui-config.md +139 -0
  583. package/docs/reference/typescript-api/common/admin-ui/index.md +5 -0
  584. package/docs/reference/typescript-api/common/async-queue.md +38 -0
  585. package/docs/reference/typescript-api/common/bootstrap.md +129 -0
  586. package/docs/reference/typescript-api/common/currency-code.md +174 -0
  587. package/docs/reference/typescript-api/common/entity-relation-paths.md +34 -0
  588. package/docs/reference/typescript-api/common/i18n-service.md +74 -0
  589. package/docs/reference/typescript-api/common/id.md +17 -0
  590. package/docs/reference/typescript-api/common/index.md +5 -0
  591. package/docs/reference/typescript-api/common/injectable-strategy.md +48 -0
  592. package/docs/reference/typescript-api/common/injector.md +47 -0
  593. package/docs/reference/typescript-api/common/job-state.md +23 -0
  594. package/docs/reference/typescript-api/common/json-compatible.md +23 -0
  595. package/docs/reference/typescript-api/common/language-code.md +177 -0
  596. package/docs/reference/typescript-api/common/middleware.md +72 -0
  597. package/docs/reference/typescript-api/common/paginated-list.md +36 -0
  598. package/docs/reference/typescript-api/common/permission.md +136 -0
  599. package/docs/reference/typescript-api/common/price-calculation-result.md +36 -0
  600. package/docs/reference/typescript-api/common/process-context.md +55 -0
  601. package/docs/reference/typescript-api/common/vendure_version.md +22 -0
  602. package/docs/reference/typescript-api/configurable-operation-def/config-arg-type.md +22 -0
  603. package/docs/reference/typescript-api/configurable-operation-def/config-args.md +89 -0
  604. package/docs/reference/typescript-api/configurable-operation-def/configurable-operation-def-options.md +57 -0
  605. package/docs/reference/typescript-api/configurable-operation-def/default-form-component-id.md +34 -0
  606. package/docs/reference/typescript-api/configurable-operation-def/default-form-config-hash.md +137 -0
  607. package/docs/reference/typescript-api/configurable-operation-def/index.md +149 -0
  608. package/docs/reference/typescript-api/configurable-operation-def/localized-string-array.md +28 -0
  609. package/docs/reference/typescript-api/configuration/api-options.md +159 -0
  610. package/docs/reference/typescript-api/configuration/collection-filter.md +76 -0
  611. package/docs/reference/typescript-api/configuration/default-config.md +13 -0
  612. package/docs/reference/typescript-api/configuration/default-slug-strategy.md +46 -0
  613. package/docs/reference/typescript-api/configuration/entity-duplicator.md +195 -0
  614. package/docs/reference/typescript-api/configuration/entity-id-decorator.md +24 -0
  615. package/docs/reference/typescript-api/configuration/entity-id-strategy.md +168 -0
  616. package/docs/reference/typescript-api/configuration/entity-options.md +153 -0
  617. package/docs/reference/typescript-api/configuration/index.md +5 -0
  618. package/docs/reference/typescript-api/configuration/merge-config.md +49 -0
  619. package/docs/reference/typescript-api/configuration/product-variant-price-selection-strategy.md +71 -0
  620. package/docs/reference/typescript-api/configuration/product-variant-price-update-strategy.md +185 -0
  621. package/docs/reference/typescript-api/configuration/runtime-vendure-config.md +111 -0
  622. package/docs/reference/typescript-api/configuration/settings-store-fields.md +29 -0
  623. package/docs/reference/typescript-api/configuration/slug-strategy.md +47 -0
  624. package/docs/reference/typescript-api/configuration/system-options.md +50 -0
  625. package/docs/reference/typescript-api/configuration/trust-proxy-options.md +19 -0
  626. package/docs/reference/typescript-api/configuration/vendure-config.md +168 -0
  627. package/docs/reference/typescript-api/custom-fields/custom-field-config.md +25 -0
  628. package/docs/reference/typescript-api/custom-fields/custom-field-type.md +42 -0
  629. package/docs/reference/typescript-api/custom-fields/index.md +70 -0
  630. package/docs/reference/typescript-api/custom-fields/struct-custom-field-config.md +21 -0
  631. package/docs/reference/typescript-api/custom-fields/struct-field-config.md +41 -0
  632. package/docs/reference/typescript-api/custom-fields/typed-custom-single-field-config.md +24 -0
  633. package/docs/reference/typescript-api/data-access/calculated-property-subscriber.md +38 -0
  634. package/docs/reference/typescript-api/data-access/calculated.md +62 -0
  635. package/docs/reference/typescript-api/data-access/entity-hydrator.md +103 -0
  636. package/docs/reference/typescript-api/data-access/get-entity-or-throw-options.md +53 -0
  637. package/docs/reference/typescript-api/data-access/hydrate-options.md +40 -0
  638. package/docs/reference/typescript-api/data-access/index.md +5 -0
  639. package/docs/reference/typescript-api/data-access/list-query-builder.md +239 -0
  640. package/docs/reference/typescript-api/data-access/transactional-connection.md +173 -0
  641. package/docs/reference/typescript-api/default-search-plugin/default-search-plugin-init-options.md +126 -0
  642. package/docs/reference/typescript-api/default-search-plugin/index.md +64 -0
  643. package/docs/reference/typescript-api/default-search-plugin/mysql-search-strategy.md +56 -0
  644. package/docs/reference/typescript-api/default-search-plugin/postgres-search-strategy.md +56 -0
  645. package/docs/reference/typescript-api/default-search-plugin/search-strategy.md +58 -0
  646. package/docs/reference/typescript-api/default-search-plugin/sqlite-search-strategy.md +57 -0
  647. package/docs/reference/typescript-api/entities/address.md +124 -0
  648. package/docs/reference/typescript-api/entities/administrator.md +78 -0
  649. package/docs/reference/typescript-api/entities/anonymous-session.md +34 -0
  650. package/docs/reference/typescript-api/entities/asset.md +135 -0
  651. package/docs/reference/typescript-api/entities/authenticated-session.md +48 -0
  652. package/docs/reference/typescript-api/entities/authentication-method.md +156 -0
  653. package/docs/reference/typescript-api/entities/channel.md +233 -0
  654. package/docs/reference/typescript-api/entities/collection.md +152 -0
  655. package/docs/reference/typescript-api/entities/country.md +40 -0
  656. package/docs/reference/typescript-api/entities/customer-group.md +63 -0
  657. package/docs/reference/typescript-api/entities/customer-history-entry.md +40 -0
  658. package/docs/reference/typescript-api/entities/customer.md +122 -0
  659. package/docs/reference/typescript-api/entities/facet-value.md +98 -0
  660. package/docs/reference/typescript-api/entities/facet.md +89 -0
  661. package/docs/reference/typescript-api/entities/fulfillment.md +84 -0
  662. package/docs/reference/typescript-api/entities/global-settings.md +66 -0
  663. package/docs/reference/typescript-api/entities/history-entry.md +66 -0
  664. package/docs/reference/typescript-api/entities/index.md +5 -0
  665. package/docs/reference/typescript-api/entities/interfaces.md +125 -0
  666. package/docs/reference/typescript-api/entities/order-history-entry.md +40 -0
  667. package/docs/reference/typescript-api/entities/order-line-reference.md +174 -0
  668. package/docs/reference/typescript-api/entities/order-line.md +344 -0
  669. package/docs/reference/typescript-api/entities/order-modification.md +103 -0
  670. package/docs/reference/typescript-api/entities/order.md +284 -0
  671. package/docs/reference/typescript-api/entities/orderable-asset.md +61 -0
  672. package/docs/reference/typescript-api/entities/payment-method.md +94 -0
  673. package/docs/reference/typescript-api/entities/payment.md +96 -0
  674. package/docs/reference/typescript-api/entities/product-option-group.md +84 -0
  675. package/docs/reference/typescript-api/entities/product-option.md +90 -0
  676. package/docs/reference/typescript-api/entities/product-variant-price.md +70 -0
  677. package/docs/reference/typescript-api/entities/product-variant.md +251 -0
  678. package/docs/reference/typescript-api/entities/product.md +134 -0
  679. package/docs/reference/typescript-api/entities/promotion.md +190 -0
  680. package/docs/reference/typescript-api/entities/province.md +39 -0
  681. package/docs/reference/typescript-api/entities/refund.md +120 -0
  682. package/docs/reference/typescript-api/entities/region.md +87 -0
  683. package/docs/reference/typescript-api/entities/role.md +62 -0
  684. package/docs/reference/typescript-api/entities/seller.md +63 -0
  685. package/docs/reference/typescript-api/entities/session.md +87 -0
  686. package/docs/reference/typescript-api/entities/settings-store-entry.md +57 -0
  687. package/docs/reference/typescript-api/entities/shipping-line.md +150 -0
  688. package/docs/reference/typescript-api/entities/shipping-method.md +123 -0
  689. package/docs/reference/typescript-api/entities/stock-level.md +87 -0
  690. package/docs/reference/typescript-api/entities/stock-location.md +76 -0
  691. package/docs/reference/typescript-api/entities/stock-movement.md +270 -0
  692. package/docs/reference/typescript-api/entities/surcharge.md +102 -0
  693. package/docs/reference/typescript-api/entities/tag.md +40 -0
  694. package/docs/reference/typescript-api/entities/tax-category.md +68 -0
  695. package/docs/reference/typescript-api/entities/tax-rate.md +138 -0
  696. package/docs/reference/typescript-api/entities/user.md +111 -0
  697. package/docs/reference/typescript-api/entities/vendure-entity.md +49 -0
  698. package/docs/reference/typescript-api/entities/zone.md +78 -0
  699. package/docs/reference/typescript-api/errors/error-handler-strategy.md +88 -0
  700. package/docs/reference/typescript-api/errors/error-result-union.md +30 -0
  701. package/docs/reference/typescript-api/errors/error-types.md +197 -0
  702. package/docs/reference/typescript-api/errors/i18n-error.md +39 -0
  703. package/docs/reference/typescript-api/errors/index.md +5 -0
  704. package/docs/reference/typescript-api/errors/is-graph-ql-error-result.md +40 -0
  705. package/docs/reference/typescript-api/events/blocking-event-handler-options.md +57 -0
  706. package/docs/reference/typescript-api/events/event-bus.md +136 -0
  707. package/docs/reference/typescript-api/events/event-types.md +1689 -0
  708. package/docs/reference/typescript-api/events/index.md +5 -0
  709. package/docs/reference/typescript-api/events/vendure-entity-event.md +57 -0
  710. package/docs/reference/typescript-api/events/vendure-event.md +35 -0
  711. package/docs/reference/typescript-api/fulfillment/fulfillment-handler.md +173 -0
  712. package/docs/reference/typescript-api/fulfillment/fulfillment-process.md +71 -0
  713. package/docs/reference/typescript-api/fulfillment/fulfillment-state.md +21 -0
  714. package/docs/reference/typescript-api/fulfillment/fulfillment-states.md +18 -0
  715. package/docs/reference/typescript-api/fulfillment/fulfillment-transition-data.md +41 -0
  716. package/docs/reference/typescript-api/fulfillment/index.md +5 -0
  717. package/docs/reference/typescript-api/health-check/health-check-registry-service.md +70 -0
  718. package/docs/reference/typescript-api/health-check/health-check-strategy.md +70 -0
  719. package/docs/reference/typescript-api/health-check/http-health-check-strategy.md +61 -0
  720. package/docs/reference/typescript-api/health-check/index.md +5 -0
  721. package/docs/reference/typescript-api/health-check/type-ormhealth-check-strategy.md +64 -0
  722. package/docs/reference/typescript-api/import-export/asset-import-strategy.md +46 -0
  723. package/docs/reference/typescript-api/import-export/asset-importer.md +32 -0
  724. package/docs/reference/typescript-api/import-export/default-asset-import-strategy.md +48 -0
  725. package/docs/reference/typescript-api/import-export/fast-importer-service.md +67 -0
  726. package/docs/reference/typescript-api/import-export/import-export-options.md +36 -0
  727. package/docs/reference/typescript-api/import-export/import-parser.md +280 -0
  728. package/docs/reference/typescript-api/import-export/importer.md +54 -0
  729. package/docs/reference/typescript-api/import-export/index.md +5 -0
  730. package/docs/reference/typescript-api/import-export/initial-data.md +71 -0
  731. package/docs/reference/typescript-api/import-export/populate.md +71 -0
  732. package/docs/reference/typescript-api/import-export/populator.md +38 -0
  733. package/docs/reference/typescript-api/job-queue/default-job-queue-plugin.md +245 -0
  734. package/docs/reference/typescript-api/job-queue/in-memory-job-buffer-storage-strategy.md +55 -0
  735. package/docs/reference/typescript-api/job-queue/in-memory-job-queue-strategy.md +110 -0
  736. package/docs/reference/typescript-api/job-queue/index.md +84 -0
  737. package/docs/reference/typescript-api/job-queue/inspectable-job-queue-strategy.md +60 -0
  738. package/docs/reference/typescript-api/job-queue/job-buffer-storage-strategy.md +78 -0
  739. package/docs/reference/typescript-api/job-queue/job-buffer.md +119 -0
  740. package/docs/reference/typescript-api/job-queue/job-queue-options.md +56 -0
  741. package/docs/reference/typescript-api/job-queue/job-queue-service.md +128 -0
  742. package/docs/reference/typescript-api/job-queue/job-queue-strategy.md +59 -0
  743. package/docs/reference/typescript-api/job-queue/job.md +195 -0
  744. package/docs/reference/typescript-api/job-queue/polling-job-queue-strategy.md +122 -0
  745. package/docs/reference/typescript-api/job-queue/sql-job-queue-strategy.md +84 -0
  746. package/docs/reference/typescript-api/job-queue/subscribable-job.md +44 -0
  747. package/docs/reference/typescript-api/job-queue/types.md +199 -0
  748. package/docs/reference/typescript-api/logger/default-logger.md +81 -0
  749. package/docs/reference/typescript-api/logger/index.md +120 -0
  750. package/docs/reference/typescript-api/logger/log-level.md +30 -0
  751. package/docs/reference/typescript-api/logger/vendure-logger.md +60 -0
  752. package/docs/reference/typescript-api/migration/generate-migration.md +28 -0
  753. package/docs/reference/typescript-api/migration/index.md +5 -0
  754. package/docs/reference/typescript-api/migration/migration-options.md +36 -0
  755. package/docs/reference/typescript-api/migration/revert-last-migration.md +23 -0
  756. package/docs/reference/typescript-api/migration/run-migrations.md +23 -0
  757. package/docs/reference/typescript-api/money/big-int-money-strategy.md +68 -0
  758. package/docs/reference/typescript-api/money/default-money-strategy.md +47 -0
  759. package/docs/reference/typescript-api/money/index.md +5 -0
  760. package/docs/reference/typescript-api/money/money-decorator.md +23 -0
  761. package/docs/reference/typescript-api/money/money-strategy.md +116 -0
  762. package/docs/reference/typescript-api/money/round-money.md +26 -0
  763. package/docs/reference/typescript-api/orders/active-order-service.md +69 -0
  764. package/docs/reference/typescript-api/orders/active-order-strategy.md +203 -0
  765. package/docs/reference/typescript-api/orders/changed-price-handling-strategy.md +51 -0
  766. package/docs/reference/typescript-api/orders/custom-order-states.md +18 -0
  767. package/docs/reference/typescript-api/orders/default-active-order-strategy.md +52 -0
  768. package/docs/reference/typescript-api/orders/default-guest-checkout-strategy.md +93 -0
  769. package/docs/reference/typescript-api/orders/default-order-item-price-calculation-strategy.md +33 -0
  770. package/docs/reference/typescript-api/orders/default-order-placed-strategy.md +33 -0
  771. package/docs/reference/typescript-api/orders/default-stock-allocation-strategy.md +33 -0
  772. package/docs/reference/typescript-api/orders/guest-checkout-strategy.md +56 -0
  773. package/docs/reference/typescript-api/orders/index.md +5 -0
  774. package/docs/reference/typescript-api/orders/merge-strategies.md +114 -0
  775. package/docs/reference/typescript-api/orders/order-by-code-access-strategy.md +91 -0
  776. package/docs/reference/typescript-api/orders/order-code-strategy.md +88 -0
  777. package/docs/reference/typescript-api/orders/order-interceptor.md +250 -0
  778. package/docs/reference/typescript-api/orders/order-item-price-calculation-strategy.md +91 -0
  779. package/docs/reference/typescript-api/orders/order-merge-strategy.md +80 -0
  780. package/docs/reference/typescript-api/orders/order-options.md +145 -0
  781. package/docs/reference/typescript-api/orders/order-placed-strategy.md +54 -0
  782. package/docs/reference/typescript-api/orders/order-process.md +271 -0
  783. package/docs/reference/typescript-api/orders/order-seller-strategy.md +124 -0
  784. package/docs/reference/typescript-api/orders/stock-allocation-strategy.md +46 -0
  785. package/docs/reference/typescript-api/payment/default-payment-process.md +13 -0
  786. package/docs/reference/typescript-api/payment/default-refund-process.md +13 -0
  787. package/docs/reference/typescript-api/payment/dummy-payment-handler.md +31 -0
  788. package/docs/reference/typescript-api/payment/index.md +5 -0
  789. package/docs/reference/typescript-api/payment/payment-method-config-options.md +69 -0
  790. package/docs/reference/typescript-api/payment/payment-method-eligibility-checker.md +95 -0
  791. package/docs/reference/typescript-api/payment/payment-method-handler.md +83 -0
  792. package/docs/reference/typescript-api/payment/payment-method-types.md +386 -0
  793. package/docs/reference/typescript-api/payment/payment-options.md +57 -0
  794. package/docs/reference/typescript-api/payment/payment-process.md +62 -0
  795. package/docs/reference/typescript-api/payment/payment-state.md +20 -0
  796. package/docs/reference/typescript-api/payment/payment-states.md +18 -0
  797. package/docs/reference/typescript-api/payment/payment-transition-data.md +42 -0
  798. package/docs/reference/typescript-api/payment/refund-process.md +55 -0
  799. package/docs/reference/typescript-api/payment/refund-state.md +16 -0
  800. package/docs/reference/typescript-api/payment/refund-states.md +18 -0
  801. package/docs/reference/typescript-api/payment/refund-transition-data.md +41 -0
  802. package/docs/reference/typescript-api/plugin/index.md +5 -0
  803. package/docs/reference/typescript-api/plugin/plugin-common-module.md +28 -0
  804. package/docs/reference/typescript-api/plugin/plugin-utilities.md +96 -0
  805. package/docs/reference/typescript-api/plugin/vendure-plugin-metadata.md +147 -0
  806. package/docs/reference/typescript-api/plugin/vendure-plugin.md +52 -0
  807. package/docs/reference/typescript-api/products-stock/catalog-options.md +70 -0
  808. package/docs/reference/typescript-api/products-stock/default-product-variant-price-calculation-strategy.md +38 -0
  809. package/docs/reference/typescript-api/products-stock/default-stock-display-strategy.md +40 -0
  810. package/docs/reference/typescript-api/products-stock/default-stock-location-strategy.md +47 -0
  811. package/docs/reference/typescript-api/products-stock/index.md +5 -0
  812. package/docs/reference/typescript-api/products-stock/multi-channel-stock-location-strategy.md +47 -0
  813. package/docs/reference/typescript-api/products-stock/product-variant-price-calculation-strategy.md +95 -0
  814. package/docs/reference/typescript-api/products-stock/stock-display-strategy.md +47 -0
  815. package/docs/reference/typescript-api/products-stock/stock-location-strategy.md +157 -0
  816. package/docs/reference/typescript-api/promotions/facet-value-checker.md +78 -0
  817. package/docs/reference/typescript-api/promotions/index.md +5 -0
  818. package/docs/reference/typescript-api/promotions/promotion-action.md +454 -0
  819. package/docs/reference/typescript-api/promotions/promotion-condition.md +117 -0
  820. package/docs/reference/typescript-api/promotions/promotion-options.md +35 -0
  821. package/docs/reference/typescript-api/request/allow-decorator.md +53 -0
  822. package/docs/reference/typescript-api/request/api-decorator.md +28 -0
  823. package/docs/reference/typescript-api/request/api-type.md +16 -0
  824. package/docs/reference/typescript-api/request/ctx-decorator.md +23 -0
  825. package/docs/reference/typescript-api/request/index.md +5 -0
  826. package/docs/reference/typescript-api/request/relations-decorator.md +116 -0
  827. package/docs/reference/typescript-api/request/request-context-service.md +47 -0
  828. package/docs/reference/typescript-api/request/request-context.md +205 -0
  829. package/docs/reference/typescript-api/request/transaction-decorator.md +82 -0
  830. package/docs/reference/typescript-api/scheduled-tasks/clean-sessions-task.md +40 -0
  831. package/docs/reference/typescript-api/scheduled-tasks/default-scheduler-plugin.md +86 -0
  832. package/docs/reference/typescript-api/scheduled-tasks/default-scheduler-strategy.md +76 -0
  833. package/docs/reference/typescript-api/scheduled-tasks/index.md +5 -0
  834. package/docs/reference/typescript-api/scheduled-tasks/scheduled-task.md +196 -0
  835. package/docs/reference/typescript-api/scheduled-tasks/scheduler-options.md +47 -0
  836. package/docs/reference/typescript-api/scheduled-tasks/scheduler-service.md +62 -0
  837. package/docs/reference/typescript-api/scheduled-tasks/scheduler-strategy.md +141 -0
  838. package/docs/reference/typescript-api/service-helpers/entity-duplicator-service.md +43 -0
  839. package/docs/reference/typescript-api/service-helpers/index.md +5 -0
  840. package/docs/reference/typescript-api/service-helpers/order-calculator.md +54 -0
  841. package/docs/reference/typescript-api/service-helpers/order-modifier.md +91 -0
  842. package/docs/reference/typescript-api/service-helpers/product-price-applicator.md +62 -0
  843. package/docs/reference/typescript-api/service-helpers/slug-validator.md +86 -0
  844. package/docs/reference/typescript-api/service-helpers/translatable-saver.md +66 -0
  845. package/docs/reference/typescript-api/service-helpers/translator-service.md +63 -0
  846. package/docs/reference/typescript-api/services/administrator-service.md +71 -0
  847. package/docs/reference/typescript-api/services/asset-service.md +177 -0
  848. package/docs/reference/typescript-api/services/auth-service.md +54 -0
  849. package/docs/reference/typescript-api/services/channel-service.md +111 -0
  850. package/docs/reference/typescript-api/services/collection-service.md +186 -0
  851. package/docs/reference/typescript-api/services/country-service.md +71 -0
  852. package/docs/reference/typescript-api/services/customer-group-service.md +77 -0
  853. package/docs/reference/typescript-api/services/customer-service.md +203 -0
  854. package/docs/reference/typescript-api/services/entity-slug-service.md +37 -0
  855. package/docs/reference/typescript-api/services/facet-service.md +95 -0
  856. package/docs/reference/typescript-api/services/facet-value-service.md +104 -0
  857. package/docs/reference/typescript-api/services/fulfillment-service.md +69 -0
  858. package/docs/reference/typescript-api/services/global-settings-service.md +41 -0
  859. package/docs/reference/typescript-api/services/history-service.md +172 -0
  860. package/docs/reference/typescript-api/services/index.md +5 -0
  861. package/docs/reference/typescript-api/services/initializer-service.md +36 -0
  862. package/docs/reference/typescript-api/services/order-service.md +440 -0
  863. package/docs/reference/typescript-api/services/order-testing-service.md +44 -0
  864. package/docs/reference/typescript-api/services/payment-method-service.md +105 -0
  865. package/docs/reference/typescript-api/services/payment-service.md +105 -0
  866. package/docs/reference/typescript-api/services/product-option-group-service.md +67 -0
  867. package/docs/reference/typescript-api/services/product-option-service.md +64 -0
  868. package/docs/reference/typescript-api/services/product-service.md +111 -0
  869. package/docs/reference/typescript-api/services/product-variant-service.md +179 -0
  870. package/docs/reference/typescript-api/services/promotion-service.md +127 -0
  871. package/docs/reference/typescript-api/services/province-service.md +59 -0
  872. package/docs/reference/typescript-api/services/role-service.md +113 -0
  873. package/docs/reference/typescript-api/services/search-service.md +37 -0
  874. package/docs/reference/typescript-api/services/seller-service.md +65 -0
  875. package/docs/reference/typescript-api/services/session-service.md +105 -0
  876. package/docs/reference/typescript-api/services/settings-store-service.md +194 -0
  877. package/docs/reference/typescript-api/services/shipping-method-service.md +95 -0
  878. package/docs/reference/typescript-api/services/slug-service.md +35 -0
  879. package/docs/reference/typescript-api/services/stock-level-service.md +62 -0
  880. package/docs/reference/typescript-api/services/stock-location-service.md +115 -0
  881. package/docs/reference/typescript-api/services/stock-movement-service.md +95 -0
  882. package/docs/reference/typescript-api/services/tag-service.md +71 -0
  883. package/docs/reference/typescript-api/services/tax-category-service.md +59 -0
  884. package/docs/reference/typescript-api/services/tax-rate-service.md +66 -0
  885. package/docs/reference/typescript-api/services/user-service.md +132 -0
  886. package/docs/reference/typescript-api/services/zone-service.md +77 -0
  887. package/docs/reference/typescript-api/settings-store/cleanup-orphaned-settings-store-entries-options.md +48 -0
  888. package/docs/reference/typescript-api/settings-store/cleanup-orphaned-settings-store-entries-result.md +41 -0
  889. package/docs/reference/typescript-api/settings-store/index.md +38 -0
  890. package/docs/reference/typescript-api/settings-store/orphaned-settings-store-entry.md +48 -0
  891. package/docs/reference/typescript-api/settings-store/set-settings-store-value-result.md +42 -0
  892. package/docs/reference/typescript-api/settings-store/settings-store-field-config.md +92 -0
  893. package/docs/reference/typescript-api/settings-store/settings-store-registration.md +36 -0
  894. package/docs/reference/typescript-api/settings-store/settings-store-scope-function.md +35 -0
  895. package/docs/reference/typescript-api/settings-store/settings-store-scopes.md +36 -0
  896. package/docs/reference/typescript-api/shipping/check-shipping-eligibility-checker-fn.md +27 -0
  897. package/docs/reference/typescript-api/shipping/default-shipping-line-assignment-strategy.md +33 -0
  898. package/docs/reference/typescript-api/shipping/index.md +5 -0
  899. package/docs/reference/typescript-api/shipping/shipping-calculator.md +121 -0
  900. package/docs/reference/typescript-api/shipping/shipping-eligibility-checker-config.md +39 -0
  901. package/docs/reference/typescript-api/shipping/shipping-eligibility-checker.md +60 -0
  902. package/docs/reference/typescript-api/shipping/shipping-line-assignment-strategy.md +75 -0
  903. package/docs/reference/typescript-api/shipping/shipping-options.md +63 -0
  904. package/docs/reference/typescript-api/shipping/should-run-check-fn.md +44 -0
  905. package/docs/reference/typescript-api/state-machine/fsm.md +66 -0
  906. package/docs/reference/typescript-api/state-machine/index.md +5 -0
  907. package/docs/reference/typescript-api/state-machine/state-machine-config.md +103 -0
  908. package/docs/reference/typescript-api/state-machine/transitions.md +43 -0
  909. package/docs/reference/typescript-api/tax/address-based-tax-zone-strategy.md +57 -0
  910. package/docs/reference/typescript-api/tax/default-tax-line-calculation-strategy.md +33 -0
  911. package/docs/reference/typescript-api/tax/default-tax-zone-strategy.md +35 -0
  912. package/docs/reference/typescript-api/tax/index.md +5 -0
  913. package/docs/reference/typescript-api/tax/tax-line-calculation-strategy.md +90 -0
  914. package/docs/reference/typescript-api/tax/tax-options.md +35 -0
  915. package/docs/reference/typescript-api/tax/tax-zone-strategy.md +54 -0
  916. package/docs/reference/typescript-api/telemetry/index.md +5 -0
  917. package/docs/reference/typescript-api/telemetry/instrument.md +47 -0
  918. package/docs/reference/typescript-api/telemetry/instrumentation-strategy.md +35 -0
  919. package/docs/reference/typescript-api/telemetry/wrapped-method-args.md +56 -0
  920. package/docs/reference/typescript-api/testing/create-error-result-guard.md +33 -0
  921. package/docs/reference/typescript-api/testing/create-test-environment.md +47 -0
  922. package/docs/reference/typescript-api/testing/error-result-guard.md +73 -0
  923. package/docs/reference/typescript-api/testing/get-superadmin-context.md +23 -0
  924. package/docs/reference/typescript-api/testing/index.md +5 -0
  925. package/docs/reference/typescript-api/testing/register-initializer.md +27 -0
  926. package/docs/reference/typescript-api/testing/simple-graph-qlclient.md +123 -0
  927. package/docs/reference/typescript-api/testing/test-config.md +30 -0
  928. package/docs/reference/typescript-api/testing/test-db-initializer.md +60 -0
  929. package/docs/reference/typescript-api/testing/test-environment.md +42 -0
  930. package/docs/reference/typescript-api/testing/test-server-options.md +47 -0
  931. package/docs/reference/typescript-api/testing/test-server.md +60 -0
  932. package/docs/reference/typescript-api/testing/testing-logger.md +140 -0
  933. package/docs/reference/typescript-api/worker/bootstrap-worker.md +76 -0
  934. package/docs/reference/typescript-api/worker/index.md +5 -0
  935. package/docs/reference/typescript-api/worker/vendure-worker.md +50 -0
  936. package/docs/reference/typescript-api/worker/worker-health-check-config.md +41 -0
  937. package/docs/user-guide/catalog/collections.md +27 -0
  938. package/docs/user-guide/catalog/facets.md +34 -0
  939. package/docs/user-guide/catalog/product-variants.png +0 -0
  940. package/docs/user-guide/catalog/products.md +35 -0
  941. package/docs/user-guide/catalog/screen-facet-add.webp +0 -0
  942. package/docs/user-guide/catalog/screen-facet-list.webp +0 -0
  943. package/docs/user-guide/catalog/screen-inventory.webp +0 -0
  944. package/docs/user-guide/customers/index.md +30 -0
  945. package/docs/user-guide/customers/screen-customer-group.webp +0 -0
  946. package/docs/user-guide/index.md +7 -0
  947. package/docs/user-guide/localization/index.md +42 -0
  948. package/docs/user-guide/localization/screen-ui-language.webp +0 -0
  949. package/docs/user-guide/orders/draft-orders.md +28 -0
  950. package/docs/user-guide/orders/order-state-diagram-for-admin.png +0 -0
  951. package/docs/user-guide/orders/orders.md +65 -0
  952. package/docs/user-guide/orders/screen-fulfillment-shipped.webp +0 -0
  953. package/docs/user-guide/orders/screen-fulfillment.webp +0 -0
  954. package/docs/user-guide/orders/screen-modification.webp +0 -0
  955. package/docs/user-guide/orders/screen-modify-button.webp +0 -0
  956. package/docs/user-guide/orders/screen-refund-button.webp +0 -0
  957. package/docs/user-guide/orders/screen-settle-payment.webp +0 -0
  958. package/docs/user-guide/promotions/index.md +44 -0
  959. package/docs/user-guide/settings/administrators-roles.md +34 -0
  960. package/docs/user-guide/settings/channels.md +18 -0
  961. package/docs/user-guide/settings/countries-zones.md +12 -0
  962. package/docs/user-guide/settings/global-settings.md +12 -0
  963. package/docs/user-guide/settings/payment-methods.md +32 -0
  964. package/docs/user-guide/settings/screen-shipping-test.webp +0 -0
  965. package/docs/user-guide/settings/screen-translations.webp +0 -0
  966. package/docs/user-guide/settings/shipping-methods.md +55 -0
  967. package/docs/user-guide/settings/taxes.md +32 -0
  968. package/package.json +32 -0
@@ -0,0 +1,2083 @@
1
+ ---
2
+ title: 'Building a CMS Integration Plugin'
3
+ ---
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ A CMS integration plugin allows you to automatically synchronize your Vendure product catalog with an external Content Management System.
9
+
10
+ This is done in a way that establishes Vendure as the source of truth for the ecommerce's data.
11
+
12
+ This guide demonstrates how to build a production-ready CMS integration plugin. The principles covered here are designed to be CMS-agnostic, however we do have [working examples](/guides/how-to/cms-integration-plugin/#platform-specific-setup) for various platforms.
13
+
14
+ Platfroms covered in the [guide](/guides/how-to/cms-integration-plugin/#platform-specific-setup):
15
+
16
+ - [Payload](https://payloadcms.com/)
17
+ - [Sanity](https://www.sanity.io/)
18
+ - [Storyblok](https://www.storyblok.com/)
19
+ - [Strapi](https://strapi.io/)
20
+ - [Contentful](https://www.contentful.com/)
21
+
22
+ ## Working Example Repository
23
+
24
+ :::info
25
+ This guide provides a high-level overview of building a CMS integration plugin. For complete implementations, refer to [working examples repository](https://github.com/vendurehq/examples/tree/master/examples/cms-integration-plugin)
26
+
27
+ The code examples in this guide are simplified for educational purposes. The actual implementations contain additional features like error handling, retry logic, and performance optimizations.
28
+ :::
29
+
30
+ ## Prerequisites
31
+
32
+ - Node.js 20+ with npm package manager
33
+ - An existing Vendure project created with the [Vendure create command](/guides/getting-started/installation/)
34
+ - An access key to a CMS platform that provides an API
35
+
36
+ ## Core Concepts
37
+
38
+ This [plugin](/guides/developer-guide/plugins/) leverages several key Vendure concepts:
39
+
40
+ - **[EventBus](/guides/developer-guide/events/)**: Provides real-time notifications when entities are created, updated, or deleted.
41
+ - **[Job Queues](/guides/developer-guide/worker-job-queue/)**: Ensures that synchronization tasks are performed reliably and asynchronously, with retries on failure.
42
+ - **[Plugin API](/guides/developer-guide/plugins/)**: The foundation for extending Vendure with custom capabilities.
43
+
44
+ ## How It Works
45
+
46
+ The CMS integration follows a simple event-driven flow:
47
+
48
+ ```mermaid
49
+
50
+ sequenceDiagram
51
+ participant vendure as vendure
52
+ participant Bus as Event Bus
53
+ participant Sync as CMS Sync Service
54
+ participant Platform as Platform Specific Service
55
+ participant CMS as External CMS
56
+
57
+ Note over vendure: Source of Truth
58
+
59
+ vendure --> Bus: Initialized Plugin
60
+
61
+ Note over Bus: Entity Change Occurs<br/> - Product<br/> - Variant<br/> - Collection<br/>etc..
62
+
63
+ Bus->>Sync: 2. Triggers a job with entity data
64
+ activate Sync
65
+
66
+ Sync->>Platform: 3. Calls the CMS-platform service
67
+ deactivate Sync
68
+
69
+ activate Platform
70
+ Note over Platform: 4. Makes the specific API call
71
+ Platform->>CMS: 5. API Request
72
+ deactivate Platform
73
+
74
+ CMS ->> vendure: 6. Request Status Logged
75
+ ```
76
+
77
+ This ensures reliable, asynchronous synchronization with built-in retry capabilities.
78
+
79
+ ## Plugin Structure and Types
80
+
81
+ First, let's use the Vendure CLI to scaffold the basic plugin structure:
82
+
83
+ ```bash
84
+ npx vendure add -p CmsPlugin
85
+ ```
86
+
87
+ This command will create the basic plugin structure. Next, we'll generate the required services:
88
+
89
+ ```bash
90
+ # Generate the main sync service
91
+ npx vendure add -s CmsSyncService --selected-plugin CmsPlugin
92
+
93
+ # Generate the CMS-specific service (replace with your CMS)
94
+ npx vendure add -s CmsSpecificService --selected-plugin CmsPlugin
95
+ # Explained later in the Event-Driven Synchronization Section
96
+ ```
97
+
98
+ Now we start by defining the main [plugin](/guides/developer-guide/plugins/) class, its [services](/guides/developer-guide/the-service-layer/), and the configuration types.
99
+
100
+ ### Plugin Definition
101
+
102
+ The `CmsPlugin` class registers the necessary [services](/guides/developer-guide/the-service-layer/) (`CmsSyncService`, `CmsSpecificService`) and sets up any Admin API extensions.
103
+
104
+ ```ts title="src/plugins/cms/cms.plugin.ts"
105
+ import { VendurePlugin, PluginCommonModule, Type, OnModuleInit } from '@vendure/core';
106
+ import { CmsSyncService } from './services/cms-sync.service';
107
+ import { CmsSpecificService } from './services/cms-specific.service';
108
+ import { PluginInitOptions, CMS_PLUGIN_OPTIONS } from './types';
109
+ // ...
110
+
111
+ @VendurePlugin({
112
+ imports: [PluginCommonModule],
113
+ providers: [
114
+ { provide: CMS_PLUGIN_OPTIONS, useFactory: () => CmsPlugin.options },
115
+ CmsSyncService,
116
+ CmsSpecificService, // The service for the specific CMS platform
117
+ ],
118
+ // ...
119
+ })
120
+ export class CmsPlugin {
121
+ static options: PluginInitOptions;
122
+
123
+ static init(options: PluginInitOptions): Type<CmsPlugin> {
124
+ this.options = options;
125
+ return CmsPlugin;
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Configuration Types
131
+
132
+ The plugin's configuration options are defined in a `types.ts` file.
133
+
134
+ Create this file in your plugin directory to define the interfaces. These options will be passed to the plugin from your `vendure-config.ts`.
135
+
136
+ :::note
137
+ This would be created for you automatically when you run the CLI command `npx vendure add`
138
+ :::
139
+
140
+ ```ts title="src/plugins/cms/types.ts"
141
+ import { ID, InjectionToken } from '@vendure/core';
142
+
143
+ export interface PluginInitOptions {
144
+ cmsApiKey?: string;
145
+ CmsSpecificOptions?: any;
146
+ retryAttempts?: number;
147
+ retryDelay?: number;
148
+ }
149
+
150
+ export interface SyncJobData {
151
+ entityType: 'Product' | 'ProductVariant' | 'Collection';
152
+ entityId: ID;
153
+ operationType: 'create' | 'update' | 'delete';
154
+ timestamp: string;
155
+ retryCount: number;
156
+ }
157
+
158
+ export interface SyncResponse {
159
+ success: boolean;
160
+ message?: string;
161
+ error?: string;
162
+ }
163
+ ```
164
+
165
+ ## Event-Driven Synchronization
166
+
167
+ The plugin uses Vendure's [EventBus](/guides/developer-guide/events/) to capture changes in real-time.
168
+
169
+ In the [onModuleInit](/guides/developer-guide/events/#subscribing-to-events) lifecycle hook, we create job queues and subscribe to entity events.
170
+
171
+ ### Creating Job Queues and Subscribing to Events
172
+
173
+ You can also scaffold job queue handlers using the CLI:
174
+
175
+ ```bash
176
+ npx vendure add -j CmsProductSync --name productSyncQueue --selected-service CmsSyncService
177
+ ```
178
+
179
+ This creates a `productSyncQueue ` in the `CmsSyncService`. The service will be responsible for setting up the queues and processing the jobs. It will expose public methods to trigger new jobs.
180
+
181
+ ```ts title="src/plugins/cms/services/cms-sync.service.ts"
182
+ import { Injectable, OnModuleInit } from '@nestjs/common';
183
+ import { JobQueue, JobQueueService /* ... other imports */ } from '@vendure/core';
184
+ import { SyncJobData } from '../types';
185
+
186
+ @Injectable()
187
+ export class CmsSyncService implements OnModuleInit {
188
+ private productSyncQueue: JobQueue<SyncJobData>;
189
+ private variantSyncQueue: JobQueue<SyncJobData>;
190
+ private collectionSyncQueue: JobQueue<SyncJobData>;
191
+
192
+ constructor(
193
+ private jobQueueService: JobQueueService,
194
+ // ... other dependencies
195
+ ) {}
196
+
197
+ async onModuleInit() {
198
+ this.productSyncQueue = await this.jobQueueService.createQueue({
199
+ name: 'cms-product-sync',
200
+ process: async job => {
201
+ return this.syncProductToCms(job.data);
202
+ },
203
+ });
204
+
205
+ this.variantSyncQueue = await this.jobQueueService.createQueue({
206
+ name: 'cms-variant-sync',
207
+ process: async job => {
208
+ return this.syncVariantToCms(job.data);
209
+ },
210
+ });
211
+
212
+ this.collectionSyncQueue = await this.jobQueueService.createQueue({
213
+ name: 'cms-collection-sync',
214
+ process: async job => {
215
+ return this.syncCollectionToCms(job.data);
216
+ },
217
+ });
218
+ }
219
+
220
+ triggerProductSync(data: SyncJobData) {
221
+ return this.productSyncQueue.add(data);
222
+ }
223
+
224
+ triggerVariantSync(data: SyncJobData) {
225
+ return this.variantSyncQueue.add(data);
226
+ }
227
+
228
+ triggerCollectionSync(data: SyncJobData) {
229
+ return this.collectionSyncQueue.add(data);
230
+ }
231
+
232
+ // ... other methods for the actual sync logic (e.g. syncProductToCms)
233
+ }
234
+ ```
235
+
236
+ Next, in the `CmsPlugin`, we subscribe to the `EventBus` and call the new service method to add a job to the queue whenever a relevant event occurs.
237
+
238
+ ```ts title="src/plugins/cms/cms.plugin.ts"
239
+ import { OnModuleInit, EventBus, ProductEvent, VendurePlugin } from '@vendure/core';
240
+ import { CmsSyncService } from './services/cms-sync.service';
241
+
242
+ @VendurePlugin({
243
+ // ...
244
+ providers: [CmsSyncService /* ... */],
245
+ })
246
+ export class CmsPlugin implements OnModuleInit {
247
+ constructor(
248
+ private eventBus: EventBus,
249
+ private cmsSyncService: CmsSyncService,
250
+ ) {}
251
+
252
+ async onModuleInit() {
253
+ // Listen for Product events
254
+ this.eventBus.ofType(ProductEvent).subscribe(event => {
255
+ const syncData = this.extractSyncData(event);
256
+ this.cmsSyncService.triggerProductSync(syncData);
257
+ });
258
+
259
+ // Similar listeners for ProductVariantEvent and CollectionEvent...
260
+ }
261
+ // ...
262
+ }
263
+ ```
264
+
265
+ ## Implementing the Sync Logic
266
+
267
+ The sync logic is split into two services: a generic service to fetch data, and a specific service to communicate with the CMS.
268
+
269
+ `CmsSyncService` orchestrates the synchronization logic. It acts as the bridge between Vendure's internal systems and your CMS platform, handling data fetching, relationship resolution, and error management.
270
+
271
+ :::tip
272
+ Separating orchestration logic from CMS-specific API calls allows for better testability and maintainability. The sync service handles Vendure-specific operations while CMS services focus on API communication.
273
+ :::
274
+
275
+ #### Core Responsibilities
276
+
277
+ The sync service handles several critical functions:
278
+
279
+ - **Entity Data Fetching**: Retrieves complete entity data with necessary relations
280
+ - **Translation Management**: Handles Vendure's multi-language support
281
+ - **Relationship Resolution**: Manages complex entity relationships
282
+ - **Error Handling**: Provides consistent error handling and logging
283
+
284
+ ### Service Structure and Dependencies
285
+
286
+ The service follows Vendure's [dependency injection pattern](/guides/developer-guide/the-service-layer/) and requires several core Vendure services:
287
+
288
+ ```ts title="src/plugins/cms/services/cms-sync.service.ts"
289
+ @Injectable()
290
+ export class CmsSyncService implements OnApplicationBootstrap {
291
+ constructor(
292
+ @Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions,
293
+ private readonly connection: TransactionalConnection,
294
+ private readonly channelService: ChannelService,
295
+ private readonly collectionService: CollectionService,
296
+ private readonly requestContextService: RequestContextService,
297
+ // Your CMS-specific service
298
+ private readonly CmsSpecificService: CmsSpecificService,
299
+ private processContext: ProcessContext,
300
+ ) {}
301
+
302
+ async onApplicationBootstrap() {
303
+ // Ensure this logic only runs on the Vendure worker, and not the server
304
+ if (this.processContext.isWorker) {
305
+ // This is where you would add any necessary setup or initialization logic
306
+ // for example, ensuring that the CMS has compatible content types.
307
+ }
308
+ }
309
+ }
310
+ ```
311
+
312
+ ### Product Synchronization
313
+
314
+ Product sync demonstrates the complete workflow from job processing to CMS communication:
315
+
316
+ ```ts
317
+ async syncProductToCms(jobData: SyncJobData): Promise<SyncResponse> {
318
+ try {
319
+ // Fetch fresh product data from database with translations
320
+ const product = await this.connection.rawConnection
321
+ .getRepository(Product)
322
+ .findOne({
323
+ where: { id: jobData.entityId },
324
+ relations: { translations: true },
325
+ });
326
+
327
+ if (!product) {
328
+ throw new Error(`Product with ID ${jobData.entityId} not found`);
329
+ }
330
+
331
+ const operationType = jobData.operationType;
332
+ const defaultLanguageCode = await this.getDefaultLanguageCode();
333
+
334
+ // Get product slug using translation utilities
335
+ const productSlug = this.translationUtils.getSlugByLanguage(
336
+ product.translations,
337
+ defaultLanguageCode,
338
+ );
339
+
340
+ // Delegate to CMS-specific service
341
+ await this.CmsSpecificService.syncProduct({
342
+ product,
343
+ defaultLanguageCode,
344
+ operationType,
345
+ productSlug,
346
+ });
347
+
348
+ return {
349
+ success: true,
350
+ message: `Product ${jobData.operationType} synced successfully`,
351
+ timestamp: new Date(),
352
+ };
353
+ } catch (error) {
354
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
355
+ Logger.error(`Product sync failed: ${errorMessage}`, error.stack);
356
+ return {
357
+ success: false,
358
+ message: `Product sync failed: ${errorMessage}`,
359
+ };
360
+ }
361
+ }
362
+ ```
363
+
364
+ ### Relationship Handling
365
+
366
+ The service includes methods to resolve entity relationships. For example, finding collections that contain a specific variant:
367
+
368
+ ```ts
369
+ async findCollectionsForVariant(
370
+ variantId: string | number,
371
+ ): Promise<Collection[]> {
372
+ try {
373
+ const variant = await this.connection.rawConnection
374
+ .getRepository(ProductVariant)
375
+ .findOne({
376
+ where: {
377
+ id: variantId,
378
+ },
379
+ relations: ["collections"],
380
+ });
381
+
382
+ return variant?.collections || [];
383
+
384
+ } catch (error) {
385
+ Logger.error(
386
+ `Failed to find collections for variant ${variantId}`,
387
+ String(error),
388
+ );
389
+ return [];
390
+ }
391
+ }
392
+ return collectionsWithVariant;
393
+ } catch (error) {
394
+ // Variants can have no collecitons, therefore, sync anyways.
395
+ Logger.error(`Failed to find collections for variant ${variantId}`, String(error));
396
+ return [];
397
+ }
398
+ }
399
+ ```
400
+
401
+ :::info Additional Entity Types
402
+ The service can also includes `syncVariantToCms()` and `syncCollectionToCms()` methods that follow the same pattern as the product sync shown above.
403
+
404
+ These implementations are omitted from this guide for brevity, but they handle their respective entity types with similar data fetching, relationship resolution, and error handling patterns.
405
+
406
+ The complete implementations can be found in the working [example repositories](https://github.com/vendurehq/examples/tree/master/examples/cms-integration-plugin).
407
+ :::
408
+
409
+ This sync service provides the foundation for handling all Vendure-specific complexity while delegating CMS API communication to specialized services.
410
+
411
+ ## Platform specific setup
412
+
413
+ <Tabs>
414
+ <TabItem value="storyblok" label="Storyblok">
415
+
416
+ :::tip Working Example
417
+ The complete, production-ready Storyblok implementation can be found in the [Storyblok integration example](https://github.com/vendurehq/examples/tree/master/examples/storyblok-cms-integration). Refer to it for a minimal working implementation.
418
+ :::
419
+
420
+ **Setting up Storyblok Space**
421
+
422
+ #### 1. Create a Storyblok Account and Space
423
+
424
+ 1. Sign up at [storyblok.com](https://app.storyblok.com/#!/signup) if you don't have an account
425
+ 2. Create a new Space (equivalent to a project in Storyblok)
426
+ 3. Choose a suitable plan based on your needs
427
+
428
+ #### 2. Get Your API Credentials
429
+
430
+ 1. Navigate to **Settings → Access Tokens** in your Storyblok space
431
+ 2. Create a new **Management API Token** with write permissions
432
+ 3. Note down your **Space ID** (found in Settings → General)
433
+
434
+ #### 3. Configure Environment Variables
435
+
436
+ Add these variables to your `.env` file:
437
+
438
+ ```env
439
+ STORYBLOK_API_KEY=your_management_api_token
440
+ STORYBLOK_SPACE_ID=your_space_id
441
+ ```
442
+
443
+ **The Storyblok Service**
444
+
445
+ `StoryblokService` handles all Storyblok-specific operations including API communication, content type management, and data transformation. Key features include:
446
+
447
+ - **Content Type Management**: Automatically creates Vendure-specific content types (components) in Storyblok
448
+ - **Story Management**: CRUD operations for stories representing products, variants, and collections
449
+ - **Relationship Handling**: Manages references between products, variants, and collections
450
+
451
+ ---
452
+
453
+ **Basic Service Structure**
454
+
455
+ The service follows Vendure's standard dependency injection pattern and implements OnApplicationBootstrap to ensure Storyblok is properly configured before handling sync operations.
456
+
457
+ ```ts title="src/plugins/cms/services/storyblok.service.ts"
458
+ // Define component types as constants for consistency and maintainability
459
+ const COMPONENT_TYPE = {
460
+ product: 'vendure_product',
461
+ product_variant: 'vendure_product_variant',
462
+ collection: 'vendure_collection',
463
+ };
464
+
465
+ @Injectable()
466
+ export class StoryblokService implements OnApplicationBootstrap {
467
+ constructor(@Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions) {}
468
+
469
+ async onApplicationBootstrap() {
470
+ // This is where you would add any necessary setup or initialization logic
471
+ // for example, ensuring that the CMS has compatible content types.
472
+ }
473
+
474
+ // Main entry point for product synchronization
475
+ // The operationType determines which CRUD operation to perform
476
+ async syncProduct({ product, defaultLanguageCode, operationType }) {
477
+ switch (operationType) {
478
+ case 'create':
479
+ return this.createStoryFromProduct(product, defaultLanguageCode);
480
+ case 'update':
481
+ return this.updateStoryFromProduct(product, defaultLanguageCode);
482
+ case 'delete':
483
+ return this.deleteStoryFromProduct(product, defaultLanguageCode);
484
+ }
485
+ }
486
+ }
487
+ ```
488
+
489
+ ---
490
+
491
+ **Making API Requests**
492
+
493
+ All Storyblok API communication is centralized through a single method that handles authentication, error handling, and response parsing. This approach ensures consistent behavior across all operations.
494
+
495
+ ```ts
496
+ private async makeStoryblokRequest({ method, endpoint, data }) {
497
+ // Construct the full API URL using the configured space ID
498
+ const url = `https://mapi.storyblok.com/v1/spaces/${this.options.storyblokSpaceId}/${endpoint}`;
499
+
500
+ const response = await fetch(url, {
501
+ method,
502
+ headers: {
503
+ Authorization: this.options.storyblokApiKey,
504
+ 'Content-Type': 'application/json',
505
+ },
506
+ // Only include request body for POST/PUT operations
507
+ body: data ? JSON.stringify(data) : undefined,
508
+ });
509
+
510
+ if (!response.ok) {
511
+ // Provide clear error messages for debugging API issues
512
+ throw new Error(`Storyblok API error: ${response.status}`);
513
+ }
514
+ return response.json();
515
+ }
516
+ ```
517
+
518
+ ---
519
+
520
+ **Data Transformation**
521
+
522
+ Transformation methods convert Vendure entities into the format expected by Storyblok's API. The `content` object structure must match the component schema defined in Storyblok.
523
+
524
+ ```ts
525
+ private async transformProductData(product, defaultLanguageCode, productSlug?) {
526
+ // Extract the translation for the default language
527
+ // Vendure stores translations in an array, so we need to find the correct one
528
+ const translation = product.translations.find(
529
+ t => t.languageCode === defaultLanguageCode
530
+ );
531
+
532
+ if (!translation) {
533
+ return undefined; // Skip if no translation exists
534
+ }
535
+
536
+ // Find all variant story UUIDs for this product using relationship handling
537
+ const variantStoryIds = await this.findVariantStoriesForProductUuids(
538
+ product.id,
539
+ defaultLanguageCode,
540
+ productSlug,
541
+ );
542
+
543
+ return {
544
+ story: {
545
+ name: translation?.name, // Story name in Storyblok
546
+ slug: translation?.slug, // URL slug for the story
547
+ content: {
548
+ component: COMPONENT_TYPE.product, // Must match the component name in Storyblok
549
+ vendureId: product.id.toString(), // Store Vendure ID for reference
550
+ variants: variantStoryIds, // Array of story UUIDs for product variants
551
+ },
552
+ },
553
+ publish: 1, // Auto-publish the story (1 = published, 0 = draft)
554
+ };
555
+ }
556
+ ```
557
+
558
+ ---
559
+
560
+ **Relationship Handling**
561
+
562
+ One of the most important aspects of CMS integration is maintaining relationships between entities. Products have variants, variants belong to collections, and these relationships need to be reflected in the CMS. Storyblok uses story UUIDs to create references between content pieces.
563
+
564
+ - **Finding Related Entities**
565
+
566
+ > First, we need methods to query the Vendure database for related entities:
567
+
568
+ ```ts
569
+ // Find all product variants for a given product ID from the database
570
+ private async findProductVariants(productId: string | number): Promise<ProductVariant[]> {
571
+ try {
572
+ return await this.connection.rawConnection
573
+ .getRepository(ProductVariant)
574
+ .find({
575
+ where: { productId: productId as any },
576
+ relations: ['translations'], // Include translations for slug generation
577
+ order: { id: 'ASC' },
578
+ });
579
+ } catch (error) {
580
+ Logger.error(`Failed to find variants for product ${productId}`, String(error));
581
+ return [];
582
+ }
583
+ }
584
+ ```
585
+
586
+ - **Batch Story Lookups**
587
+
588
+ > We can also use batch lookups to find multiple stories at once:
589
+
590
+ ```ts
591
+ // Batch lookup method for efficient story retrieval
592
+ private async findStoriesBySlugs(slugs: string[]): Promise<Map<string, any>> {
593
+ const storyMap = new Map<string, any>();
594
+ if (slugs.length === 0) return storyMap;
595
+
596
+ try {
597
+ // Storyblok supports comma-separated slugs for batch lookup
598
+ const slugsParam = slugs.join(',');
599
+ const response = await this.makeStoryblokRequest({
600
+ method: 'GET',
601
+ endpoint: `stories?by_slugs=${slugsParam}`,
602
+ });
603
+
604
+ if (response.stories) {
605
+ for (const story of response.stories) {
606
+ storyMap.set(story.slug, story);
607
+ }
608
+ }
609
+ } catch (error) {
610
+ Logger.error(`Failed to find stories by slugs: ${slugs.join(', ')}`, String(error));
611
+ }
612
+
613
+ return storyMap;
614
+ }
615
+ ```
616
+
617
+ - **Building Relationships**
618
+
619
+ > Finally, we combine database queries with CMS lookups to build relationships:
620
+
621
+ ```ts
622
+ // Find variant stories using batch lookup for efficiency
623
+ private async findVariantStoriesForProductUuids(
624
+ productId: string | number,
625
+ defaultLanguageCode: LanguageCode,
626
+ productSlug?: string | null,
627
+ ): Promise<string[]> {
628
+ if (!productSlug) return [];
629
+ // Get all variants for this product from Vendure database
630
+ const variants = await this.findProductVariants(productId);
631
+ // Generate slugs for all variants (convention: product-slug-variant-id)
632
+ const variantSlugs = variants.map(
633
+ (variant) => `${productSlug}-variant-${variant.id}`,
634
+ );
635
+ if (variantSlugs.length === 0) return [];
636
+ // Batch lookup all variant stories and extract UUIDs
637
+ const storiesMap = await this.findStoriesBySlugs(variantSlugs);
638
+ const storyUuids: string[] = [];
639
+
640
+ for (const [slug, story] of storiesMap) {
641
+ if (story?.uuid) {
642
+ storyUuids.push(story.uuid.toString()); // Storyblok uses UUIDs for references
643
+ }
644
+ }
645
+ return storyUuids;
646
+ }
647
+ // Example: Transform variant data with relationships
648
+ private async transformVariantData(
649
+ variant: ProductVariant,
650
+ defaultLanguageCode: LanguageCode,
651
+ variantSlug: string,
652
+ collections?: Collection[],
653
+ ) {
654
+ const translation = variant.translations.find(
655
+ t => t.languageCode === defaultLanguageCode
656
+ );
657
+ if (!translation) return undefined;
658
+
659
+ // Find parent product and collection references using the same batch lookup patterns
660
+ const parentProductStoryUuid = await this.findParentProductStoryUuid(variant, defaultLanguageCode);
661
+ const collectionStoryUuids = await this.findCollectionStoryUuids(collections, defaultLanguageCode);
662
+
663
+ return {
664
+ story: {
665
+ name: translation.name,
666
+ slug: variantSlug,
667
+ content: {
668
+ component: COMPONENT_TYPE.product_variant,
669
+ vendureId: variant.id.toString(),
670
+ parentProduct: parentProductStoryUuid ? [parentProductStoryUuid] : [],
671
+ collections: collectionStoryUuids,
672
+ },
673
+ },
674
+ publish: 1,
675
+ };
676
+ }
677
+
678
+ // Additional relationship methods like findParentProductStoryUuid and findCollectionStoryUuids
679
+ // follow similar patterns and are available in the working example repository.
680
+ ```
681
+
682
+ **CRUD Operations**
683
+
684
+ These methods handle the basic Create, Read, Update, and Delete operations for stories in Storyblok. They follow REST API conventions and leverage the centralized request method for consistent behavior.
685
+
686
+ ```ts
687
+ // Create a new story in Storyblok
688
+ private async createStoryFromProduct(product, defaultLanguageCode) {
689
+ // Transform Vendure product data into Storyblok story format
690
+ const data = this.transformProductData(product, defaultLanguageCode);
691
+
692
+ // POST to the stories endpoint to create a new story
693
+ return this.makeStoryblokRequest({
694
+ method: 'POST',
695
+ endpoint: 'stories',
696
+ data,
697
+ });
698
+ }
699
+
700
+ // Find an existing story by its slug
701
+ private async findStoryBySlug(slug: string) {
702
+ // Use Storyblok's by_slugs query parameter for efficient lookup
703
+ const response = await this.makeStoryblokRequest({
704
+ method: 'GET',
705
+ endpoint: `stories?by_slugs=${slug}`,
706
+ });
707
+
708
+ // Return the matching story or undefined if not found
709
+ return response.stories.find((story: any) => story.slug === slug);
710
+ }
711
+
712
+ // Additional CRUD methods like updateStoryFromProduct and deleteStoryFromProduct
713
+ // follow similar patterns with PUT and DELETE HTTP methods respectively.
714
+ // Full implementations are available in the working example repository.
715
+
716
+ ```
717
+
718
+ **Final Plugin Configuration**
719
+
720
+ ```ts title="src/vendure-config.ts"
721
+ CmsPlugin.init({
722
+ cmsApiKey: process.env.STORYBLOK_API_KEY,
723
+ storyblokSpaceId: process.env.STORYBLOK_SPACE_ID,
724
+ }),
725
+ ```
726
+
727
+ This setup provides a complete Storyblok CMS integration that automatically creates the necessary content types and syncs your Vendure catalog with structured content in Storyblok.
728
+
729
+ The complete implementations can be found in the working [example repositories](https://github.com/vendurehq/examples/tree/master/examples/storyblok-cms-integration).
730
+
731
+ </TabItem>
732
+ <TabItem value="contentful" label="Contentful">
733
+
734
+ :::tip Working Example
735
+ The complete, production-ready Contentful implementation can be found in the [Contentful integration example](https://github.com/vendurehq/examples/tree/master/examples/contentful-cms-integration). It includes advanced features like locale mapping, bulk operations, and a robust setup process.
736
+ :::
737
+
738
+ **Setting up Contentful Space**
739
+
740
+ **Create a Contentful Account and Space**
741
+
742
+ 1. Sign up for a free account at [contentful.com](https://www.contentful.com/get-started/).
743
+ 2. Follow the onboarding to create a new **Space**. Think of a Space as a repository for all the content of a single project.
744
+
745
+ **Get Your API Credentials**
746
+
747
+ 1. In your Contentful space, navigate to **Settings** → **API keys**.
748
+ 2. Select the **Content management tokens** tab.
749
+ 3. Click **Generate personal token**. Give it a name (e.g., "Vendure Sync") and copy the token. This token is used to create, edit, and delete content.
750
+ 4. Navigate to **Settings** → **General settings** to find your **Space ID**.
751
+
752
+ **Configure Environment Variables**
753
+
754
+ Add the API credentials and Space ID to your project's `.env` file.
755
+
756
+ ```env path=null start=null
757
+ CONTENTFUL_API_KEY=your_content_management_token
758
+ CONTENTFUL_SPACE_ID=your_space_id
759
+ ```
760
+
761
+ ---
762
+
763
+ **The Contentful Service**
764
+
765
+ The `ContentfulService` is the heart of the integration. It manages all communication with Contentful's **Content Management API**, transforms Vendure entities into Contentful **Entries**, and handles the relationships between them.
766
+
767
+ - **Content Type Management**: On startup, it automatically creates the necessary **Content Types** (e.g., `vendureProduct`, `vendureCollection`) in Contentful to structure your e-commerce data.
768
+ - **Entry Synchronization**: Provides full CRUD (Create, Read, Update, Delete) operations for products, variants, and collections.
769
+ - **Relationship Handling**: Correctly links related entries, such as connecting a variant to its parent product and its collections.
770
+
771
+ ---
772
+
773
+ **Basic Service Structure**
774
+
775
+ The service follows Vendure's standard dependency injection pattern and implements OnApplicationBootstrap. This lifecycle hook is crucial because it ensures our custom Contentful **Content Types** are created _before_ the application starts trying to sync data, preventing errors.
776
+
777
+ ```ts path=/src/plugins/cms/services/contentful.service.ts start=15
778
+ // Define content type IDs as constants for consistency and easy reference.
779
+ const CONTENT_TYPE_ID = {
780
+ product: 'vendureProduct',
781
+ product_variant: 'vendureProductVariant',
782
+ collection: 'vendureCollection',
783
+ };
784
+
785
+ @Injectable()
786
+ export class ContentfulService implements OnApplicationBootstrap {
787
+ constructor(
788
+ private connection: TransactionalConnection,
789
+ @Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions,
790
+ ) {}
791
+
792
+ async onApplicationBootstrap() {
793
+ // This is where you would add any necessary setup or initialization logic
794
+ // for example, ensuring that the CMS has compatible content types.
795
+ }
796
+
797
+ // Main entry point for synchronizing a product.
798
+ // The `operationType` determines whether to create, update, or delete an entry.
799
+ async syncProduct({ product, defaultLanguageCode, operationType }) {
800
+ switch (operationType) {
801
+ case 'create':
802
+ return this.createEntryFromProduct(product, defaultLanguageCode);
803
+ case 'update':
804
+ return this.updateEntryFromProduct(product, defaultLanguageCode);
805
+ case 'delete':
806
+ return this.deleteEntryFromProduct(product, defaultLanguageCode);
807
+ }
808
+ }
809
+ }
810
+ ```
811
+
812
+ ---
813
+
814
+ **Making API Requests**
815
+
816
+ To keep our code clean and maintainable, all communication with the Contentful API is channeled through a single `makeContentfulRequest` method. This centralizes authentication, error handling, and URL construction.
817
+
818
+ ```ts path=/src/plugins/cms/services/contentful.service.ts start=1505
819
+ private async makeContentfulRequest({ method, endpoint, data, headers = {} }) {
820
+ // Construct the full API URL using the configured space and environment ID.
821
+ const url = `https://api.contentful.com/spaces/${this.options.contentfulSpaceId}/environments/master/${endpoint}`;
822
+
823
+ const response = await fetch(url, {
824
+ method,
825
+ headers: {
826
+ // The Content Management API requires a Bearer token.
827
+ Authorization: `Bearer ${this.options.cmsApiKey}`,
828
+ 'Content-Type': 'application/vnd.contentful.management.v1+json',
829
+ ...headers,
830
+ },
831
+ body: data ? JSON.stringify(data) : undefined,
832
+ });
833
+
834
+ if (!response.ok) {
835
+ const errorText = await response.text();
836
+ // Rich error messages are critical for debugging API issues.
837
+ throw new Error(`Contentful API error: ${response.status} - ${errorText}`);
838
+ }
839
+
840
+ // DELETE requests often return no body, so we handle that case.
841
+ if (response.status === 204 || method === 'DELETE') {
842
+ return {};
843
+ }
844
+ return response.json();
845
+ }
846
+ ```
847
+
848
+ ---
849
+
850
+ **Data Transformation**
851
+
852
+ Before we can send data to Contentful, we must transform our Vendure entities into the format Contentful expects. A Contentful **Entry** consists of a `fields` object where each key corresponds to a field in the **Content Type**. Each field is also localized, so its value is nested within a locale key (e.g., `en-US`).
853
+
854
+ ```ts path=/src/plugins/cms/services/contentful.service.ts start=1260
855
+ private async transformProductData(
856
+ product: Product,
857
+ defaultLanguageCode: LanguageCode,
858
+ ) {
859
+ // Vendure stores translations in an array; we need to find the one
860
+ // for the store's default language.
861
+ const translation = product.translations.find(
862
+ t => t.languageCode === defaultLanguageCode,
863
+ );
864
+
865
+ if (!translation) return undefined; // Cannot sync if default language is missing.
866
+
867
+ // Find all variant entries that belong to this product. This is a key
868
+ // part of relationship handling, covered in the next section.
869
+ const variantEntryIds = await this.findVariantEntriesForProductIds(
870
+ product.id,
871
+ defaultLanguageCode,
872
+ translation.slug,
873
+ );
874
+
875
+ // Contentful uses a specific locale format, e.g., 'en-US'.
876
+ const contentfulLocale = this.mapToContentfulLocale(defaultLanguageCode);
877
+
878
+ return {
879
+ fields: {
880
+ name: { [contentfulLocale]: translation.name },
881
+ slug: { [contentfulLocale]: translation.slug },
882
+ vendureId: { [contentfulLocale]: product.id.toString() },
883
+ // Variants are linked via an array of "Link" objects.
884
+ variants: {
885
+ [contentfulLocale]: variantEntryIds.map(id => ({
886
+ sys: { type: 'Link', linkType: 'Entry', id },
887
+ })),
888
+ },
889
+ },
890
+ };
891
+ }
892
+ ```
893
+
894
+ ---
895
+
896
+ **Relationship Handling**
897
+
898
+ One of the most important aspects of CMS integration is maintaining relationships between entities. Products have variants, and variants belong to collections. Contentful manages these relationships using **Links**, which are essentially pointers from one entry to another. Our goal is to find the Contentful **Entry ID** of a related entity and embed it as a Link.
899
+
900
+ **Finding Related Vendure Entities**
901
+
902
+ The first step is always to query the Vendure database to find the related entities. We use the injected **TransactionalConnection** to perform efficient database lookups.
903
+
904
+ ```ts path=/src/plugins/cms/services/contentful.service.ts start=418
905
+ // Finds all ProductVariant entities for a given product ID.
906
+ private async findProductVariants(productId: ID): Promise<ProductVariant[]> {
907
+ try {
908
+ // Use Vendure's standard pattern for database access in a service.
909
+ return await this.connection.rawConnection
910
+ .getRepository(ProductVariant)
911
+ .find({
912
+ where: { productId: productId as any },
913
+ relations: ['translations'], // Eager load translations for slug generation.
914
+ });
915
+ } catch (error) {
916
+ Logger.error(`Failed to find variants for product ${productId}`, String(error));
917
+ return [];
918
+ }
919
+ }
920
+ ```
921
+
922
+ ---
923
+
924
+ **Building the Relationships**
925
+
926
+ Now we combine these two patterns. We fetch the related Vendure entities, generate their unique identifiers (slugs), use a batch lookup to find their corresponding Contentful entries, and extract their IDs to build the **Link** objects.
927
+
928
+ ```ts path=/src/plugins/cms/services/contentful.service.ts start=442
929
+ private async findVariantEntriesForProductIds(
930
+ productId: string | number,
931
+ defaultLanguageCode: LanguageCode,
932
+ productSlug?: string | null,
933
+ ): Promise<string[]> {
934
+ if (!productSlug) return [];
935
+
936
+ // 1. Get all related variants from the Vendure database.
937
+ const variants = await this.findProductVariants(productId);
938
+
939
+ // 2. Generate the unique slugs for each variant.
940
+ // Convention: `product-slug-variant-id`
941
+ const variantSlugs = variants.map(
942
+ variant => `${productSlug}-variant-${variant.id}`,
943
+ );
944
+ if (variantSlugs.length === 0) return [];
945
+
946
+ // 3. Perform a single batch lookup to find all matching Contentful entries.
947
+ const entriesMap = await this.findEntriesByField(
948
+ CONTENT_TYPE_ID.product_variant,
949
+ 'slug',
950
+ variantSlugs,
951
+ );
952
+
953
+ // 4. Extract the system ID from each entry to be used for linking.
954
+ const entryIds: string[] = [];
955
+ for (const entry of entriesMap.values()) {
956
+ if (entry?.sys?.id) {
957
+ entryIds.push(entry.sys.id);
958
+ }
959
+ }
960
+ return entryIds;
961
+ }
962
+ ```
963
+
964
+ This same pattern is used to link variants to their parent product and to their collections, as shown in the complete `transformVariantData` method in the example repository.
965
+
966
+ ---
967
+
968
+ **CRUD Operations**
969
+
970
+ These methods perform the core Create, Read, Update, and Delete logic. They rely on our centralized `makeContentfulRequest` method and handle the specifics of Contentful's API, such as versioning and the publish workflow.
971
+
972
+ :::info Contentful's Publish Workflow
973
+ Contentful requires entries to be explicitly published before they are visible in the Delivery API. After creating or updating an entry, we must make a separate API call to publish it. Similarly, to delete an entry, it must first be unpublished.
974
+ :::
975
+
976
+ ```ts path=/src/plugins/cms/services/contentful.service.ts
977
+ // Creates a new entry and then publishes it.
978
+ private async createEntryFromProduct(product: Product, defaultLanguageCode: LanguageCode) {
979
+ const data = await this.transformProductData(product, defaultLanguageCode);
980
+ if (!data) return;
981
+
982
+ // POST to the 'entries' endpoint with a special header specifying the content type.
983
+ const result = await this.makeContentfulRequest({
984
+ method: 'POST',
985
+ endpoint: this.entriesPath,
986
+ data,
987
+ headers: { 'X-Contentful-Content-Type': CONTENT_TYPE_ID.product },
988
+ });
989
+
990
+ // An entry is created as a draft; it must be published to be live.
991
+ if (result.sys?.id) {
992
+ await this.publishEntry(result.sys.id, result.sys.version);
993
+ }
994
+ }
995
+
996
+ // Updates an existing entry.
997
+ private async updateEntryFromProduct(product: Product, defaultLanguageCode: LanguageCode) {
998
+ // 1. Find the existing entry in Contentful (e.g., by its slug).
999
+ const slug = getSlug(product, defaultLanguageCode);
1000
+ const existingEntry = await this.findEntryByField(CONTENT_TYPE_ID.product, 'slug', slug);
1001
+ if (!existingEntry) {
1002
+ // If it doesn't exist, we can fall back to creating it.
1003
+ return this.createEntryFromProduct(product, defaultLanguageCode);
1004
+ }
1005
+
1006
+ // 2. Transform the data.
1007
+ const data = await this.transformProductData(product, defaultLanguageCode);
1008
+ if (!data) return;
1009
+
1010
+ // 3. PUT to the specific entry URL. This requires the current version
1011
+ // for optimistic locking, preventing race conditions.
1012
+ const result = await this.makeContentfulRequest({
1013
+ method: 'PUT',
1014
+ endpoint: `${this.entriesPath}/${existingEntry.sys.id}`,
1015
+ data,
1016
+ headers: { 'X-Contentful-Version': existingEntry.sys.version.toString() },
1017
+ });
1018
+
1019
+ // 4. Publish the new version.
1020
+ if (result.sys?.id) {
1021
+ await this.publishEntry(result.sys.id, result.sys.version);
1022
+ }
1023
+ }
1024
+
1025
+ // The full implementations for delete, as well as variant and collection CRUD,
1026
+ // can be found in the working example repository. They follow similar patterns.
1027
+ ```
1028
+
1029
+ ---
1030
+
1031
+ **Final Plugin Configuration**
1032
+
1033
+ Finally, we initialize the plugin in `vendure-config.ts`, providing the API credentials from our environment variables.
1034
+
1035
+ ```ts title="src/vendure-config.ts" path=null start=null
1036
+ import { CmsPlugin } from './plugins/cms/cms.plugin';
1037
+
1038
+ // ... other imports
1039
+
1040
+ export const config: VendureConfig = {
1041
+ // ... other config
1042
+ plugins: [
1043
+ // ... other plugins
1044
+ CmsPlugin.init({
1045
+ cmsApiKey: process.env.CONTENTFUL_API_KEY,
1046
+ contentfulSpaceId: process.env.CONTENTFUL_SPACE_ID,
1047
+ }),
1048
+ ],
1049
+ };
1050
+ ```
1051
+
1052
+ This configuration provides a complete Contentful CMS integration that automatically creates the necessary content models and syncs your Vendure catalog with structured content in Contentful, ready to be consumed by a headless storefront.
1053
+
1054
+ </TabItem>
1055
+
1056
+ <TabItem value="strapi" label="Strapi">
1057
+
1058
+ :::tip Working Example
1059
+ The complete, production-ready Strapi implementation can be found in the [Strapi integration example](https://github.com/vendurehq/examples/tree/master/examples/strapi-cms-integration). It includes advanced features like plugin-based content types, batch operations, and relationship management.
1060
+ :::
1061
+
1062
+ This guide provides a complete integration for Vendure with Strapi CMS, including setting up a Strapi application with a custom plugin and implementing the synchronization service.
1063
+
1064
+ **Setting up Strapi**
1065
+
1066
+ #### 1. Create a new Strapi project
1067
+
1068
+ Initialize your Strapi project using the CLI:
1069
+
1070
+ ```bash
1071
+ npx create-strapi-app@latest strapi-integration-app
1072
+ cd strapi-integration-app
1073
+ ```
1074
+
1075
+ Choose the following options when prompted:
1076
+
1077
+ - Installation type: **Custom (manual settings)**
1078
+ - Database client: **Sqlite** (or your preferred database)
1079
+ - Use TypeScript: **Yes**
1080
+
1081
+ #### 2. Create a Vendure Integration Plugin
1082
+
1083
+ Generate a custom plugin to organize Vendure content types:
1084
+
1085
+ ```bash
1086
+ npm run strapi generate plugin
1087
+ # Plugin name: vendure-integration-plugin
1088
+ ```
1089
+
1090
+ #### 3. Configure Environment Variables
1091
+
1092
+ Add these variables to your `.env` file:
1093
+
1094
+ ```env
1095
+ STRAPI_API_TOKEN=your_api_token
1096
+ STRAPI_BASE_URL=http://localhost:1337
1097
+ ```
1098
+
1099
+ **Defining Content Types**
1100
+
1101
+ #### 1. Create Vendure-specific content types
1102
+
1103
+ Create the content type schemas in your plugin's `server/content-types` folder:
1104
+
1105
+ **vendure-product/schema.json**
1106
+
1107
+ ```json title="src/plugins/vendure-integration-plugin/server/content-types/vendure-product/schema.json"
1108
+ {
1109
+ "kind": "collectionType",
1110
+ "collectionName": "vendure_products",
1111
+ "info": {
1112
+ "displayName": "Vendure Product",
1113
+ "singularName": "vendure-product",
1114
+ "pluralName": "vendure-products"
1115
+ },
1116
+ "attributes": {
1117
+ "vendureId": {
1118
+ "type": "integer",
1119
+ "required": true,
1120
+ "unique": true
1121
+ },
1122
+ "name": {
1123
+ "type": "string",
1124
+ "required": true
1125
+ },
1126
+ "slug": {
1127
+ "type": "string",
1128
+ "required": true,
1129
+ "unique": true
1130
+ },
1131
+ "productVariants": {
1132
+ "type": "relation",
1133
+ "relation": "oneToMany",
1134
+ "target": "api::vendure-product-variant.vendure-product-variant",
1135
+ "mappedBy": "product"
1136
+ }
1137
+ }
1138
+ }
1139
+ ```
1140
+
1141
+ **Additional content types for VendureProductVariant and VendureCollection**
1142
+ follow the same pattern with appropriate field definitions and relationships.
1143
+
1144
+ ```json
1145
+ // vendure-product-variant/schema.json
1146
+ // vendure-collection/schema.json
1147
+ ```
1148
+
1149
+ #### 2. Enable the plugin
1150
+
1151
+ Update your Strapi configuration to enable the plugin:
1152
+
1153
+ ```ts title="config/plugins.ts"
1154
+ export default {
1155
+ 'vendure-integration-plugin': {
1156
+ enabled: true,
1157
+ resolve: './src/plugins/vendure-integration-plugin',
1158
+ },
1159
+ };
1160
+ ```
1161
+
1162
+ #### 3. Generate API tokens
1163
+
1164
+ After starting Strapi, create an API token:
1165
+
1166
+ 1. Navigate to **Settings → API Tokens** in the Strapi admin panel
1167
+ 2. Create a new token with **Full access** or custom permissions
1168
+ 3. Save the token for use in your Vendure plugin configuration
1169
+
1170
+ **The Strapi Service**
1171
+
1172
+ The `StrapiService` handles all Strapi-specific operations including API communication, content management, and relationship resolution. Key features include:
1173
+
1174
+ - **Content Management**: CRUD operations using Strapi's REST API
1175
+ - **Relationship Handling**: Manages references between products, variants, and collections
1176
+ - **Batch Operations**: Efficient bulk lookups for related entities
1177
+
1178
+ ---
1179
+
1180
+ **Basic Service Structure**
1181
+
1182
+ The service follows Vendure's standard dependency injection pattern and implements OnApplicationBootstrap to ensure the CMS is properly configured before handling sync operations.
1183
+
1184
+ ```ts title="src/plugins/cms/services/strapi.service.ts"
1185
+ @Injectable()
1186
+ export class StrapiService {
1187
+ private get strapiBaseUrl(): string {
1188
+ return `${this.options.strapiBaseUrl || 'http://localhost:1337'}/api`;
1189
+ }
1190
+
1191
+ constructor(@Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions) {}
1192
+
1193
+ async syncProduct({ product, defaultLanguageCode, operationType }) {
1194
+ switch (operationType) {
1195
+ case 'create':
1196
+ return this.createDocumentFromProduct(product, defaultLanguageCode);
1197
+ case 'update':
1198
+ return this.updateDocumentFromProduct(product, defaultLanguageCode);
1199
+ case 'delete':
1200
+ return this.deleteDocumentFromProduct(product, defaultLanguageCode);
1201
+ }
1202
+ }
1203
+ }
1204
+ ```
1205
+
1206
+ ---
1207
+
1208
+ **Making API Requests**
1209
+
1210
+ All Strapi API communication is centralized through a single method that handles authentication, error handling, and response parsing. This approach ensures consistent behavior across all operations.
1211
+
1212
+ ```ts
1213
+ private async makeStrapiRequest({ method, endpoint, data }) {
1214
+ const url = `${this.strapiBaseUrl}/${endpoint}`;
1215
+
1216
+ const headers: Record<string, string> = {
1217
+ 'Content-Type': 'application/json',
1218
+ };
1219
+
1220
+ if (this.options.strapiApiKey) {
1221
+ headers.Authorization = `Bearer ${this.options.strapiApiKey}`;
1222
+ }
1223
+
1224
+ const response = await fetch(url, {
1225
+ method,
1226
+ headers,
1227
+ body: data ? JSON.stringify(data) : undefined,
1228
+ });
1229
+
1230
+ if (!response.ok) {
1231
+ throw new Error(`Strapi API error: ${response.status}`);
1232
+ }
1233
+ return response.json();
1234
+ }
1235
+ ```
1236
+
1237
+ ---
1238
+
1239
+ **Data Transformation**
1240
+
1241
+ Transformation methods convert Vendure entities into the format expected by Strapi's API. The `data` object structure must match the collection schema defined in Strapi.
1242
+
1243
+ ```ts
1244
+ private async transformProductData(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1245
+ const t = product.translations.find(tr => tr.languageCode === defaultLanguageCode);
1246
+ if (!t) return undefined;
1247
+
1248
+ const variantDocumentIds = await this.findVariantDocumentsForProductIds(product.id, defaultLanguageCode, productSlug);
1249
+
1250
+ return {
1251
+ vendureId: product.id,
1252
+ name: t.name,
1253
+ slug: t.slug,
1254
+ productVariants: variantDocumentIds,
1255
+ };
1256
+ }
1257
+ ```
1258
+
1259
+ ---
1260
+
1261
+ **Relationship Handling**
1262
+
1263
+ One of the most important aspects of CMS integration is maintaining relationships between entities. Products have variants, variants belong to collections, and these relationships need to be reflected in the CMS. Strapi uses document IDs to create references between content pieces.
1264
+
1265
+ ```ts
1266
+ private async findProductVariants(productId: string | number): Promise<ProductVariant[]> {
1267
+ return this.connection.rawConnection.getRepository(ProductVariant).find({
1268
+ where: { productId: productId as any },
1269
+ relations: ['translations'],
1270
+ order: { id: 'ASC' },
1271
+ });
1272
+ }
1273
+
1274
+ private async findDocumentsBySlugs(collectionSlug: string, slugs: string[]): Promise<Map<string, any>> {
1275
+ const map = new Map<string, any>();
1276
+ if (slugs.length === 0) return map;
1277
+
1278
+ const queryParams = slugs.map((slug, i) => `filters[slug][$in][${i}]=${encodeURIComponent(slug)}`).join('&');
1279
+ const endpoint = `${collectionSlug}?${queryParams}`;
1280
+ const response = await this.makeStrapiRequest({ method: 'GET', endpoint });
1281
+
1282
+ if (response.data) {
1283
+ for (const doc of response.data) {
1284
+ if (doc?.slug) map.set(doc.slug, doc);
1285
+ }
1286
+ }
1287
+ return map;
1288
+ }
1289
+
1290
+ private async findVariantDocumentsForProductIds(
1291
+ productId: string | number,
1292
+ defaultLanguageCode: LanguageCode,
1293
+ productSlug?: string | null,
1294
+ ): Promise<string[]> {
1295
+ if (!productSlug) return [];
1296
+ const variants = await this.findProductVariants(productId);
1297
+ const slugs = variants.map(v => `${productSlug}-variant-${v.id}`);
1298
+ if (slugs.length === 0) return [];
1299
+ const docs = await this.findDocumentsBySlugs('vendure-product-variant', slugs);
1300
+ const ids: string[] = [];
1301
+ docs.forEach(doc => { if (doc?.id) ids.push(String(doc.id)); });
1302
+ return ids;
1303
+ }
1304
+ ```
1305
+
1306
+ ---
1307
+
1308
+ **CRUD Operations**
1309
+
1310
+ These methods handle the basic Create, Read, Update, and Delete operations for documents in Strapi. They follow REST API conventions and leverage the centralized request method for consistent behavior.
1311
+
1312
+ ```ts
1313
+ private async findDocumentBySlug(collectionSlug: string, slug: string) {
1314
+ const endpoint = `${collectionSlug}?filters[slug][$eq]=${encodeURIComponent(slug)}&pagination[limit]=1`;
1315
+ const response = await this.makeStrapiRequest({ method: 'GET', endpoint });
1316
+ return response.data && response.data.length > 0 ? response.data[0] : null;
1317
+ }
1318
+
1319
+ private async createDocumentFromProduct(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1320
+ const data = await this.transformProductData(product, defaultLanguageCode, productSlug);
1321
+ if (!data) return;
1322
+ await this.makeStrapiRequest({ method: 'POST', endpoint: 'vendure-products', data: { data } });
1323
+ }
1324
+
1325
+ private async updateDocumentFromProduct(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1326
+ const slug = this.translationUtils.getSlugByLanguage(product.translations, defaultLanguageCode);
1327
+ if (!slug) return;
1328
+ const existing = await this.findDocumentBySlug('vendure-products', slug);
1329
+ if (!existing) return this.createDocumentFromProduct(product, defaultLanguageCode, productSlug);
1330
+
1331
+ const data = await this.transformProductData(product, defaultLanguageCode, productSlug);
1332
+ if (!data) return;
1333
+
1334
+ await this.makeStrapiRequest({
1335
+ method: 'PUT',
1336
+ endpoint: `vendure-products/${existing.id}`,
1337
+ data: { data },
1338
+ });
1339
+ }
1340
+
1341
+ private async deleteDocumentFromProduct(product: Product, defaultLanguageCode: LanguageCode) {
1342
+ const slug = this.translationUtils.getSlugByLanguage(product.translations, defaultLanguageCode);
1343
+ if (!slug) return;
1344
+ const existing = await this.findDocumentBySlug('vendure-products', slug);
1345
+ if (!existing) return;
1346
+
1347
+ await this.makeStrapiRequest({ method: 'DELETE', endpoint: `vendure-products/${existing.id}` });
1348
+ }
1349
+ ```
1350
+
1351
+ **Configuration**
1352
+
1353
+ Update your plugin configuration to include Strapi options:
1354
+
1355
+ ```ts title="src/plugins/cms/types.ts"
1356
+ export interface PluginInitOptions {
1357
+ // ... existing options
1358
+ strapiApiKey?: string;
1359
+ strapiBaseUrl?: string;
1360
+ }
1361
+ ```
1362
+
1363
+ **Environment Variables**
1364
+
1365
+ Add these to your `.env` file:
1366
+
1367
+ ```env
1368
+ STRAPI_API_KEY=your_strapi_api_token
1369
+ STRAPI_BASE_URL=http://localhost:1337
1370
+ ```
1371
+
1372
+ **Final Plugin Configuration**
1373
+
1374
+ ```ts title="src/vendure-config.ts"
1375
+ CmsPlugin.init({
1376
+ strapiApiKey: process.env.STRAPI_API_KEY,
1377
+ strapiBaseUrl: process.env.STRAPI_BASE_URL || 'http://localhost:1337',
1378
+ }),
1379
+ ```
1380
+
1381
+ This setup provides a complete Strapi CMS integration that automatically creates the necessary content types and syncs your Vendure catalog with structured content in Strapi.
1382
+
1383
+ The complete implementations can be found in the working [example repositories](https://github.com/vendurehq/examples/tree/master/examples/strapi-cms-integration).
1384
+
1385
+ </TabItem>
1386
+ <TabItem value="Sanity" label="Sanity">
1387
+
1388
+ :::tip Working Example
1389
+ The complete, production-ready Sanity implementation can be found in the [Sanity integration example](https://github.com/vendurehq/examples/tree/master/examples/sanity-cms-integration). It includes advanced features like content type management and bulk operations.
1390
+ :::
1391
+
1392
+ This section provides an overview of integrating Vendure with Sanity. Refer to the working example for production patterns.
1393
+
1394
+ **Setting up Sanity Studio**
1395
+
1396
+ #### 1. Create a new Studio with Sanity CLI
1397
+
1398
+ ```bash
1399
+ npm create sanity@latest -- --project <project-id> --dataset production --template clean --typescript --output-path studio-vendure-plugin
1400
+ cd studio-vendure-plugin
1401
+ ```
1402
+
1403
+ #### 2. Run Sanity Studio locally
1404
+
1405
+ ```bash
1406
+ npm run dev
1407
+ ```
1408
+
1409
+ #### 3. Log in to the Studio
1410
+
1411
+ Open `http://localhost:3333` and authenticate using the same provider you used for the CLI.
1412
+
1413
+ **Defining Schema Types**
1414
+
1415
+ Split content into three document types: vendureProduct, vendureProductVariant, and vendureCollection. Keep fields minimal and focused on IDs, names, slugs, and references.
1416
+
1417
+ ```ts
1418
+ import { defineField, defineType } from 'sanity';
1419
+
1420
+ export const vendureProduct = defineType({
1421
+ name: 'vendureProduct',
1422
+ title: 'Vendure Product',
1423
+ type: 'document',
1424
+ fields: [
1425
+ defineField({ name: 'vendureId', type: 'number', validation: r => r.required() }),
1426
+ defineField({ name: 'title', type: 'string', validation: r => r.required() }),
1427
+ defineField({
1428
+ name: 'slug',
1429
+ type: 'slug',
1430
+ options: { source: 'title' },
1431
+ validation: r => r.required(),
1432
+ }),
1433
+ defineField({
1434
+ name: 'productVariants',
1435
+ type: 'array',
1436
+ of: [{ type: 'reference', to: [{ type: 'vendureProductVariant' }] }],
1437
+ }),
1438
+ ],
1439
+ });
1440
+ ```
1441
+
1442
+ Register these in `schemaTypes/index.ts`:
1443
+
1444
+ ```ts
1445
+ export const schemaTypes = [vendureProduct, vendureProductVariant, vendureCollection];
1446
+ ```
1447
+
1448
+ **The Sanity Service**
1449
+
1450
+ The `SanityService` encapsulates Sanity-specific API communication, GROQ queries, and transformations.
1451
+
1452
+ ```ts title="src/plugins/cms/services/sanity.service.ts"
1453
+ @Injectable()
1454
+ export class SanityService {
1455
+ private get apiBase(): string {
1456
+ return `https://${this.options.sanityProjectId}.api.sanity.io/v2023-10-01`;
1457
+ }
1458
+
1459
+ constructor(@Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions) {}
1460
+
1461
+ async syncProduct({ product, defaultLanguageCode, operationType }) {
1462
+ switch (operationType) {
1463
+ case 'create':
1464
+ return this.createDocumentFromProduct(product, defaultLanguageCode);
1465
+ case 'update':
1466
+ return this.updateDocumentFromProduct(product, defaultLanguageCode);
1467
+ case 'delete':
1468
+ return this.deleteDocumentFromProduct(product, defaultLanguageCode);
1469
+ }
1470
+ }
1471
+
1472
+ private getAuthHeaders() {
1473
+ return {
1474
+ Authorization: `Bearer ${this.options.sanityApiKey}`,
1475
+ 'Content-Type': 'application/json',
1476
+ } as const;
1477
+ }
1478
+
1479
+ private async query<T>(groq: string, params: Record<string, any> = {}): Promise<T> {
1480
+ const url = `${this.apiBase}/data/query/${this.options.sanityDataset}`;
1481
+ const res = await fetch(url, {
1482
+ method: 'POST',
1483
+ headers: this.getAuthHeaders(),
1484
+ body: JSON.stringify({ query: groq, params }),
1485
+ });
1486
+ if (!res.ok) throw new Error(`Sanity query error: ${res.status}`);
1487
+ const json = await res.json();
1488
+ return json.result as T;
1489
+ }
1490
+
1491
+ private async mutate(mutations: any[]) {
1492
+ const url = `${this.apiBase}/data/mutate/${this.options.sanityDataset}?returnIds=true`;
1493
+ const res = await fetch(url, {
1494
+ method: 'POST',
1495
+ headers: this.getAuthHeaders(),
1496
+ body: JSON.stringify({ mutations }),
1497
+ });
1498
+ if (!res.ok) throw new Error(`Sanity mutate error: ${res.status}`);
1499
+ return res.json();
1500
+ }
1501
+ }
1502
+ ```
1503
+
1504
+ ---
1505
+
1506
+ **Data Transformation**
1507
+
1508
+ Transformation methods convert Vendure entities into the format expected by Sanity's API. The document structure must match the schema defined in Sanity Studio.
1509
+
1510
+ ```ts
1511
+ private async transformProductData(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1512
+ const t = product.translations.find(tr => tr.languageCode === defaultLanguageCode);
1513
+ if (!t) return undefined;
1514
+
1515
+ const variantIds = await this.findVariantDocumentsForProductIds(product.id, defaultLanguageCode, productSlug);
1516
+
1517
+ return {
1518
+ _type: 'vendureProduct',
1519
+ vendureId: parseInt(product.id.toString()),
1520
+ title: t.name,
1521
+ slug: { current: t.slug },
1522
+ productVariants: variantIds.map(id => ({ _type: 'reference', _ref: id })),
1523
+ };
1524
+ }
1525
+ ```
1526
+
1527
+ ---
1528
+
1529
+ **Relationship Handling**
1530
+
1531
+ One of the most important aspects of CMS integration is maintaining relationships between entities. Sanity uses document IDs to create references between content pieces.
1532
+
1533
+ - Finding Related Entities (Vendure DB)
1534
+
1535
+ ```ts
1536
+ private async findProductVariants(productId: string | number): Promise<ProductVariant[]> {
1537
+ try {
1538
+ return await this.connection.rawConnection.getRepository(ProductVariant).find({
1539
+ where: { productId: productId as any },
1540
+ relations: ['translations'],
1541
+ order: { id: 'ASC' },
1542
+ });
1543
+ } catch (e) {
1544
+ Logger.error(`Failed to find variants for product ${productId}`, String(e));
1545
+ return [];
1546
+ }
1547
+ }
1548
+ ```
1549
+
1550
+ - Batch Document Lookups (GROQ)
1551
+
1552
+ ```ts
1553
+ private async findDocumentsBySlugs(type: 'vendureProduct' | 'vendureProductVariant' | 'vendureCollection', slugs: string[]): Promise<Map<string, any>> {
1554
+ const map = new Map<string, any>();
1555
+ if (slugs.length === 0) return map;
1556
+
1557
+ const results = await this.query<any[]>(`*[_type == $type && slug.current in $slugs]{ _id, "slug": slug.current }`, { type, slugs });
1558
+ for (const doc of results ?? []) if (doc?.slug) map.set(doc.slug, doc);
1559
+ return map;
1560
+ }
1561
+ ```
1562
+
1563
+ - Building Relationships
1564
+
1565
+ ```ts
1566
+ private async findVariantDocumentsForProductIds(
1567
+ productId: string | number,
1568
+ defaultLanguageCode: LanguageCode,
1569
+ productSlug?: string | null,
1570
+ ): Promise<string[]> {
1571
+ if (!productSlug) return [];
1572
+ const variants = await this.findProductVariants(productId);
1573
+ const slugs = variants.map(v => `${productSlug}-variant-${v.id}`);
1574
+ if (slugs.length === 0) return [];
1575
+ const docs = await this.findDocumentsBySlugs('vendureProductVariant', slugs);
1576
+ const ids: string[] = [];
1577
+ docs.forEach(doc => { if (doc?._id) ids.push(String(doc._id)); });
1578
+ return ids;
1579
+ }
1580
+
1581
+ private async findParentProductDocumentId(variant: ProductVariant, defaultLanguageCode: LanguageCode): Promise<string | null> {
1582
+ const product = await this.connection.rawConnection.getRepository(Product).findOne({ where: { id: variant.productId }, relations: ['translations'] });
1583
+ if (!product) return null;
1584
+ const t = product.translations.find(tr => tr.languageCode === defaultLanguageCode);
1585
+ if (!t?.slug) return null;
1586
+ const results = await this.query<any[]>(`*[_type == "vendureProduct" && slug.current == $slug][0]{ _id }`, { slug: t.slug });
1587
+ return results?._id ?? null;
1588
+ }
1589
+ ```
1590
+
1591
+ ---
1592
+
1593
+ **CRUD Operations**
1594
+
1595
+ These methods handle the basic Create, Read, Update, and Delete operations for documents in Sanity. They use GROQ queries for lookups and mutations for write operations.
1596
+
1597
+ ```ts
1598
+ private async findDocumentBySlug(type: 'vendureProduct' | 'vendureProductVariant' | 'vendureCollection', slug: string) {
1599
+ return this.query<any>(`*[_type == $type && slug.current == $slug][0]{ _id, "slug": slug.current }`, { type, slug });
1600
+ }
1601
+
1602
+ private async createDocumentFromProduct(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1603
+ const data = await this.transformProductData(product, defaultLanguageCode, productSlug);
1604
+ if (!data) return;
1605
+ await this.mutate([{ create: data }]);
1606
+ Logger.info(`Created product ${product.id} in Sanity`);
1607
+ }
1608
+
1609
+ // Additional CRUD methods like updateDocumentFromProduct and deleteDocumentFromProduct
1610
+ // follow similar patterns with PUT and DELETE HTTP methods respectively.
1611
+ // Full implementations are available in the working example repository.
1612
+
1613
+ ```
1614
+
1615
+ ---
1616
+
1617
+ **Configuration**
1618
+
1619
+ Add Sanity options to your plugin types and configure via `vendure-config.ts`.
1620
+
1621
+ ```ts title="src/plugins/cms/types.ts"
1622
+ export interface PluginInitOptions {
1623
+ // ... existing options
1624
+ sanityApiKey?: string;
1625
+ sanityProjectId?: string;
1626
+ sanityDataset?: string;
1627
+ }
1628
+ ```
1629
+
1630
+ ```ts title="src/vendure-config.ts"
1631
+ CmsPlugin.init({
1632
+ sanityApiKey: process.env.SANITY_API_KEY,
1633
+ sanityProjectId: process.env.SANITY_PROJECT_ID,
1634
+ sanityDataset: process.env.SANITY_DATASET,
1635
+ }),
1636
+ ```
1637
+
1638
+ **Environment Variables**
1639
+
1640
+ ```env
1641
+ SANITY_API_KEY=your_sanity_api_key
1642
+ SANITY_PROJECT_ID=your_project_id
1643
+ SANITY_DATASET=production
1644
+ ```
1645
+
1646
+ For the complete implementation (including variants and collections, advanced error handling, and bulk operations), see:
1647
+
1648
+ https://github.com/vendurehq/examples/tree/master/examples/sanity-cms-integration
1649
+
1650
+ </TabItem>
1651
+ <TabItem value="payload" label="Payload">
1652
+
1653
+ :::tip Working Example
1654
+ The complete, production-ready Payload implementation can be found in the [Payload integration example](https://github.com/vendurehq/examples/tree/master/examples/payload-cms-integration). It includes advanced features like local API communication, collection management, and relationship handling.
1655
+ :::
1656
+
1657
+ This guide provides a complete integration for Vendure with Payload CMS, including setting up a Payload application and implementing the synchronization service.
1658
+
1659
+ **Setting up Payload CMS**
1660
+
1661
+ #### 1. Create a new Payload project
1662
+
1663
+ Initialize your Payload project using the CLI:
1664
+
1665
+ ```bash
1666
+ npx create-payload-app@latest payload-integration-app
1667
+ cd payload-integration-app
1668
+ ```
1669
+
1670
+ Choose the following options when prompted:
1671
+
1672
+ - Use TypeScript? **Yes**
1673
+ - Choose a database: **SQLite** (or your preferred database)
1674
+ - Package manager: **npm**
1675
+
1676
+ #### 2. Configure development server
1677
+
1678
+ Update your development script to use port 3001 to avoid conflicts with Vendure:
1679
+
1680
+ ```json title="package.json"
1681
+ {
1682
+ "scripts": {
1683
+ "dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon --exec tsx src/server.ts",
1684
+ "dev:payload": "payload dev --port 3001"
1685
+ }
1686
+ }
1687
+ ```
1688
+
1689
+ **Defining Collection Schemas**
1690
+
1691
+ #### 1. Create Vendure-specific collections
1692
+
1693
+ Create the collection configurations in your `collections` folder:
1694
+
1695
+ **VendureEntities.ts**
1696
+
1697
+ ```ts title="src/collections/VendureEntities.ts"
1698
+ import type { CollectionConfig } from 'payload/types';
1699
+
1700
+ export const VendureProduct: CollectionConfig = {
1701
+ slug: 'vendure-product',
1702
+ // WARNING: Development-only access! This grants unrestricted CRUD access to anyone.
1703
+ // For production, implement proper authentication and role-based access control.
1704
+ access: {
1705
+ // DEV ONLY: Public read access
1706
+ read: () => true,
1707
+ create: () => true,
1708
+ update: () => true,
1709
+ delete: () => true,
1710
+ },
1711
+ fields: [
1712
+ {
1713
+ name: 'id',
1714
+ type: 'number',
1715
+ required: true,
1716
+ unique: true,
1717
+ },
1718
+ {
1719
+ name: 'name',
1720
+ type: 'text',
1721
+ required: true,
1722
+ },
1723
+ {
1724
+ name: 'slug',
1725
+ type: 'text',
1726
+ required: true,
1727
+ },
1728
+ {
1729
+ name: 'productVariants',
1730
+ type: 'relationship',
1731
+ relationTo: 'vendure-product-variant',
1732
+ hasMany: true,
1733
+ },
1734
+ ],
1735
+ };
1736
+
1737
+ // Additional collection configurations for VendureProductVariant and VendureCollection
1738
+ // follow the same pattern with appropriate field definitions and relationships.
1739
+
1740
+ // export const VendureProductVariant: CollectionConfig = { ... }
1741
+ // export const VendureCollection: CollectionConfig = { ... }
1742
+ ```
1743
+
1744
+ #### 2. Register collections in Payload config
1745
+
1746
+ Update your `payload.config.ts` to include the Vendure collections:
1747
+
1748
+ ```ts title="src/payload.config.ts"
1749
+ import { VendureProduct, VendureProductVariant, VendureCollection } from './collections/VendureEntities';
1750
+
1751
+ export default buildConfig({
1752
+ // ... other config
1753
+ collections: [
1754
+ VendureProduct,
1755
+ VendureProductVariant,
1756
+ VendureCollection,
1757
+ // ... other collections
1758
+ ],
1759
+ });
1760
+ ```
1761
+
1762
+ #### 3. Run migrations
1763
+
1764
+ After setting up your collections, create and run migrations:
1765
+
1766
+ ```bash
1767
+ # Create a fresh migration (drops all tables and recreates)
1768
+ npm payload migrate:fresh
1769
+
1770
+ # Alternative: Create specific migration (safer for production)
1771
+ npm payload migrate:create add-vendure-collections
1772
+ npm payload migrate
1773
+
1774
+ # Generate TypeScript types
1775
+ npm payload generate:types
1776
+ ```
1777
+
1778
+ **The Payload Service**
1779
+
1780
+ The `PayloadService` handles all Payload-specific operations including local API communication, document management, and relationship resolution. Key features include:
1781
+
1782
+ - **Local API Communication**: Direct communication with Payload's local API
1783
+ - **Document Management**: CRUD operations for Payload documents
1784
+ - **Relationship Handling**: Manages references between products, variants, and collections
1785
+
1786
+ ---
1787
+
1788
+ **Basic Service Structure**
1789
+
1790
+ The `PayloadService` handles all Payload-specific operations including local API communication, document management, and relationship resolution.
1791
+
1792
+ ```ts title="src/plugins/cms/services/payload.service.ts"
1793
+ @Injectable()
1794
+ export class PayloadService {
1795
+ private readonly payloadBaseUrl = this.options.payloadBaseUrl || 'http://localhost:3001/api';
1796
+
1797
+ constructor(@Inject(CMS_PLUGIN_OPTIONS) private options: PluginInitOptions) {}
1798
+
1799
+ async syncProduct({ product, defaultLanguageCode, operationType }) {
1800
+ switch (operationType) {
1801
+ case 'create':
1802
+ return this.createDocumentFromProduct(product, defaultLanguageCode);
1803
+ case 'update':
1804
+ return this.updateDocumentFromProduct(product, defaultLanguageCode);
1805
+ case 'delete':
1806
+ return this.deleteDocumentFromProduct(product);
1807
+ }
1808
+ }
1809
+ }
1810
+ ```
1811
+
1812
+ ---
1813
+
1814
+ **Making API Requests**
1815
+
1816
+ All Payload API communication is centralized through a single method that handles authentication, error handling, and response parsing. This approach ensures consistent behavior across all operations.
1817
+
1818
+ ```ts
1819
+ private getPayloadHeaders(): Record<string, string> {
1820
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
1821
+ if (this.options.payloadApiKey) {
1822
+ headers.Authorization = `Bearer ${this.options.payloadApiKey}`;
1823
+ }
1824
+ return headers;
1825
+ }
1826
+
1827
+ private async makePayloadRequest({
1828
+ method,
1829
+ endpoint,
1830
+ data,
1831
+ }: {
1832
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
1833
+ endpoint: string;
1834
+ data?: any;
1835
+ }) {
1836
+ const url = `${this.payloadBaseUrl}/${endpoint}`;
1837
+ const res = await fetch(url, {
1838
+ method,
1839
+ headers: this.getPayloadHeaders(),
1840
+ body: data && (method === 'POST' || method === 'PATCH') ? JSON.stringify(data) : undefined,
1841
+ });
1842
+ if (!res.ok) {
1843
+ throw new Error(`Payload API error: ${res.status} ${res.statusText}`);
1844
+ }
1845
+ return method === 'DELETE' ? {} : res.json();
1846
+ }
1847
+ ```
1848
+
1849
+ ---
1850
+
1851
+ **Data Transformation**
1852
+
1853
+ Transformation methods convert Vendure entities into the format expected by Payload's API. The document structure must match the collection schema defined in Payload.
1854
+
1855
+ ```ts
1856
+ private async transformProductData(
1857
+ product: Product,
1858
+ defaultLanguageCode: LanguageCode,
1859
+ productSlug?: string | null,
1860
+ ) {
1861
+ const t = product.translations.find(tr => tr.languageCode === defaultLanguageCode);
1862
+ if (!t) return undefined;
1863
+
1864
+ const variantDocumentIds = await this.findVariantDocumentsForProductIds(
1865
+ product.id,
1866
+ defaultLanguageCode,
1867
+ productSlug,
1868
+ );
1869
+
1870
+ return {
1871
+ id: parseInt(product.id.toString()),
1872
+ name: t.name,
1873
+ slug: t.slug,
1874
+ productVariants: variantDocumentIds,
1875
+ };
1876
+ }
1877
+ ```
1878
+
1879
+ ---
1880
+
1881
+ **Relationship Handling**
1882
+
1883
+ One of the most important aspects of CMS integration is maintaining relationships between entities. Payload uses document IDs to create references between content pieces.
1884
+
1885
+ - Finding Related Entities
1886
+
1887
+ > First, we need methods to query the Vendure database for related entities:
1888
+
1889
+ ```ts
1890
+ private async findProductVariants(productId: string | number): Promise<ProductVariant[]> {
1891
+ return this.connection.rawConnection.getRepository(ProductVariant).find({
1892
+ where: { productId: productId },
1893
+ relations: ['translations'],
1894
+ order: { id: 'ASC' },
1895
+ });
1896
+ }
1897
+ ```
1898
+
1899
+ - **Building Relationships**
1900
+
1901
+ > Then, we combine database queries with CMS lookups to build relationships:
1902
+
1903
+ ```ts
1904
+ private async findVariantDocumentsForProductIds(
1905
+ productId: string | number,
1906
+ defaultLanguageCode: LanguageCode,
1907
+ productSlug?: string | null,
1908
+ ): Promise<string[]> {
1909
+ if (!productSlug) return [];
1910
+ const variants = await this.findProductVariants(productId);
1911
+ const slugs = variants.map(v => `${productSlug}-variant-${v.id}`);
1912
+ if (slugs.length === 0) return [];
1913
+ const docs = await this.findDocumentsBySlugs('vendure-product-variant', slugs);
1914
+ const ids: string[] = [];
1915
+ docs.forEach(doc => {
1916
+ if (doc?.id) {
1917
+ ids.push(String(doc.id));
1918
+ }
1919
+ });
1920
+ return ids;
1921
+ }
1922
+
1923
+ private async findParentProductDocumentId(
1924
+ variant: ProductVariant,
1925
+ defaultLanguageCode: LanguageCode,
1926
+ ): Promise<string | null> {
1927
+ const product = await this.connection.rawConnection.getRepository(Product).findOne({
1928
+ where: { id: variant.productId },
1929
+ relations: ['translations'],
1930
+ });
1931
+ if (!product) return null;
1932
+ const t = product.translations.find(tr => tr.languageCode === defaultLanguageCode);
1933
+ if (!t?.slug) return null;
1934
+ const doc = await this.findDocumentBySlug('vendure-product', t.slug);
1935
+ return doc?.id ? String(doc.id) : null;
1936
+ }
1937
+ ```
1938
+
1939
+ ---
1940
+
1941
+ **CRUD Operations**
1942
+
1943
+ These methods handle the basic Create, Read, Update, and Delete operations for documents in Payload. They follow REST API conventions and leverage the centralized request method for consistent behavior.
1944
+
1945
+ ```ts
1946
+ private async findDocumentBySlug(collection: string, slug: string) {
1947
+ const res = await this.makePayloadRequest({ method: 'GET', endpoint: `${collection}?where[slug][equals]=${encodeURIComponent(slug)}&limit=1` });
1948
+ return res.docs?.[0] ?? null;
1949
+ }
1950
+
1951
+ private async createDocumentFromProduct(product: Product, defaultLanguageCode: LanguageCode, productSlug?: string | null) {
1952
+ const data = await this.transformProductData(product, defaultLanguageCode, productSlug);
1953
+ if (!data) return;
1954
+ const res = await this.makePayloadRequest({ method: 'POST', endpoint: 'vendure-product', data });
1955
+ Logger.info(`Created product ${product.id} (Payload ID: ${res.doc?.id})`);
1956
+ }
1957
+
1958
+ // Additional CRUD methods like updateStoryFromProduct and deleteStoryFromProduct
1959
+ // follow similar patterns with PUT and DELETE HTTP methods respectively.
1960
+ // Full implementations are available in the working example repository.
1961
+
1962
+ ```
1963
+
1964
+ **Configuration**
1965
+
1966
+ Update your plugin configuration to include Payload options:
1967
+
1968
+ ```ts title="src/plugins/cms/types.ts"
1969
+ export interface PluginInitOptions {
1970
+ // ... existing options
1971
+ payloadApiKey?: string;
1972
+ payloadBaseUrl?: string;
1973
+ }
1974
+ ```
1975
+
1976
+ **Environment Variables**
1977
+
1978
+ Add these to your `.env` file:
1979
+
1980
+ ```env
1981
+ PAYLOAD_API_KEY=your_payload_api_key
1982
+ PAYLOAD_BASE_URL=http://localhost:3001/api
1983
+ ```
1984
+
1985
+ **Final Plugin Configuration**
1986
+
1987
+ ```ts title="src/vendure-config.ts"
1988
+ CmsPlugin.init({
1989
+ payloadApiKey: process.env.PAYLOAD_API_KEY,
1990
+ payloadBaseUrl: process.env.PAYLOAD_BASE_URL || 'http://localhost:3001/api',
1991
+ }),
1992
+ ```
1993
+
1994
+ This section provides a high-level overview. For a complete, production-ready implementation, see the Payload integration example:
1995
+ https://github.com/vendurehq/examples/tree/master/examples/payload-cms-integration
1996
+
1997
+ </TabItem>
1998
+ </Tabs>
1999
+
2000
+ ## Admin API Integration
2001
+
2002
+ To allow for manual synchronization through graphQL mutations, we can extend the Admin API with new mutations. Let's use the CLI to generate the API extensions:
2003
+
2004
+ ```bash
2005
+ # Generate API extensions for the CMS plugin
2006
+ npx vendure add -a CmsSyncAdminResolver --selected-plugin CmsPlugin
2007
+ ```
2008
+
2009
+ ### Extending the GraphQL API
2010
+
2011
+ ```ts title="src/plugins/cms/api/api-extensions.ts"
2012
+ export const adminApiExtensions = gql`
2013
+ extend type Mutation {
2014
+ syncProductToCms(productId: ID!): SyncResponse!
2015
+ syncCollectionToCms(collectionId: ID!): SyncResponse!
2016
+ }
2017
+ // ...
2018
+ `;
2019
+ ```
2020
+
2021
+ ### Implementing the Resolver
2022
+
2023
+ The resolver for these mutations re-uses the existing `CmsSyncService` to add a job to the queue.
2024
+
2025
+ ```ts title="src/plugins/cms/api/cms-sync-admin.resolver.ts"
2026
+ @Resolver()
2027
+ export class CmsSyncAdminResolver {
2028
+ constructor(private cmsSyncService: CmsSyncService) {}
2029
+
2030
+ @Mutation()
2031
+ @Allow(Permission.UpdateCatalog)
2032
+ async syncProductToCms(@Args() args: { productId: ID }): Promise<SyncResponse> {
2033
+ // This creates the data payload for the job
2034
+ const syncData: SyncJobData = {
2035
+ entityType: 'Product',
2036
+ entityId: args.productId,
2037
+ operationType: 'update', // Manual syncs are usually 'update'
2038
+ timestamp: new Date().toISOString(),
2039
+ retryCount: 0,
2040
+ };
2041
+ // The service method adds the job to the queue
2042
+ await this.cmsSyncService.triggerProductSync(syncData);
2043
+
2044
+ return {
2045
+ success: true,
2046
+ message: `Successfully queued sync for product ${args.productId}.`,
2047
+ };
2048
+ }
2049
+
2050
+ // ... resolver for collection sync
2051
+ }
2052
+ ```
2053
+
2054
+ ## Final Configuration
2055
+
2056
+ Finally, add the plugin to your `vendure-config.ts` file with the appropriate configuration for your chosen CMS platform.
2057
+
2058
+ ```ts title="src/vendure-config.ts"
2059
+ import { VendureConfig } from '@vendure/core';
2060
+ import { CmsPlugin } from './plugins/cms/cms.plugin';
2061
+
2062
+ export const config: VendureConfig = {
2063
+ // ... other config
2064
+ plugins: [
2065
+ // ... other plugins
2066
+ CmsPlugin.init({
2067
+ // Configure based on your chosen CMS platform
2068
+ // See platform-specific tabs above for exact configuration
2069
+ cmsApiKey: process.env.CMS_API_KEY,
2070
+ // Additional CMS-specific options...
2071
+ }),
2072
+ ],
2073
+ };
2074
+ ```
2075
+
2076
+ Refer to the platform-specific configuration examples in the tabs above for the exact environment variables and options needed for your chosen CMS.
2077
+
2078
+ For complete, production-ready implementations, see the working examples:
2079
+
2080
+ - [Storyblok CMS Integration](https://github.com/vendurehq/examples/tree/master/examples/storyblok-cms-integration)
2081
+ - [Strapi CMS Integration](https://github.com/vendurehq/examples/tree/master/examples/strapi-cms-integration)
2082
+ - [Sanity CMS Integration](https://github.com/vendurehq/examples/tree/master/examples/sanity-cms-integration)
2083
+ - [Payload CMS Integration](https://github.com/vendurehq/examples/tree/master/examples/payload-cms-integration)