Skip to main content

Config Versions and Init

Instances of your integration keep their saved configuration when you publish a new version. A headless configuration's version and init function let you change the configuration's shape safely, and let you suggest values whenever a customer opens your app's form.

The configuration version​

Every headless configuration has a version, a name you choose for the current shape of its schema:

configuration({
instance: {
schema: configurationSchema,
version: "fake-crm-v1",
},
});

Each instance records the version its values were saved under. The configuration version is separate from the integration's version number: you can publish many integration versions with the same configuration version.

Keep the version when saved values still satisfy the new schema. For example, adding an optional field, or changing a flow without changing the configuration.

Change the version when saved values no longer satisfy the schema. For example, renaming a field, making an optional field required, or changing a field's type.

How updates reach instances​

When you publish a new integration version and make it available in your marketplace, Prismatic offers an update to each instance on an older version. Your app reads it from the instance's update property, which says whether the update requiresReconfiguration:

Configuration versionrequiresReconfigurationWhat your app does
UnchangedfalseCalls the instance's upgrade action, which moves it to the new version keeping its values, then deploy.
ChangedtrueOpens the new version's configuration, runs its init, shows the result for review, then calls save and deploy.

Saving a newer version's configuration is what moves the instance to that version. Until your app deploys the instance, the old version keeps running.

Write an init function​

init suggests the values your app shows in its form. Your app calls it when a customer sets up a new instance, edits a saved configuration, or reviews an update. It saves nothing.

init receives the instance's saved configuration and the version it was saved under, in context.configuration and context.configurationVersion. Spectral narrows the type of context.configuration by the version you check:

src/configuration.ts
const DEFAULTS: Configuration = {
recordType: "Lead",
fieldMapping: DEFAULT_MAPPINGS.Lead,
};

export const integrationConfiguration = configuration({
instance: { schema: configurationSchema, version: CONFIGURATION_VERSION },
init: {
perform: async (context) => {
if (context.configurationVersion === CONFIGURATION_VERSION) {
// A new instance arrives here too, with an empty configuration,
// so check whether anything valid has been saved before using it.
const saved = configurationSchema.safeParse(context.configuration);
return {
configuration: saved.success ? saved.data : DEFAULTS,
unresolvedReasons: [],
};
}
return {
configuration: DEFAULTS,
unresolvedReasons: [
`Saved configuration version "${String(context.configurationVersion)}" is unknown.`,
],
};
},
},
});
A new instance's configuration is empty, not null

When a customer sets up a new instance, init receives your current configurationVersion and an empty {} configuration. Validate the saved configuration, for example with your schema's safeParse, before you return it as the suggested values.

init returns an object with:

  • configuration: the suggested instance values, which Spectral types with your current schema.
  • userConfiguration: the suggested user-level values, required when your configuration has a userLevel scope.
  • Any other properties you want your app to have. The examples in these docs add unresolvedReasons, messages for the customer to review before saving.

Prismatic passes the result to your app unchanged. It doesn't validate the suggested values against your schema at runtime; that happens when your app saves them.

context.configurationVersion can be:

ValueMeaning
Your current versionThe instance is new (with an empty configuration) or was saved under the current schema.
A version in versionSchemasThe instance was saved under an older schema. context.configuration is typed with that schema.
nullThe instance was configured with config pages. See Migrating from config pages.
Any other stringA version this code doesn't know. Return safe defaults and explain why.

In TypeScript, Spectral types an unknown version as a unique symbol rather than a string, so handle it in your final fallthrough branch instead of comparing it with a string literal.

Migrate values from an older version​

To change the schema, keep the old schema under versionSchemas, give the new schema a new version, and convert old values in init.

Suppose version slack-alerts-v1 saved only a channel ID, and slack-alerts-v2 saves the channel's ID and name:

src/configuration.ts
export const integrationConfiguration = configuration({
instance: {
schema: z.object({
channel: z.object({ id: z.string(), name: z.string() }),
}),
version: "slack-alerts-v2",
versionSchemas: {
"slack-alerts-v1": z.object({ channelId: z.string() }),
},
},
init: {
perform: async (context) => {
if (context.configurationVersion === "slack-alerts-v1") {
// context.configuration is typed as { channelId: string }
return {
configuration: {
channel: { id: context.configuration.channelId, name: "" },
},
unresolvedReasons: [
"Choose your channel again so its name can be shown.",
],
};
}
// ...
},
},
});

Because the version changed, instances saved under slack-alerts-v1 see an update that requires reconfiguration. Your app runs the new version's init, the customer reviews the migrated values, and saving moves the instance to the new version.

Test init​

Like a server function, init.perform is a plain async function. Export the logic it uses and test that, or define the perform function as its own export and call it with a context that holds the properties it reads.