WooCommerce Import¶
Import from WooCommerce via a direct read of the WordPress/WooCommerce database or via the Woo REST API. The direct-database mode is the strongest path for a heavily customized shop whose data only exists in its tables; both modes normalize to the same field vocabulary, so one reviewed mapping serves either, and meta.* / usermeta.* fields are auto-routed into Vectis custom fields.
At a glance
| Extension id | woocommerce |
| Category | migration |
| Version | 0.1.0 |
| Provides | Import provider (products, variants, collections, customers, orders) |
| External account | WooCommerce REST API consumer key/secret, or read access to the store's database (per source mode) |
What It Provides¶
- An import provider with key
woocommerce(label "WooCommerce"), registered on the strategy resolver when the extension activates. It appears in the provider list of the admin Import Wizard via theimportProvidersGraphQL query. - Two source modes:
source_db(direct database read) andadmin_api(Woo REST API at/wp-json/wc/v3/). - Five entity types:
product,variant,collection,customer,order, with a dependency DAG — variants require products; orders require customers, products, and variants. Runs import dependencies before dependents, and the wizard flags unselected prerequisites. - Default field mappings per entity over the shared logical vocabulary (
name,slug,description,status,sku,price,external_id, …). WooCommercepost_statusvalues mappublish→published,draft/pending→draft,private/trash→archived. Discoveredmeta.*andusermeta.*fields are automatically routed intocustom_fields. - Import runs execute through the core
ImportRunWorkflowTemporal workflow (Building Extensions covers the strategy surface). The extension itself registers no workflows, webhook endpoints, admin pages, or GraphQL fields of its own.
Setup¶
- The extension loads at boot; it should appear under Settings > Extensions.
- Open Settings > Import Wizard in the admin (
/settings/import). - Provider step: pick WooCommerce.
- Entities step: choose what to import. The wizard flags unselected prerequisites (e.g. picking
orderwithoutproduct/variant/customer) and offers to add them, so cross-entity references stay linked. - Connect step — enter the connection config as JSON for the chosen source mode:
- Direct source database —
dsn(e.g.mysql+aiomysql://user:pw@host:3306/woo) and optionallytable_prefix(defaultwp_). - Live Admin API —
store_url,consumer_key,consumer_secret(the standard Woo REST key pair, sent as HTTP Basic auth).
- Direct source database —
- Map & review step: the provider's default mapping is pre-filled per entity; unmapped source fields can be routed into
custom_fields, and each entity's mapping can be saved as a reusable import profile. - Dry run step: previews create/update/skip/fail counts without writing anything — every row runs in a savepoint that a dry run rolls back.
- Run step: executes the import.
There is no separate test-connection step in the wizard: bad credentials surface when the wizard first discovers source fields on the way into Map & review. (The connectors implement a test_connection hook — DB mode runs SELECT 1 against the posts table, REST mode fetches one product from /products — but no wizard step currently invokes it.)
Configuration Reference¶
This provider has no extension-level config_schema — connection details are entered per run in the wizard's Connect step and are not stored (the persisted import-run record keeps the plan, not the connection config). Per source mode:
Direct source database mode
| Key | Type | Required | Secret | Description |
|---|---|---|---|---|
dsn |
string | Yes | Yes | SQLAlchemy async DSN, e.g. mysql+aiomysql://user:pw@host:3306/woo |
table_prefix |
string | No | No | WordPress table prefix, defaults to wp_ |
Admin API mode
| Key | Type | Required | Secret | Description |
|---|---|---|---|---|
store_url |
string | Yes | No | e.g. https://shop.example.com |
consumer_key |
string | Yes | Yes | WooCommerce REST API consumer key |
consumer_secret |
string | Yes | Yes | WooCommerce REST API consumer secret |
The DB connector also honors an optional batch_size key (default 500) controlling how many rows are fetched per keyset batch.
Permissions¶
The manifest declares no permissions of its own. Running imports and saving mapping profiles require the core import_export.import permission.
Operational Notes¶
- DB mode tables — products from
posts(post_type='product') +postmeta+wc_product_meta_lookup; variations fromposts(post_type='product_variation'); collections fromterms/term_taxonomy(product_cattaxonomy); customers fromusers+usermeta; order headers from the HPOSwc_ordersandwc_order_addressestables; order lines fromwoocommerce_order_items+woocommerce_order_itemmeta. All table names use the configuredtable_prefix, which is validated against^[A-Za-z0-9_]+$(an unsafe prefix is rejected outright). - DB mode performance — pagination is keyset-by-id in batches, and
postmeta/usermeta/item meta are fetched per batch rather than per row to avoid N+1 queries. - Simple products get a default variant — any product with a SKU in
wc_product_meta_lookupyields one variant, so simple (non-variable) products import with a sellable variant. - Order-line SKU resolution (DB mode) — each line's
_variation_id(variable products) or_product_id(simple products) is resolved to a real SKU throughwc_product_meta_lookup, so the order loader can match lines by SKU uniformly. - REST mode pagination — 100 records per page via the
X-WP-TotalPagesheader, with a 60-second HTTP timeout; variations are fetched per variable product (/products?type=variable, then/products/{id}/variations). - Status and fallbacks — DB-mode order statuses have the
wc-prefix stripped; a missing order status falls back toimportedand a missing currency toUSD. A variation without a SKU is assignedwoo-var-<id>. - Orders import as historical records — the default (and currently only working)
order_import_modeishistorical: order and line rows are written without triggering payment, inventory, or fulfillment workflows, and imported orders are stamped_importedin metadata. Selectinglivemode is rejected by the order loader (it raises per row instead of silently re-entering workflows). - Order lines must resolve to variants — each order line is matched to an existing variant by SKU (DB mode also falls back to the variation's stamped external id); an unresolvable line fails that order's row. Import products and variants before orders.
- Idempotent re-runs — external ids are the source's numeric ids (post/term/user/order ids as strings; simple-product default variants use the SKU); each successfully imported row records a
(provider, entity, external_id) → internal idcrossref. Loaders upsert by natural key — slug for products and collections, SKU for variants, email for customers, order number for orders — so re-running an import updates existing records instead of duplicating them. - Failure isolation — every row runs inside a database savepoint, so one bad row cannot poison the rest; failures are collected into the run's error report.
- Durability — runs execute as the core
ImportRunWorkflowon thevectis-mainTemporal task queue, with a 30-minute activity timeout and up to 3 retry attempts.
See also: Shopify Import, Building Extensions.