---
title: 'flexkit import'
description: 'Reference and usage guidance for flexkit import.'
---

# `flexkit import`

Imports entities and assets in bulk from an NDJSON file, a directory or a tarball created by [`flexkit export`](/docs/cli/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

```bash
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:

```json
{
  "_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`:

```json
{"_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"}}
```

```bash
# 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
```


---

[View full sitemap](/docs/sitemap.md)
