# Run batch migrations with the Management SDK

Learn how to use the Hygraph Management SDK to run batch migrations, compare environments using getEnvironmentDiff, and apply schema changes. Understand supported schema elements, limitations, and best practices to prevent content loss.

With batch migrations, you can apply multiple schema updates to an environment in a single operation.

This workflow uses the `Client.getEnvironmentDiff` method to get the diff, then submits all required schema changes at once. The migration either succeeds completely or fails entirely. If any step fails, no changes are applied. This reduces the risk of partial or inconsistent updates.

While supported and commonly used, submitting schema changes directly to production does not guarantee zero risk, especially when content already exists.

## How it works

Submitting batch changes is a two-step process:

1. **Generate a diff**: The Management SDK compares the source environment with a target environment using `getEnvironmentDiff` and produces a list of schema operations required to make the target match the source.

2. **Submit the diff as a single batch**: The generated diff is submitted using `applySchemaChanges`, which applies all operations together in a single action.

The batch either succeeds completely or fails entirely. If any operation fails validation or execution, the entire batch is rolled back.

**Note:**

This workflow is commonly recommended for customers, who have two environments by default, such as those on the Pro plan. It works on any plan where you have at least two environments.

### Supported schema elements

The following schema elements are supported for batch changes submissions:

    <div className="flex flex-col md:flex-row md:gap-4">
      <div className="flex-1">
        - Models
        - Components
        - Locales
        - Simple fields
        - Conditional visibility in fields
        - Relational fields
        - Enumerations
        - Enumerable fields
        - Initial values in enumeration fields
      </div>
      <div className="flex-1">
        - Stages
        - Union fields
        - Apps
        - Custom renderers and app fields
        - Sidebar elements
        - Remote fields
        - Remote type definitions
        - Remote sources
      </div>
    </div>

**Warning:****Not supported:** UI extensions.

## Get the environment name

First, you need the environment name. For this query, you need to provide the project ID. You can find the Project ID in [Project settings](https://hygraph.com/docs/developer-guides/project/manage-project-info) or in the project URL - `https://app.hygraph.com/<projectId>/<environmentId>/`.

1. Navigate to the **API Playground** in your Hygraph project.
2. In the **API selector** dropdown, select the **Management API**.
3. Run the following query to get the environment names:

```graphql
query MyQuery {
  viewer {
    project(id: "<your-project-id>") {
      environments {
        name
        id
      }
    }
  }
}
```

## Create a Management SDK client

Create a new file, for example `submit-batch-changes.ts`, and initialize the client with the following parameters:

```ts
import { Client } from '@hygraph/management-sdk';

const client = new Client({
  authToken,
  endpoint,
  name, // optional
});
```

| Option                      | Description                                         |
|-----------------------------|-----------------------------------------------------|
| `authToken`                 | Permanent auth token for your project. This can be retrieved from your [Hygraph project](https://app.hygraph.com/) in **Project Settings > Permanent Auth Tokens > Token**. Make sure the token has proper Management API permissions depending on what you plan to execute via the SDK.               |
| `endpoint`                  | Endpoint of the High Performance Content API that belongs to the environment that you will work with. The URL can be retrieved from your [Hygraph project](https://app.hygraph.com/) in **Project Settings > Endpoints > High Performance Content API**.            |
| `name`                      | Optional identifier used for logging and debugging. Every migration has a unique name within an environment. If unspecified, a name will be generated and will be part of the response of a successful migration. Subsequent migrations with the same name in the same environment will fail.   |

For more information, [read this document](https://hygraph.com/docs/getting-started/access-and-permissions/api-access).

## Generate a diff

Use the `getEnvironmentDiff` method to generate a list of operations required to make the target environment match the specified source environment.

In this example:
- The client is initialized with the `master` environment's auth token and endpoint. This is the target environment where changes will be applied.
- In the `getEnvironmentDiff` method, we pass `development` as the parameter. This is the source environment where changes come from.
- The result is a list of operations that will make `master` (target) match `development` (source).

```ts
const diff = await client.getEnvironmentDiff("development");
```

This returns operations to sync schema changes from `development` (source) to `master` (target).

### Required fields in diffs

When you make a field **required** in the source environment (`development`), existing content in the target environment (`master`) may contain `null` values for that field. To apply this change successfully, you must provide a migration value that replaces `null` for existing entries.

![Migration value in field validations](https://hygraph.com/images/docs/api-reference/schema/env-diffing-migrationvalue.png)

When generating a diff, the Management SDK suggests updating the field to `required`, but it does not automatically include a migration value. If you apply the diff without adding one, the operation will fail.

Before applying the diff, you must manually add a `migrationValue` to the corresponding change.

**Note:**

For boolean fields, use `true` or `false` as the migration value instead of a string.


```json
{
  "changes": [
    {
      "createSimpleField": {
        "apiId": "newField",
        "parentApiId": "Post",
        "type": "STRING",
        "displayName": "NewField",
        "description": null,
        "initialValue": null,
        "tableRenderer": "GCMS_SINGLE_LINE",
        "formRenderer": "GCMS_SINGLE_LINE",
        "tableExtension": null,
        "formExtension": null,
        "formConfig": {},
        "tableConfig": {},
        "isList": false,
        "isLocalized": false,
        "isRequired": true,
        "isUnique": false,
        "isHidden": false,
        "embeddableModels": [],
        "visibility": "READ_WRITE",
        "isTitle": false,
        "position": 3,
        "validations": null,
        "embedsEnabled": null,
        "migrationValue": "value"
      }
    }
  ]
}
```

The provided `migrationValue` replaces existing `null` values when batch changes are applied.

## Apply schema changes

Use the following method to schedule the generated diff for the target environment:

```ts
client.applySchemaChanges(diff);
```
This schedules all operations from the diff. 

## Dry run a migration

You can dry run your migration to preview what changes would be applied.

```ts
const changes = client.dryRun();

console.log(changes);
```

Inspect the `changes` array and validate the list of operations that will be applied to the target environment.

## Run a migration in production

The `run()` method executes the applied schema changes, making the target environment (`master`) match the source environment (`development`).

```ts
const result = await client.run(true);

if (result.errors) {
  console.log(result.errors);
} else {
  console.log(result.name);
}
```

## Full example

Here's a complete workflow showing source/target relationships. All schema changes from `development` (source) are applied to `master` (target), making `master` identical to `development`.

```ts
import { Client } from '@hygraph/management-sdk';

async function syncEnvironments() {
  // Create client for TARGET environment (where changes will be applied)
  const targetClient = new Client({
    authToken: '',
    endpoint: '',
    name: 'sync-dev-to-master'
  });

  // Generate diff from SOURCE environment (where changes come from)
  const diff = await targetClient.getEnvironmentDiff("development");

  // Apply schema changes to TARGET
  try {
    targetClient.applySchemaChanges(diff);
    const result = await targetClient.run(true);
    
    if (result.errors) {
      console.error('Migration failed with errors:', result.errors);
      return;
    }
    
    console.log('Successfully synced master to match development');
    console.log(`Migration name: ${result.name}`);
    console.log(`Finished at: ${result.finishedAt}`);
  } catch (error) {
    console.error('Migration failed:', error);
  }
}

syncEnvironments();
```

## How schema changes affect the target environment

Batch changes submitted with the Management SDK do not merge schemas. Instead, the target schema is replaced so that it exactly matches the source schema.

The SDK first generates a diff using `getEnvironmentDiff`, which lists the operations required to transform the target environment (`master`) into the source environment (`development`). When this diff is applied, schema elements are added, modified, or deleted as needed to achieve an exact match. Any schema elements present in `master` but missing or different in `development`, including models, fields, and sidebar elements, may be overwritten or deleted.

If a schema change exists only in `master`, the diff will include operations to remove or overwrite that change. To reduce the risk of unintended deletions or content loss:

- Freeze schema changes in `master` while working in `development`
- Keep schema changes mirrored across environments as you go
- Always review the generated diff before applying it

### Example scenarios

- You clone `master` to create `development`, make schema changes in `development`, and later make additional schema changes directly in `master`. When you generate and apply the diff, the SDK will suggest deleting the changes that exist only in `master`, because they are not present in `development`.

- A field named **Title Field** exists in both environments, but you rename it to **Title** only in `master`. The diff will suggest deleting **Title** and recreating **Title Field**, which aligns the schemas but results in content loss.

- You delete a field in `development` and then create a new field with the same name. The diff may not detect this as a deletion and recreation, even though the underlying field identity has changed.
