In this example, we will explain how to Import CSV data into your project.

Here's a sample of the CSV table we will use in this project

id

title

text

c0f99460-68f0-47a7-b26d-88230987a885

Just an example

"<h1>HTML Ipsum Presents</h1><p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus.</p><pre><code>npm install react</code></pre>"

af7c1fe6-d669-414e-b066-e9733f0de7a8

Another example

"<h1>Another HTML Ipsum Presents</h1><p><strong>Lorem ipsum dolor sit</strong> amet, consectetur adipiscing elit.</p>"

This is the final code of this example: https://github.com/caisy-io/data-import-example

Setting up Caisy SDK

Install @caisy/sdk and get your personal access token ready

const sdk = initSdk({
  token: "<YOUR_PERONSAL_ACCESS_TOKEN>", 
  endpoint: "https://cloud.caisy.io/caisy/graphql"
});

Check the docs for Internal API and Personal Access Token


Acquiring Blueprint Details

The next step is to get blueprint details so we can extract the fields and create content

const getBlueprintDetails = async () => {
  // Blueprint can be fetched by its ID using GetBlueprintById
  const blueprintResponse = await sdk.GetBlueprintByName({
    input: { blueprintName: "Article", projectId: "<CAISY_PROJECT_ID>" },
  });
  
  return blueprintResponse?.GetBlueprintByName?.blueprint;
};

You can get Blueprint Name from API field in Blueprint settings menu

Blueprint name

You can get BLUEPRINT ID from any created content with the same blueprint

Getting blueprint ID

Extracting and Validating Blueprint Fields

const fieldNamesToImport = ["title", "text"];
const validateBlueprintAndFields = async (blueprint) => {
  if (!blueprint) {
    throw new Error(
      "Blueprint not found. Please ensure a blueprint named 'Article' exists with the fields 'title' and 'text'."
    );
  }

  // blueprint.groups?.[0]?.fields return blueprint fields as Array

  const fields = blueprint.groups?.[0]?.fields?.filter(
    (field) => field?.name && fieldNamesToImport.includes(field.name)
  );

  if (!fields || fields.length !== fieldNamesToImport.length) {
    throw new Error(
      "The blueprint lacks the requisite fields. Ensure the existence of a 'BlogPost' blueprint with 'title' and 'text' fields."
    );
  }

  return fields;
};

Parsing and Preparing the Data File

Once the blueprint and its fields are validated, the next phase involves parsing and preparing the CSV data file:

In the following snippet, we will use csvtojson package but if you intend to build a UI extension for non-technical users you can use react-csv-importer

const readAndParseData = async () => {
  const importData = await csvtojson().fromFile("<PATH_TO_YOUR_FILE>");
  return importData.map((item) => ({
    ...item,
    text: item.text && parseHTMLToJSON(item.text)
  }));
};

Preparing Caisy Document input

At this step, we transform the CSV data into Caisy Document fields format, in the snippet we add type casting so it helps with typescript auto-completion

const dataRowsToCaisyDocument = ({
  importDataParsed,
  blueprint,
  blueprintFields,
}) => {
  return {
    input: {
      projectId: projectId,
      // Iterate over CSV data to create Caisy document inputs
      documentInputs: importDataParsed.map((rowData) => {
        return {
          documentId: rowData.id,
          title: rowData.title,
          statusId: 2,
          blueprintId: blueprint?.blueprintId,
          // Map CSV columns to corresponding blueprint fields
          fields: Object.keys(rowData)
            .map((key) => {
              const value = rowData[key];
              const matchingField = blueprintFields.find(
                (field) => field?.name === key
              );

              return {
                blueprintFieldId: matchingField?.blueprintFieldId,
                data: value,
              } as DocumentFieldInput;
            })
            .filter((field) => !!field?.blueprintFieldId),
        } as DocumentWithFieldsInput;
      }),
    },
  };
};

Uploading Data to the Caisy Project

Putting all documents at once

The final step involves the actual uploading of data to the Caisy project:

(async () => {
  const importDataParsed = await readAndParseData();

  const blueprint = await getBlueprintDetails();
  const fields = await validateBlueprintAndFields(blueprint);

  // Construct input data for PutManyDocuments method
  const documentsInput = dataRowToCaisyDocument({importDataParsed, blueprint, blueprintFields: fields});
  

  // Execute the sdk.PutManyDocuments method
  const upsertResult = await sdk.PutManyDocuments(documentsInput);

  // Handle successful imports
  if (upsertResult?.PutManyDocuments?.successfulDocumentIds?.length) {
    console.info(
      `Successfully imported ${upsertResult.PutManyDocuments.successfulDocumentIds.length} documents.`
    );
  }

  // Handle errors
  if (upsertResult?.PutManyDocuments?.errors?.length) {
    console.error(
      "Errors occurred during the import process: ",
      upsertResult.PutManyDocuments.errors
    );
  }
})();

Putting Documents individually

In the above example, we have shown How to create all documents at once because it's the most performant method if you want to create documents one by one you could use sdk.CreateDocument then getting the documentId from CreateDoucment response:

const documentId = createDocument?.CreateDocument?.document?.documentId;

Then iterating over Blueprint fields to update the fields

for (let field of bluePrintFields) {
  await sdk.UpdateDocumentField({
    input: {
      projectId: "<CAISY_PROJECT_ID>",
      documentId: documentId,
      blueprintFieldId: field?.blueprintFieldId,
      data: "Hello World",
    },
  });
}

Conclusion:

By following the steps outlined above, you can now leverage the internal API of Caisy to upload data efficiently. ๐Ÿ˜‰