Data import techniques

Importing a random set of customers using Mockaroo and the import SDK with Deno

Willem Haring
Willem Haring
Sales Engineer, commercetools
Published 06 December 2023
Estimated reading time minutes

When evaluating a headless commerce system, one of the first things you should do is get your own data into it. Importing data can sometimes be a “chicken or the egg” situation, so you need to have some things configured, such as product attributes, for an import to really work well. In this article, I am going to show how you can use the commercetools import API from Deno to import a list of randomly generated customers from Mockaroo, how you can upload them in batches and how to monitor progress.

Data import techniques

The commercetools Import API

The Import API uploads data to commercetools Composable Commerce. Its asynchronous design is suitable to sync a large amount of resources from external systems into projects.

commercetools import API

The Import API has the following advantages:

  • Asynchronous
    You can send bulk data at once, and your systems can do something else while your data is imported asynchronously.

  • Resource specific
    You can separately import products, product variants and prices.

  • Automatic dependency handling
    The Import API automatically handles data dependencies such as the parental relations of categories.

  • API-first
    Unlike other CLI tools and UI tools, it gives you a better integration opportunity with your modern application infrastructure.

When you want to import random customers into commercetools, you can follow the schema of CustomerImport.

Since this is a pretty simple schema, it almost asks to be generated by something in a pseudo random fashion. Here comes Mockaroo to the rescue.

Generating random customers with Mockaroo

Mockaroo is an excellent SaaS tool to generate demo data. I have been using it for a long time and it is one of those tools you would like to have in your tool belt. 

With Mockaroo, you can define a schema that follows the schema of ImportCustomer:

Define a schema with Mockaroo

On top of that schema, you can create an API that takes some parameters, like the number of customers you would like to generate. And so a list of random customers is created:

Mockaroo API script

This simple Mockaroo API script generates 30 customers by default, but when adding a count parameter, it will use the count, like in the example below:

https://my.api.mockaroo.com/customers.json?key=00aa64c0&count=5

The import API in Deno

Now that we have more demo data than we could ever consume, let's start loading those customers into commercetools. In the previous article, I wrote about the Deno SDK for commercetools. This SDK also exposes an Import SDK that can be used in the following way:

import {importsdk} from
"https://deno.land/x/commercetools_demo_sdk/importsdk.ts";

const handle = importsdk.init()

const result = await handle
   .root()
   .importContainers()
   .get()
   .execute()

console.log(result.body)

This small snippet uses the TypeScript Import SDK to list the available import containers:

{
   limit: 20,
   offset: 0,
   count: 0,
   total: 0,
   results: []
}

You can create an Import container using the following snippet:

import {importsdk} from
"https://deno.land/x/commercetools_demo_sdk/importsdk.ts";

const handle = importsdk.init()
const result = await handle
   .root()
   .importContainers()
   .post({
      body: {
         key: "my-import-container"
      }
   })
   .execute()
console.log(result.body)

This will output the following result:

{
   key: "my-import-container",
   version: 1,
   createdAt: "2023-11-16T08:49:39.470Z",
   lastModifiedAt: "2023-11-16T08:49:39.470Z"
}

The Import container can handle imports of all sorts. You can upload a list of items to the container, where the container will provide the compute to do the actual import of the resources. A container can be queried for its status to check the progress of the import job.

First, lets kick-off an import job:

import {importsdk} from "https://deno.land/x/commercetools_demo_sdk/importsdk.ts";

const handle = importsdk.init()
   const result = await handle
     .root()
     .importContainers()
     .post({
       body: {
        key: "my-import-container"
       }
     })
     .execute() // create the container first
   console.log(result.body)
   // now that we have a container, import customer data into it
   const randomendpoint =
`https://my.api.mockaroo.com/customers.json?
key=00aa64c0&count=5`
   // mockeroo
   const customersresponse = await fetch(randomendpoint)
   const customerdata = await customersresponse.json()
   console.log(`fetched ${customerdata.length} customers from Mockaroo`)
   // we now have an array of 5 customer import objects

   const importresult = await handle
     .root()
     .customers()
     .importContainers()
     .withImportContainerKeyValue({importContainerKey: "my-import-container"})
     .post({body: {type: "customer", resources: customerdata}})
     .execute()
   console.log(importresult.body.operationStatus)

This small import script does the following:

  1. Create an import container with key: “my-import-container”

  2. Imports 5 random customers from Mockaroo

  3. It sends the 5 customers to the import container

The response is:

[
   { operationId: "7f1b2beb-4022-4347-b23c-f9c6c374bf5b", state: "processing"},
   { operationId: "9d2c24da-a28e-4a64-aee9-8a568184e4ad", state: "processing"},
   { operationId: "49f8ace0-9d72-4082-ab84-1f0fbc9b4340", state: "processing"},
   { operationId: "9bd39745-de47-4768-a07f-392f2695c39c", state: "processing"},
   { operationId: "46775478-e6a2-4725-9dd0-6264b0c5b2ef", state: "processing"}
]

This indicates that each record in the job is processing. If you check the container again a couple of seconds later with an operation ID, the following response would be shown:

{
   body: {
     limit: 20,
     offset: 0,
     count: 5,
    total: 5,
     results: [
       {
         version: 2,
         importContainerKey: "my-import-container",
         resourceKey: "pt-d-riddock",
         id: "7f1b2beb-4022-4347-b23c-f9c6c374bf5b",
         state: "imported",
         resourceVersion: 1,
         createdAt: "2023-11-16T08:53:08.039Z",
         lastModifiedAt: "2023-11-16T08:54:04.310Z",
         expiresAt: "2023-11-18T08:53:08.039Z"
       },...
     ]
   },
   statusCode: 200
}

This indicates that this single resource is now imported into commercetools. This also allows us to query it using the SDK:

import { sdk} from "https://deno.land/x/commercetools_demo_sdk/clientsdk.ts";

const handle = sdk.init()
const result = await handle
   .root()
   .customers()
   .withKey({key: "fr-clmentine-yarwood"})
   .get()
   .execute()
console.log(result.body)

This shows the generated customer imported in commercetools:

{
   id: "f4664728-a3a2-4fa4-ac4d-779bb8f89bc0",
   version: 1,
   versionModifiedAt: "2023-11-16T08:54:04.302Z",
   lastMessageSequenceNumber: 1,
   createdAt: "2023-11-16T08:54:04.302Z",
   lastModifiedAt: "2023-11-16T08:54:04.302Z",
   lastModifiedBy: { isPlatformClient: true },
   createdBy: { isPlatformClient: true },
   email: "clmentine.yarwood@reallinks.fr",
   firstName: "Clémentine",
   lastName: "Yarwood",
   title: "Mr",
   dateOfBirth: "2001-10-03",
   companyName: "Reallinks",
   vatId: "9398736632",
   password: "****480=",
   addresses: [
     {
       id: "vSUelAA6",
       title: "Mr",
       salutation: "Dear Mr Yarwood, ",
       firstName: "Clémentine",
       lastName: "Yarwood",
       streetName: "Gulseth",
       streetNumber: "35689",
       postalCode: "94726 CEDEX",
       city: "Fontenay-sous-Bois",
       country: "FR",
       company: "Reallinks",
       department: "Marketing",
       phone: "478-279-2855",
       mobile: "707-405-2045",
       email: "clmentine.yarwood@reallinks.fr",
       fax: "124-321-8560",
       externalId: "940d28ed-d7b1-4950-a004-6c3fc00fbaba",
       key: "fr-clmentine-yarwood-fontenay-sous-bois"
     }
   ],
   shippingAddressIds: [],
   billingAddressIds: [],
   isEmailVerified: true,
   externalId: "0d7dd1d3-8fcd-4ecb-a1ae-39622c9f4b7e",
   key: "fr-clmentine-yarwood",
   stores: [],
   authenticationMode: "Password"
}

So far so good for the basic techniques. Now let's create a bulk importer that imports large lists of customers and then wait for the import to complete.

Importing a large dataset

To help with importing a large dataset of any resource type, I created a small batch importer. The importer takes an array of objects to import, creates a container when needed and sends the data in chunks of 20 objects to the Import SDK. After that, it waits for the import SDK to complete by polling each operation. To make the batch importer resource type agnostic, you need to provide a callback that does the actual import of the requested resource.

The batch importer can be used in the following way:

import { importbatch } from
"https://deno.land/x/commercetools_demo_cli/helpers/import/batchimport.ts"
import {CustomerImport, ImportContainer, ImportOperationStatus, importsdk} from
"https://deno.land/x/commercetools_demo_sdk/importsdk.ts";

async function importCustomers(handle: importsdk, container: ImportContainer, customers:
CustomerImport[]): Promise {
   const result = await handle
      .root()
      .customers()
      .importContainers()
      .withImportContainerKeyValue({importContainerKey: container.key})
      .post({
         body: {
            type: "customer",
            resources: customers
         }
      })
      .execute()
   return result.body.operationStatus
}

const randomendpoint = `https://my.api.mockaroo.com/customers.json key=00aa64c0&count=22`
  // mockeroo
const customersresponse = await fetch(randomendpoint)
const customerdata = await customersresponse.json()

await importbatch("my-import-container", customerdata, importCustomers)

This will import 22 random customers from Mockaroo into commercetools. It will split the import in a batch of 20 and a batch of 2. After the import is fired to commercetools, it will wait for the importer to complete all items:

deno run -A import/batchcustomerimport.ts
Importing a batch of 20 items
Importing a batch of 2 items
✅ Item: pt-mahlie-mawdsley is imported
✅ Item: pt-gsta-macilriach is imported
✅ Item: pt-rbecca-daintith is imported
✅ Item: gb-mng-jeffreys is imported
✅ Item: pt-mlanie-thiem is imported
✅ Item: de-pnlope-reinert is imported
✅ Item: pt-la-pim is imported
✅ Item: es-mlina-cardus is imported
✅ Item: pt-lucrce-braizier is imported
✅ Item: fr-sverine-orrom is imported
✅ Item: pt-ins-astbury is imported
✅ Item: fr-laurlie-wooster is imported
✅ Item: fr-dafne-birkhead is imported
✅ Item: pt-mlanie-kubal is imported
✅ Item: fr-d-fullbrook is imported
✅ Item: pt-loca-olenin is imported
✅ Item: fr-sverine-tretter is imported
✅ Item: pt-los-robison is imported
✅ Item: pt-rjane-pipes is imported
✅ Item: es-erwi-vanhaeften is imported
✅ Item: it-grel-neesam is imported
✅ Item: fr-cungonde-halloran is imported

The next article in this series will showcase how to prepare for the next import: Products. But before we can import products, we need to get the commercetools configuration setup correctly. 

Roll up your sleeves for some infrastructure as code!

To learn more about the creation of the Deno SDK, read the first article of the series: A Deno SDK for commercetools.

Willem Haring
Willem Haring
Sales Engineer, commercetools

Willem has been working in retail technology since the beginning of 2000. He has worked with several technology companies and with clients on point of sales, stores infrastructure, reporting and dashboarding. He has been a participating member on the ARTS committee for the data warehouse chapter under ARTS XML. At commercetools, Willem works with clients, prospects and customers to translate complicated use cases to workable eCommerce solutions based on commercetools.

Related Blog Posts