Tags
Let's start by looking at a blueprint in the CMS UI of a simple blog article.
If you get this whole blueprint with all its groups and fields from the API, it looks like this:
{
"blueprintId": "d5f7b0fe-3c47-407a-ae35-d6b469059773",
"groups": [
{
"blueprintGroupId": "65927fb5-cbe4-44c5-9140-677769bc01d2",
"fields": [
...
{
"blueprintFieldId": "2de9fa60-a9f1-4464-ad78-eace20aa63e5",
"blueprintGroupId": "65927fb5-cbe4-44c5-9140-677769bc01d2",
"blueprintId": "d5f7b0fe-3c47-407a-ae35-d6b469059773",
"description": "Used to generate the url path",
"name": "slug",
"options": {
"required": true,
"string": {
"max": 80,
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
},
"uniqueLocal": true
},
"title": "slug",
"type": "BLUEPRINT_FIELD_TYPE_STRING"
},
...
],
"name": "Main"
}
],
"name": "BlogArticle",
"projectId": "85e79eaf-c776-47fa-85f4-564d5bd6410b",
"tagIds": [],
"title": "Blog Article",
"variant": "BLUEPRINT_VARIANT_DOCUMENT"
}
The full example json can be found here.
To get this data, you can either collect all your project blueprints with this GraphQL query from the internal API, as seen here:
import { initSdk } from "@caisy/sdk";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
});
(async () => {
const byNameResponse = await sdk.GetBlueprintByName({
input: { blueprintName: "BlogArticle", projectId },
});
const blogArticleBlueprint = byNameResponse.GetBlueprintByName?.blueprint;
console.log(JSON.stringify(blogArticleBlueprint, null, 2));
})();
{
GetBlueprintByName(
input: {blueprintName: "BlogArticle", projectId: "85e79eaf-c776-47fa-85f4-564d5bd6410b"}
) {
blueprint {
name
title
description
projectId
blueprintId
single
system
tagIds
variant
groups {
blueprintGroupId
name
fields {
name
blueprintFieldId
blueprintGroupId
blueprintId
description
system
title
type
}
}
}
}
}
When creating blueprints, all IDs that are not passed are automatically generated. If you are looking into running multiple projects and syncing them, it might be good to note that you can create blueprints with the same IDs in different projects. The same applies for field and group IDs. If you duplicate a project in the UI, the blueprint IDs are also kept identical.
import { initSdk, BlueprintFieldType, BlueprintVariant } from "@caisy/sdk";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
});
(async () => {
await sdk.CreateBlueprint({
input: {
input: {
groups: [
{
fields: [
{
name: "title",
title: "Title",
type: BlueprintFieldType.BlueprintFieldTypeString,
},
],
name: "Main",
},
],
name: "BlogSection",
title: "Blog Section",
variant: BlueprintVariant.BlueprintVariantDocument,
},
projectId,
},
});
})();
mutation CreateBlueprint($input: CreateBlueprintRequest) {
CreateBlueprint(input: $input) {
blueprint {
name
title
description
projectId
blueprintId
single
system
tagIds
variant
groups {
blueprintGroupId
name
fields {
name
blueprintFieldId
blueprintGroupId
blueprintId
description
system
title
type
}
}
}
}
}
The update operation allows you to modify various aspects of a blueprint, such as its name, description, and the fields within its groups.
One of the advantages of this update process is the ability to move fields between different groups without any problem. As long as the unique identifier (blueprintFieldId
) of the field remains the same, you can change the group it belongs to without causing any data loss or having an impact on the external API.
This flexibility allows you to restructure your blueprints as needed, reorganizing fields into different groups or creating new groups altogether. The system ensures that the field data remains intact and correctly associated with its corresponding entity, regardless of which group it resides in.
By maintaining the same blueprintFieldId
for each field, the update operation can seamlessly relocate fields within the blueprint structure, ensuring a smooth transition without disrupting any existing data or breaking integrations with external systems.
This approach promotes better organization and maintainability of your blueprints, enabling you to adapt to changing requirements or optimize the structure of your data models without the need for complex data migrations or API changes.
Here's an example of how to retrieve a blueprint and update its fields:
import { initSdk } from "@caisy/sdk";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
});
(async () => {
const blueprintByNameResult = await sdk.GetBlueprintByName({
input: {
blueprintName: "BlogSection",
projectId,
},
});
const blueprint = blueprintByNameResult.GetBlueprintByName?.blueprint;
const updatedBlueprint = {
groups: blueprint?.groups?.map((group) => {
if (group?.fields) {
group?.fields?.map((field) => {
// we want to rename the title field to headline
// be cause we do not touch id of the blueprint field all content will be preserved
if (field?.name === "title") {
field.name = "headline";
field.title = "Headline";
}
return field;
});
}
return group;
}),
name: blueprint?.name,
title: blueprint?.title,
description: blueprint?.description,
variant: blueprint?.variant,
exposeMutations: blueprint?.exposeMutations,
previewImageUrl: blueprint?.previewImageUrl,
single: blueprint?.single,
tagIds: blueprint?.tagIds,
};
await sdk.UpdateBlueprint({
input: {
input: updatedBlueprint,
blueprintId: blueprint?.blueprintId,
projectId,
},
});
})();
mutation UpdateBlueprint($input: UpdateBlueprintRequest!) {
UpdateBlueprint(input: $input) {
blueprint {
name
title
description
projectId
blueprintId
single
system
tagIds
variant
groups {
blueprintGroupId
name
fields {
name
blueprintFieldId
blueprintGroupId
blueprintId
description
system
title
type
}
}
}
}
}
Sometimes you may need to remove a blueprint from your project entirely. This operation will permanently delete the blueprint and all its associated data, including groups and fields. Here's an example of how to delete a blueprint:
We can use the DeleteBlueprint
mutation to remove a blueprint from our project. This mutation takes the blueprintId
as an input parameter, which uniquely identifies the blueprint you want to delete. After executing this mutation successfully, the blueprint and its associated data will be permanently removed from the system.
import { initSdk } from "@caisy/sdk";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
});
(async () => {
const blueprintByNameResult = await sdk.GetBlueprintByName({
input: {
blueprintName: "BlogSection",
projectId,
},
});
const blueprint = blueprintByNameResult.GetBlueprintByName?.blueprint;
await sdk.DeleteBlueprint({
input: {
blueprintId: blueprint?.blueprintId,
projectId,
},
});
})();
mutation DeleteBlueprint($input: DeleteBlueprintRequest!) {
DeleteBlueprint(input: $input) {
deleted
}
}
Retrieving all the blueprints associated with a project can be a useful operation for various reasons. For example, you might want to display a list of available blueprints to users, allowing them to select and work with specific blueprints within your application. Additionally, you may need to perform batch operations or analyses across all blueprints in a project.
Our system supports fetching all the blueprints within a project through a single GraphQL query. This query can be particularly handy when you need to have a comprehensive view of the data models and structures defined across your project.
Here's an example of how to fetch all blueprints from a project:
import { initSdk } from "@caisy/sdk";
import fs from "fs";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
});
(async () => {
const getManyBlueprintsResponse = await sdk.GetManyBlueprints({
input: { projectId },
});
const allBlueprints = getManyBlueprintsResponse.GetManyBlueprints?.connection?.edges?.map(edge => edge?.node) || [];
fs.writeFileSync("allBlueprints.json", JSON.stringify(allBlueprints, null, 2));
})();
{
GetManyBlueprints(input: {projectId: "85e79eaf-c776-47fa-85f4-564d5bd6410b"}) {
connection {
pageInfo {
hasNextPage
endCursor
}
totalCount
edges {
cursor
node {
name
title
description
projectId
blueprintId
single
system
tagIds
variant
createdAt
createdBy
variant
groups {
blueprintGroupId
name
fields {
name
blueprintFieldId
blueprintGroupId
blueprintId
description
system
title
type
}
}
}
}
}
}
}
The response structure, connection->edges->node
follows the Relay specification for pagination and connection handling. The connection
field contains the paginated data, with each edge
representing a single blueprint. The node
field within each edge contains the actual blueprint data.
It's worth noting that for projects with a large number of blueprints (more than 1000), our system supports pagination to ensure efficient data retrieval and prevent potential performance issues. Pagination allows you to fetch blueprints in smaller batches, making it easier to manage and process the data on the client side.
However, for most users and projects, fetching all the blueprints on the first page should be sufficient, as it's uncommon to have thousands of blueprints within a single project. By default, the first page will return the first 1000 blueprints, providing you with a fast overview of the available data models in your project.
In this example, we'll leverage the output from the GetManyBlueprints
query to upsert (insert or update) multiple blueprints in our project simultaneously. This approach allows us to efficiently synchronize our project's blueprints with a backup or external source.
import { initSdk } from "@caisy/sdk";
import fs from "fs";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
endpoint: "https://cloud.staging.caisy.io",
});
(async () => {
const backupFile = fs.readFileSync("allBlueprints.json", "utf-8");
const backupBlueprints = JSON.parse(backupFile);
const getManyBlueprintsResponse = await sdk.PutManyBlueprints({
input: { projectId, blueprintInputs: backupBlueprints },
});
console.log(JSON.stringify(getManyBlueprintsResponse, null, 2));
})();
mutation PutManyBlueprints($input: PutManyBlueprintsRequestInput!) {
PutManyBlueprints(input: $input) {
successfulBlueprintIds
errors {
errorMessage
blueprintId
}
}
}
This approach streamlines the process of synchronizing our project's blueprints with a backup or external source. By directly using the output from GetManyBlueprints
as input for PutManyBlueprints
, we can efficiently upsert multiple blueprints without the need for manual processing or transformation.
The PutManyBlueprints
mutation will create new blueprints for those that don't exist in the project and update existing ones if they have changed, ensuring that our project's blueprints match the desired state defined by the backupBlueprints
array.
There are more considerations than just blueprints when duplicating project data. You may also want to copy tags, and potentially other related entities. However, instead of downloading all the data and uploading it again, which can be inefficient, you can leverage a dedicated port operation to copy this data even faster. Here's an example that duplicates tags and blueprints from one project to another, similar to the GetManyBlueprints
and PutManyBlueprints
operations:
import { initSdk } from "@caisy/sdk";
const token = process.env.CAISY_PERSONAL_ACCESS_TOKEN!;
const projectId = "85e79eaf-c776-47fa-85f4-564d5bd6410b";
const sdk = initSdk({
token,
endpoint: "https://cloud.staging.caisy.io",
});
(async () => {
const duplicateToProjectResponse = await sdk.DuplicateToProject({
input: {
projectId,
source: {
projectId: "11e79eaf-c776-47fa-85f4-564d5bd6410a",
},
selection: {
blueprint: true,
tag: true,
},
},
});
// this is just if you want to check the progress of the duplication
// this just takes a couple of 100ms to finish, but still it runs async
const portId = duplicateToProjectResponse.DuplicateToProject?.portId;
const portStatusResponse = await sdk.GetProjectPort({
input: {
portId,
},
});
console.log(JSON.stringify(portStatusResponse.GetProjectPort, null, 2));
})();
mutation DuplicateToProject($input: DuplicateToProjectRequestInput!) {
DuplicateToProject(input: $input) {
portId
}
}
This article has covered various operations for managing blueprints within your projects using the Caisy Internal API. From creating, updating, and deleting individual blueprints to fetching all blueprints or duplicating entire project structures, the API provides a comprehensive set of tools to streamline your workflow.
As your project grows and evolves, the ability to efficiently manipulate blueprints is crucial. The examples and explanations provided should equip you with the knowledge and skills necessary to seamlessly work with blueprints and ensure your project's data structure remains consistent and up-to-date.
Remember, the power lies in the flexibility and scalability of the Caisy Internal API.
Happy coding, and may your projects thrive with the help of the Caisy Internal API's powerful blueprint management capabilities!
Subscribe to our newsletters
and stay updated
While you subscribe you agree to our Privacy Policy and Terms of Service apply.
Tags