npm package: @pintawebware/strapi-sync
Description: Export Strapi content to snapshot JSON and apply snapshot content to Strapi
Latest version: 1.0.4

# strapi-sync

Export Strapi schemas and content to a snapshot file and apply snapshot changes back to Strapi.

## Configuration

Optional config file in the project root: `.strapi-sync.json`, `strapi-sync.config.json`, or `strapi-sync.json`.

`strapi-sync` also loads `.env` from the project root before resolving environment fallback values.

`.env` example:

```env
STRAPI_URL=https://cms.example.com
STRAPI_API_TOKEN=your-api-token
STRAPI_PROJECT_PATH=/var/www/strapi-app
STRAPI_SSH_HOST=cms.example.com
STRAPI_SSH_USER=deploy
STRAPI_SSH_PORT=22
STRAPI_SSH_PASSWORD=your-password
STRAPI_SSH_PRIVATE_KEY_PATH=C:/Users/Ivan/.ssh/id_rsa
```

Minimal config example:

```json
{
  "strapiUrl": "https://your-strapi.example.com",
  "apiToken": "your-api-token",
  "strapiProjectPath": "/var/www/strapi-app",
  "strapiSSHUser": "deploy",
  "strapiSSHPassword": "your-password",
  "strapiSSHPrivateKeyPath": "C:/Users/Ivan/.ssh/id_rsa"
}
```

For local schema writes, set `strapiProjectPath` to the local project path.

For remote schema writes, set `strapiProjectPath` to the remote project path and add `strapiSSHHost` with either `strapiSSHPassword` or `strapiSSHPrivateKeyPath`.

Config values are applied in this order, from lower priority to higher priority:

1. `.env` or process environment values for fallback-supported fields
2. Project json config file
3. CLI flags

Supported environment variables:

- `STRAPI_URL`
- `STRAPI_API_TOKEN`
- `STRAPI_PROJECT_PATH`
- `STRAPI_SSH_HOST`
- `STRAPI_SSH_USER`
- `STRAPI_SSH_PORT`
- `STRAPI_SSH_PASSWORD`
- `STRAPI_SSH_PRIVATE_KEY_PATH`

Only these exact environment variable names are read by the CLI.

Environment variables are used only when the corresponding CLI or config value is missing.

Supported config fields:

- `strapiUrl` - required
- `apiToken` - required
- `output` - optional, default `strapi-snapshot.json`
- `strapiProjectPath` - optional for export and content-only sync; required for local or remote schema writes
- `strapiSSHHost` - optional, enables remote SSH schema writes when set
- `updateSchema` - optional, default `true`
- `strapiSSHUser` - optional, default `root` for remote SSH writes
- `strapiSSHPort` - optional, default `22` for remote SSH writes
- `strapiSSHPassword` - optional, no default
- `strapiSSHPrivateKeyPath` - optional, no default

## Usage

Export a snapshot from Strapi:

```bash
strapi-sync --export
```

Apply the current snapshot:

```bash
strapi-sync
```

Show package version:

```bash
strapi-sync --version
```

## Options

- `--export` – Optional. Export from Strapi to snapshot and exit. Default behavior without this flag is apply mode.
- `--output <path>` – Optional. Snapshot file path. Default `strapi-snapshot.json`.
- `-p, --project <path>` – Optional. Project directory used for config and snapshot resolution. Default is current working directory.
- `-u, --strapi-url <url>` – Optional if `strapiUrl` exists in config. Otherwise required.
- `--api-token <token>` – Optional if `apiToken` exists in config. Otherwise required.
- `--config <path>` – Optional. Use a custom JSON config file instead of the auto-discovered one.
- `--strapi-project <path>` – Optional. Override `strapiProjectPath` from config or `STRAPI_PROJECT_PATH`.
- `--strapi-ssh-host <host>` – Optional. Set the SSH host for remote schema writes.
- `--strapi-ssh-user <user>` – Optional. Override the SSH user for remote schema writes.
- `--strapi-ssh-port <port>` – Optional. Override the SSH port for remote schema writes.
- `--strapi-ssh-password <pwd>` – Optional. Use SSH password auth for remote schema writes.
- `--strapi-ssh-key <path>` – Optional. Use a private key file for remote schema writes.
- `-h, --help` – Optional. Show help and exit.
- `-v, --version` – Optional. Show the installed package version and exit.

## Behavior

- Running `strapi-sync` always prints a preview before confirmation.
- Running `strapi-sync --export` asks for confirmation before overwriting the snapshot file.
- Schema writes are controlled by `updateSchema` in config.
- When `strapiSSHHost` is set, `strapiProjectPath` is treated as a remote filesystem path and schema writes are executed over SSH.
- When `strapiSSHHost` is not set, `strapiProjectPath` is treated as a local filesystem path.
- Remote schema writes support password auth or private key auth.
- After any successful change, the snapshot is exported again from Strapi.
- Adding a field to schema without adding a value for that field in snapshot entries is treated as a schema-only change.
- Media fields are exported and shown in schema definitions, but content writes skip them.
- Unknown CLI arguments stop the command with an error.
- Invalid snapshot syntax stops the command with detailed validation errors.

## Snapshot Format

The snapshot file contains:

- `version`
- `contentTypes`
- `components`

Component definitions live in the top-level `components` block:

```json
{
  "components": {
    "shared.seo": {
      "attributes": {
        "metaTitle": "string",
        "metaDescription": "string"
      }
    }
  }
}
```

Each content type includes `singleType`, `attributes`, and `entries`.

Collection type example:

```json
{
  "singleType": false,
  "attributes": {
    "title": "string"
  },
  "entries": [
    {
      "title": "Example"
    }
  ]
}
```

Localized collection type example:

```json
{
  "singleType": false,
  "attributes": {
    "title": "string"
  },
  "entries": {
    "en": [
      {
        "title": "Example"
      }
    ],
    "uk": [
      {
        "title": "Приклад"
      }
    ]
  }
}
```

Single type example:

```json
{
  "singleType": true,
  "attributes": {
    "title": "string"
  },
  "entries": {
    "title": "Example"
  }
}
```

Localized single type example:

```json
{
  "singleType": true,
  "attributes": {
    "title": "string"
  },
  "entries": {
    "en": {
      "title": "Example"
    },
    "uk": {
      "title": "Приклад"
    }
  }
}
```

For `singleType: false`, `entries` must be an array or an object with locale keys.

For `singleType: true`, `entries` must be a single object or an object with locale keys that point to objects.

Supported attribute examples:

- `string`
- `number`
- `boolean`
- `date`
- `json`
- `media`
- `media[]`
- `component:shared.seo`
- `component:shared.gallery:repeatable`
- `["shared.media", "shared.rich-text"]` for dynamic zones
- `relation:article:oneToMany`

Component field example:

```json
{
  "singleType": false,
  "attributes": {
    "seo": "component:shared.seo"
  },
  "entries": [
    {
      "seo": {
        "metaTitle": "Example",
        "metaDescription": "Example description"
      }
    }
  ]
}
```

Repeatable component example:

```json
{
  "singleType": false,
  "attributes": {
    "gallery": "component:shared.gallery:repeatable"
  },
  "entries": [
    {
      "gallery": [
        {
          "title": "Slide 1"
        },
        {
          "title": "Slide 2"
        }
      ]
    }
  ]
}
```

Dynamic zone example:

```json
{
  "singleType": false,
  "attributes": {
    "blocks": [
      "shared.media",
      "shared.rich-text"
    ]
  },
  "entries": [
    {
      "blocks": [
        {
          "__component": "shared.rich-text",
          "body": "Example text"
        },
        {
          "__component": "shared.media",
          "caption": "Example image"
        }
      ]
    }
  ]
}
```

Relation attributes must use the short form like `relation:article:oneToMany`. During schema writes it is expanded to the full Strapi UID automatically.

Supported relation examples:

- `relation:article:oneToOne`
- `relation:article:oneToMany`
- `relation:article:manyToOne`
- `relation:article:manyToMany`
- `relation:article:oneWay`
- `relation:article:manyWay`
- `relation:article:morphOne`
- `relation:article:morphMany`