Importing Product Data

If you have hundreds, thousands or more products, inputting all the data by hand via the Admin UI can be too inefficient. To solve this, Vendure supports bulk-importing product and other data.

Data import is also useful for setting up test or demo environments, and is also used by the @vendure/testing package for end-to-end tests.

Product Import Format

Vendure uses a flat .csv format for importing product data. The format encodes data about:

  • products
  • product variants
  • product & variant assets
  • product & variant facets
  • product & variant custom fields

Here’s an example which defines 2 products, “Laptop” and “Clacky Keyboard”. The laptop has 4 variants, and the keyboard only a single variant.

name            , slug            , description               , assets                      , facets                              , optionGroups    , optionValues , sku         , price   , taxCategory , stockOnHand , trackInventory , variantAssets , variantFacets
Laptop          , laptop          , "Description of laptop"   , laptop_01.jpg|laptop_02.jpg , category:electronics|brand:Apple    , screen size|RAM , 13 inch|8GB  , L2201308    , 1299.00 , standard    , 100         , false          ,               , 
                ,                 ,                           ,                             ,                                     ,                 , 15 inch|8GB  , L2201508    , 1399.00 , standard    , 100         , false          ,               , 
                ,                 ,                           ,                             ,                                     ,                 , 13 inch|16GB , L2201316    , 2199.00 , standard    , 100         , false          ,               , 
                ,                 ,                           ,                             ,                                     ,                 , 15 inch|16GB , L2201516    , 2299.00 , standard    , 100         , false          ,               , 
Clacky Keyboard , clacky-keyboard , "Description of keyboard" , keyboard_01.jpg             , category:electronics|brand:Logitech ,                 ,              , A4TKLA45535 , 74.89   , standard    , 100         , false          ,               ,

Here’s an explanation of each column:

  • name: The name of the product. Rows with an empty “name” are interpreted as variants of the preceeding product row.
  • slug: The product’s slug. Can be omitted, in which case will be generated from the name.
  • description: The product description.
  • assets: One or more asset file names separated by the pipe (|) character. The files must be located on the local file system, and the path is interpreted as being relative to the importAssetsDir as defined in the VendureConfig. The first asset will be set as the featuredAsset.
  • facets: One or more facets to apply to the product separated by the pipe (|) character. A facet has the format <facet-name>:<facet-value>.
  • optionGroups: OptionGroups define what variants make up the product. Applies only to products with more than one variant.
  • optionValues: For each optionGroup defined, a corresponding value must be specified for each variant. Applies only to products with more than one variant.
  • sku: The Stock Keeping Unit (unique product code) for this product variant.
  • price: The price can be either with or without taxes, depending on your channel settings (can be set later).
  • taxCategory: The name of an existing tax category. Tax categories can be also be imported using the InitialData object.
  • stockOnHand: The number of units in stock.
  • trackInventory: Whether this variant should have its stock level tracked, i.e. the stock level is automatically decreased for each unit ordered.
  • variantAssets: Same as assets but applied to the product variant.
  • variantFacets: Same as facets but applied to the product variant.

Importing Custom Field Data

If you have CustomFields defined on your Product or ProductVariant entities, this data can also be encoded in the import csv:

  • product:<customFieldName>: The value of this column will populate Product.customFields[customFieldName].
  • variant:<customFieldName>: The value of this column will populate ProductVariant.customFields[customFieldName].

Importing relation custom fields

To import custom fields with the type relation, the value in the CSV must be a stringified object with an id property:

... ,product:featuredReview
... ,"{ ""id"": 123 }"

Importing list custom fields

To import custom fields with list set to true, the data should be separated with a pipe (|) character:

... ,product:keywords
... ,tablet|pad|android

Initial Data

As well as product data, other initialization data can be populated using the InitialData object. This format is intentionally limited; more advanced requirements (e.g. setting up ShippingMethods that use custom checkers & calculators) should be carried out via scripts which interact with the Admin GraphQL API.

import { InitialData, LanguageCode } from '@vendure/core';

export const initialData: InitialData = {
    defaultLanguage: LanguageCode.en,
    countries: [
        { name: 'Austria', code: 'AT', zone: 'Europe' },
        { name: 'Malaysia', code: 'MY', zone: 'Asia' },
        { name: 'United Kingdom', code: 'GB', zone: 'Europe' },
    defaultZone: 'Europe',
    taxRates: [
        { name: 'Standard Tax', percentage: 20 },
        { name: 'Reduced Tax', percentage: 10 },
        { name: 'Zero Tax', percentage: 0 },
    shippingMethods: [{ name: 'Standard Shipping', price: 500 }, { name: 'Express Shipping', price: 1000 }],
    collections: [
            name: 'Electronics',
            filters: [
                    code: 'facet-value-filter',
                    args: { facetValueNames: ['Electronics'], containsAny: false },
            assetPaths: ['jakob-owens-274337-unsplash.jpg'],
  • defaultLanguage: Sets the language which will be used for all translatable entities created by the initial data e.g. Products, ProductVariants, Collections etc. Should correspond to the language used in your product csv file.
  • countries: Defines which countries are available.
    • name: The name of the country in the language specified by defaultLanguage
    • code: A standardized code for the country, e.g. ISO 3166-1
    • zone: A Zone to which this country belongs.
  • defaultZone: Sets the default shipping & tax zone for the default Channel. The zone must correspond to a value of zone set in the countries array.
  • taxRates: For each item, a new TaxCategory is created, and then a TaxRate is created for each unique zone defined in the countries array.
  • shippingMethods: Allows simple flat-rate ShippingMethods to be defined.
  • collections: Allows Collections to be created. Currently, only collections based on facet values can be created (code: 'facet-value-filter'). The assetPaths and facetValueNames value must correspond to a values specified in the products csv file. The name should match the value specified in the product csv file (or can be a normalized - lower-case & hyphenated - version thereof). If there are FacetValues in multiple Facets with the same name, the facet may be specified with a colon delimiter, e.g. brand:apple, flavour: apple.

Populating The Server

The @vendure/core package exposes a populate() function which can be used along with the data formats described above to populate your Vendure server:

// populate-server.ts
import { bootstrap } from '@vendure/core';
import { populate } from '@vendure/core/cli';

import { config } from './vendure-config.ts';
import { initialData } from './my-initial-data.ts';

const productsCsvFile = path.join(__dirname, 'path/to/products.csv')

  () => bootstrap(config),
.then(app => {
  return app.close();
  () => process.exit(0),
  err => {