Skip to Content
CLIimport

flexkit import

Imports entities and assets in bulk from an NDJSON file, a directory or a tarball created by flexkit export. Referenced files are uploaded first and connected to entities by _id, so a single import file can describe a full dataset — including images and entity relationships.

Synopsis

flexkit import <file.ndjson | dir | tarball> [options]

Accepted inputs

  • NDJSON file — one entity per line. Relative file references resolve against the file’s directory.
  • Directory — must contain a data.ndjson (entities) and/or an assets.ndjson (asset metadata) with the referenced files alongside.
  • Tarball (.tar.gz, .tgz, .tar) — same layout as a directory; this is what flexkit export and flexkit assets export produce.

Import file format

Each line of data.ndjson is a JSON object describing one entity:

{
  "_type": "product",
  "_id": "product-chair",
  "name": "Chair",
  "description": "A red chair",
  "image": { "_asset": "file://images/chair.jpg" },
  "gallery": [{ "_asset": "https://example.com/detail.jpg" }],
  "brand": { "_ref": "brand-acme" }
}
  • _type (required): the entity name (or plural) as defined in your schema.
  • _id (optional): a stable id for the entity. Omitting it generates a random UUID — but supplying deterministic ids is strongly recommended, since they make re-runs idempotent and enable _ref relationships.
  • Scalar attributes (global scope): plain JSON values.
  • Localized attributes (scope: 'local'): a plain JSON value writes the default scope. To set several scopes, use {"_scopes": {"default": "Chair", "gb": "Armchair"}} — one entry per scope name defined in the project’s scopes config. Unknown scope names fail validation.
  • Asset attributes (dataType: 'asset'): {"_asset": "<source>"} where the source is a file:// path (relative to the NDJSON file), a plain relative path, or an https:// URL.
  • Asset relationships (relationship to _asset, multiple mode): an array of {"_asset": ...} values. Array position becomes the sortOrder of each connection.
  • Entity relationships: {"_ref": "<entity _id>"} (single mode) or an array of refs (multiple mode). Refs may point at entities defined later in the same file — they are connected in a second pass after all entities exist.

Unknown types or attributes fail validation before anything is written.

Pipeline

  1. Parse and validate every line against the local config’s schema; verify referenced local files exist. Any error aborts the import before uploads or mutations run.
  2. Upload assets — each unique source is uploaded once through the one-shot assets endpoint. SHA-256 deduplication means re-importing the same files never creates duplicates.
  3. Create entities in batches using GraphQL variables (no string interpolation). If a batch fails, its items retry one-by-one so a single conflict doesn’t sink the batch. Asset connections are made inline by _id.
  4. Connect _ref relationships in a second pass, so forward references between lines are fine.

Idempotency and re-runs

  • Assets are deduplicated by content hash — re-runs upload nothing new.
  • Entities with an _id that already exists are skipped by default (--skip-existing).
  • With --replace, existing entities are updated instead: scalar values are set (for localized attributes, only the scopes present in the file are touched), and asset/relationship connections are diffed against the current state (removed connections are disconnected, new ones connected, order updated).

Options

  • --project <ID>: Select the project when the local config defines several.
  • --dry-run: Validate the input and report what would happen. Nothing is uploaded or created.
  • --skip-existing: Skip entities whose _id already exists (default).
  • --replace: Update entities whose _id already exists instead of skipping them.
  • --batch-size <N>: Entities per create mutation (default 10).
  • --concurrency <N>: Parallel asset uploads (default 5).
  • --tag <NAME>: Tag every imported asset with the given tag.
  • --json: Print the import summary as JSON on stdout.

The command exits non-zero if any entity or asset failed, and prints a created / replaced / skipped / failed summary.

Worked example: products with images

products.ndjson:

{"_type": "brand", "_id": "brand-acme", "name": "Acme"}
{"_type": "product", "_id": "product-chair", "name": "Chair", "image": {"_asset": "file://images/chair.jpg"}, "gallery": [{"_asset": "file://images/chair-front.jpg"}, {"_asset": "file://images/chair-side.jpg"}], "brand": {"_ref": "brand-acme"}}
{"_type": "product", "_id": "product-table", "name": "Table", "image": {"_asset": "https://example.com/table.jpg"}, "brand": {"_ref": "brand-acme"}}
# Validate first
flexkit import products.ndjson --dry-run
 
# Import (images upload first, then entities, then relationships)
flexkit import products.ndjson
 
# Re-run safely: assets dedupe, existing entities are skipped
flexkit import products.ndjson
 
# Update existing entities with changed values instead
flexkit import products.ndjson --replace
Last updated on

© 2026