Management SDK

Our Management SDK is a helper that can be used to execute operations without using UI, making your work easier. It provides a typed SDK that allows multiple schema-related actions to be executed in order and in a single transaction.

AdvantagesAnchor

Our Management SDK makes work easier when you want to apply multiple schema-related changes. It allows accepting a set of changes which are applied on an “all or none” basis. So if anything fails in one of the events along the process, everything is rolled back automatically.

The Management SDK also provides a way of working on a project that bypasses the Web App, by allowing you to create a script that applies all the changes at once, without you having to procedurally create everything.

Finally, you can apply the scripts you create to different environments. So they can be used in test environments to test schema changes upfront. In this scenario, you would make all the schema changes in this migration script, test it, then apply the script to the master environment when everything works as intended.

How it worksAnchor

Our Management SDK works with names, using the submitBatchChanges mutation, as follows:

submitBatchChanges mutation
submitBatchChanges mutation

As mentioned above, the SDK uses a special mutation:

submitBatchChanges(data: BatchMigrationInput!): AsyncOperationPayload!

This mutation takes an environmentId, an optional name, and a list of changes as arguments, then executes them in a single transaction.

The following sections show you how to use the new Management SDK:

QuickstartAnchor

import { Client } from '@hygraph/management-sdk';
const client = new Client({
authToken: '...',
endpoint: '...',
});
const run = async () => {
client.createModel({
apiId: 'Post',
apiIdPlural: 'Posts',
displayName: 'Post',
});
const result = await client.run(true);
if (result.errors) {
throw new Error(result.errors);
}
return result;
};
run()
.then((result) => console.log(`Finished migration at: ${result.finishedAt}`))
.catch((err) => console.error('Error: ', err));

InstallationAnchor

Use the following command to install the package:

npm install @hygraph/management-sdk

UsageAnchor

To use the Management SDK you need to instantiate a client.

Creating the clientAnchor

To create the Management SDK client you need to pass the following parameters:

const { Client } = require('@hygraph/management-sdk');
const client = new Client({
authToken,
endpoint,
name, // optional
});
  • Authentication Token authToken: This can be retrieved from your Hygraph project in Settings > API Access > Permanent Auth Tokens. Make sure the token has proper management permissions depending on what you plan to execute via the SDK.
  • Hygraph Content API Endpoint endpoint: Endpoint of the Content API that belongs to the environment that you plan to interact with. The URL can be retrieved from your Hygraph project in Settings > Environments > Endpoints.
  • Migration Name name [optional]: 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.

At the moment, Hygraph only stores metadata for the last 30 migrations for each environment.

Every single time a new migration is applied to an environment, metadata regarding older migrations exceeding 30 - sorted by time of creation - are deleted. This means changes performed by older migrations do stay in place, however, the name and other information related to this migration will no longer be available.

Running a MigrationAnchor

The run method runs the migration.

const result = await client.run(foreground);
if (result.errors) {
console.log(result.errors);
} else {
console.log(result.name);
}

By default, migrations run in the foreground, meaning that the SDK client will return the results of all actions that were executed. Passing an optional boolean argument as false configures the migration to run in the background. A successful result here only means that the actions were successfully scheduled, but not executed.

Dry Run a MigrationAnchor

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

const changes = client.dryRun();
console.log(changes);

Supported operationsAnchor

All operations that can be executed by the SDK can be found in the TypeScript Type Definitions (Client.d.ts).

MigrationAnchor

To migrate from the previous SDK, which can be found here, there are a couple of changes that need to be made in order to use it with existing migration scripts.

The old SDK has been deprecated but won't be removed from NPM, so old scripts using it will still work in the future. New features, though, will only be available in the new SDK.

While the old SDK will continue to work, it has been deprecated and support will no longer be provided for it.

First of all the import of the NPM package has to be changed. For this, the package name needs to be changed to @hygraph/management-sdk. In the previous version, the SDK offered a named export newMigration that could be used to create a new migration. This changed with the new version, so a new migration must be created as follows:

import { Client } from '@hygraph/management-sdk';
const migration = new Client({
authToken: '...',
endpoint: '...',
});

The general methods on the migration class stayed the same, so running - or dryRunning - a migration will still work as before.

The major change are the operations that are supported to actually execute changes:

Before, the SDK was built in a fluent API style. This means you could chain operations like the following:

migration
.createModel({
apiId: 'Author',
apiIdPlural: 'Authors',
displayName: 'Author',
})
.addSimpleField({
apiId: 'firstName',
displayName: 'First Name',
type: FieldType.String,
});

The new SDK version no longer supports chaining. The reason behind that is that the SDK is now fully generated by the schema it is using to execute the migration. The previous example needs to be changed as follows:

migration.createModel({
apiId: 'Post',
apiIdPlural: 'Posts',
displayName: 'Post',
});
migration.createSimpleField({
apiId: 'firstName',
displayName: 'First Name',
type: SimpleFieldType.String,
modelApiId: 'Post',
});

To link the second operation to the first one - to create the field on the created model in the first operation - the modelApiId must be passed into the operation.

ExamplesAnchor

Create different field typesAnchor

The following example shows you how to use the Management SDK with different field types.

Please consider that the example shown here creates fields in a certain order because said fields depend on something being created before them as part of the schema.

If you prefer to read a step by step version of this example, please click here.

If what you're looking for is an example on how to work with a specific field type, this additional document will help you.

import {
Client,
RelationalFieldType,
SimpleFieldType,
VisibilityTypes,
} from '@hygraph/management-sdk';
const client = new Client({
authToken: '',
endpoint: '',
});
client.createModel({
apiId: 'Page',
apiIdPlural: 'Pages',
displayName: 'Page',
});
client.createModel({
apiId: 'Author',
apiIdPlural: 'Authors',
displayName: 'Author',
});
client.createModel({
apiId: 'Book',
apiIdPlural: 'Books',
displayName: 'Book',
});
// required title field of type string:
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.String,
apiId: 'title',
displayName: 'Title',
isTitle: true,
isRequired: true,
visibility: VisibilityTypes.ReadWrite,
});
// simple field of type string:
client.createSimpleField({
parentApiId: 'Author',
type: SimpleFieldType.String,
apiId: 'favouritePastime',
displayName: 'Author Favourite Pastime',
isRequired: true,
visibility: VisibilityTypes.ReadWrite,
});
// slug field:
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.String,
apiId: 'slug',
displayName: 'Slug',
description: 'Enter the slug for this page, such as about, blog, or contact',
isRequired: true,
isUnique: true,
tableRenderer: 'GCMS_SLUG',
formRenderer: 'GCMS_SLUG',
});
// hidden integer field with custom field validation:
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.Int,
apiId: 'count',
displayName: 'Count',
visibility: VisibilityTypes.Hidden,
validations: {
Int: {
range: {
max: 1000,
min: 0,
errorMessage: 'Counter has to be between 0 and 1000',
},
},
},
});
// required + unique string field with custom regex validation for emails:
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.String,
apiId: 'email',
displayName: 'Email',
isRequired: true,
isUnique: true,
validations: {
String: {
matches: {
regex: '^([a-z0-9_\\.\\+-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$',
},
},
},
});
// basic richtext field
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.Richtext,
apiId: 'content',
displayName: 'Content',
description:
'Enter the content for this page. The content uses the rich-text editor, giving you a better visual representation.',
isRequired: true,
});
// richtext with embeds and single allowed embeddable model:
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.Richtext,
apiId: 'contentExtended',
displayName: 'Content Extended',
embedsEnabled: true,
embeddableModels: ['Author'],
});
// list of date times
client.createSimpleField({
parentApiId: 'Page',
type: SimpleFieldType.Datetime,
apiId: 'lastSeen',
displayName: 'Last Seen',
isRequired: true,
isList: true,
});
// required uni-directional asset field
client.createRelationalField({
parentApiId: 'Page',
apiId: 'preview',
displayName: 'Preview',
type: RelationalFieldType.Asset,
isRequired: true,
reverseField: {
isUnidirectional: true,
apiId: 'page',
displayName: 'Page',
modelApiId: 'Page',
},
});
// regular bi-directional m-1 relation
client.createRelationalField({
parentApiId: 'Page',
apiId: 'writtenBy',
displayName: 'Written By',
type: RelationalFieldType.Relation,
reverseField: {
modelApiId: 'Author',
apiId: 'pages',
displayName: 'pages',
isList: true,
},
});
// m-n relation
client.createRelationalField({
parentApiId: 'Author',
apiId: 'favouriteBooks',
displayName: 'Favourite Books',
type: RelationalFieldType.Relation,
isList: true,
reverseField: {
modelApiId: 'Page',
apiId: 'favouriteOf',
displayName: 'Favourite of',
isList: true,
},
});
// create union field:
client.createUnionField({
parentApiId: 'Author',
apiId: 'favourites',
displayName: 'Author Favourite Books',
reverseField: {
apiId: 'authorFavouriteBook',
displayName: 'author favourite book',
modelApiIds: ['Book'],
},
});
// add a union member to the previous union field
client.updateUnionField({
parentApiId: 'Author',
apiId: 'favourites',
displayName: 'Favourite Pastime',
reverseField: {
modelApiIds: ['Book', 'Movie'],
},
});
// create a component:
client.createComponent({
apiId: 'Address',
apiIdPlural: 'Adresses',
displayName: 'Address',
});
client.createComponent({
apiId: 'Contributor',
apiIdPlural: 'Contributors',
displayName: 'Contributor',
});
client.createComponent({
apiId: 'VideoBlock',
apiIdPlural: 'VideoBlocks',
displayName: 'VideoBlock',
});
// add a field to the component:
client.createSimpleField({
parentApiId: 'Address',
type: SimpleFieldType.String,
apiId: 'city',
displayName: 'City',
});
// create basic component field:
client.createComponentField({
parentApiId: 'Author',
apiId: 'address',
displayName: 'Address',
description: 'Address of the author',
componentApiId: 'Address',
});
// create a component union field:
client.createComponentUnionField({
parentApiId: 'Page',
apiId: 'section',
displayName: 'Section',
componentApiIds: ['Contributor', 'VideoBlock'],
});
// remove VideoBlock from the previous component union field:
client.updateComponentUnionField({
parentApiId: 'Page',
apiId: 'section',
displayName: 'Section',
componentApiIds: ['Contributor'],
});

Create nested componentApiIdsAnchor

Essentially, the code syntax is the same as for creating a model, except that the parentApiId for a component field points to an ApiId of a component.

client.createModel({
apiId: 'Apple',
apiIdPlural: 'Apples',
displayName: 'Apple',
});
client.createComponent({
apiId: 'Level1',
apiIdPlural: 'Level1S',
displayName: 'Level1',
});
client.createComponent({
apiId: 'Level2',
apiIdPlural: 'Level2S',
displayName: 'Level2',
});
client.createComponentField({
apiId: 'sub1',
parentApiId: 'Apple', // Model
displayName: 'Entry1',
componentApiId: 'Level1',
});
client.createComponentField({
apiId: 'sub2',
parentApiId: 'Level1', // Component
displayName: 'Entry2',
componentApiId: 'Level2',
});