Building a CSV import system for SitecoreAI
Learn how to build a self-contained Next.js application for importing CSV data into SitecoreAI using the Authoring GraphQL API.
Learn how to build a self-contained Next.js application for importing CSV data into SitecoreAI using the Authoring GraphQL API.
Start typing to search...
Many organizations manage hundreds of content items annually. Teams often receive data in CSV format from various sources, but getting this data into SitecoreAI was a manual, time-consuming process involving:
You end up with hours of manual work, prone to human error and just frustrations from content editors.
Sitecore has made efforts into creating work arounds where you can create your own script in order to deal with these issues, we decided to takke a step further. We prioritize user experience by building a dedicated web application that content editors use directly.
The import process follows this workflow:
The system uses Sitecore's Authoring GraphQL API to create and update items. The process is split into two steps for reliability.
mutation CreateItem(
$name: String!
$templateId: ID!
$parent: ID!
$language: String!
) {
createItem(
input: {
name: $name
templateId: $templateId
parent: $parent
language: $language
}
) {
item {
itemId
name
path
}
}
}Important Notes:
parent parameter requires a GUID (not a path) - query for parent ID firsttemplateId uses your custom template GUIDmutation {
updateItem(
input: {
itemId: "{GUID}"
database: "master"
language: "en"
version: 1
fields: [
{ name: "title", value: "Example Title", reset: false }
{ name: "startDateTime", value: "2026-07-01T11:00:00", reset: false }
]
}
) {
item {
itemId
name
}
}
}Important Notes:
database: "master" parameterreset: false for each field to preserve existing dataHere are some things we've taken note of which plays a big role in preventing any side effects and keeping the flow of the application seamless.
Running the same import twice shouldn't create duplicate items. This three-layer system prevents that:
// Check cache first
let existingId = itemIdCache.get(fullItemPath);
if (!existingId) {
// Not in cache, check Sitecore directly
try {
existingId = await getItemIdByPath(fullItemPath, accessToken);
if (existingId) {
itemIdCache.set(fullItemPath, existingId); // Cache it
}
} catch (error) {
// Item doesn't exist - we'll create it
}
}
// If exists, update instead of create
if (existingId) {
await updateItemFields(existingId, fieldsToUpdate, accessToken);
return { id: existingId, path: fullItemPath, name: itemName };
}This approach:
Items often need to be organized in folder hierarchies. This recursive function creates the entire path automatically:
async function getOrCreateParentId(itemPath: string, accessToken: string) {
// Try to get existing item
try {
return await getItemIdByPath(itemPath, accessToken);
} catch (error) {
// Doesn't exist - create it
const folderName = pathParts[pathParts.length - 1];
const parentPath = pathParts.slice(0, -1).join('/');
// Recursively ensure parent exists
const parentId = await getOrCreateParentId(parentPath, accessToken);
// Create this folder
return await createFolder(parentPath, folderName, accessToken);
}
}Example: Creating an item at /sitecore/content/YourSite/Events/2026/July/event-name will automatically create Events/, 2026/, and July/ folders if they don't exist.
Multiple items with the same title need unique item names in Sitecore. This function generates URL-safe names with identifiers:
function generateItemName(title: string, uniqueIdentifier?: string): string {
let itemName = title
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
if (uniqueIdentifier) {
// Add unique identifier (could be date, ID, etc.)
itemName += `-${uniqueIdentifier}`;
}
return itemName;
}Example: "Summer Festival" on July 1, 2026 becomes summer-festival-2026-07-01 This ensures:
To build an implementation like this, your environment will need to be configured to talk to the Sitecore Authoring GraphQL endpoint.
CLIENT_ID, CLIENT_SECRET, and the specific AUTHORING_ENDPOINT for your instance.xmcloud.item.create, xmcloud.item.update, and xmcloud.item.read scopes.When you begin developing your own implementation, we recommend following these core practices. We found these to be vital for maintaining a stable and user-friendly experience:
If you’re considering implementing this pattern in a future project, here are several scenarios where a CSV import system is particularly effective:
This standalone application effectively handles bulk CSV imports, but it does live outside the core Sitecore environment. As a next step, we are looking toward the Sitecore Marketplace. Integrating this methodology directly into the Marketplace setup would keep the tool secure and accessible right within the Sitecore UI, further reducing friction for content teams.