Skip to main content
Big news! Interval has been acquired by Meter. Learn more →

Account migration tool

It's common to write custom scripts for migrating accounts or importing data into your app, with custom code to parse and transform to your expected format. This example shows how to quickly extend a typical migration script to build a resuable, multi-step migration tool on Interval that anyone on your team can use.

interval.com

In this guide, we'll show you how to create a simple data migration tool that works seamlessly with your app's codebase. We'll cover these concepts:

  • Using Interval's search API to perform an account lookup
  • Using Interval's file upload API to collect external customer data
  • Running arbitrary code from your app's backend between steps to prepare the data to be imported
  • Using Interval's loading APIs to deliver a quality user experience while the data is being processed

How it works

info

Check out our blog post for a comprehensive walk through of building this account migration tool.

This example builds a tool that imports data for a given user. To begin, we'll provide a way to search for the user using an I/O method and a helper function that queries our database.

import { Action, io } from "@interval/sdk";
import { findUsers } from "../util";

export default new Action(async () => {
const user = await io.search("Select an account", {
onSearch: query => {
return findUsers(query);
},
renderResult: u => ({
label: `${u.firstName} ${u.lastName}`,
description: u.email,
}),
});
});
interval.com

Next we'll request a file upload containing the JSON data we'd like to import for the user.

import { Action, io } from "@interval/sdk";
import { findUsers } from "../util";

export default new Action(async () => {
const user = await io.search("Select an account", {
onSearch: query => {
return findUsers(query);
},
renderResult: u => ({
label: `${u.firstName} ${u.lastName}`,
description: u.email,
}),
});

const videosFile = await io.input.file("Select a file", {
allowedExtensions: [".json"],
});

const videos = await videosFile.json();

await io.display.table("Videos to import", {
data: videos,
helpText: "Press Continue to run the import.",
});
});
interval.com

Finally we'll confirm the import with a call to io.confirm and process and tranform the data for our app.

import { Action, io } from "@interval/sdk";
import { findUsers } from "../util";

export default new Action(async () => {
const user = await io.search("Select an account", {
onSearch: query => {
return findUsers(query);
},
renderResult: u => ({
label: `${u.firstName} ${u.lastName}`,
description: u.email,
}),
});

const videosFile = await io.input.file("Select a file", {
allowedExtensions: [".json"],
});

const videos = await videosFile.json();

await io.display.table("Videos to import", {
data: videos,
helpText: "Press Continue to run the import.",
});

// Add a confirmation step
const confirmed = await io.confirm(`Import ${videos.length} videos?`);
if (!confirmed) return "Action canceled, no videos imported";

const importedVideos: Video[] = [];

for (let i = 0; i < videos.length; i++) {
// use our app's internal methods to create the missing inputs
const thumbnailUrl = await generateThumbnail(videos[i].url);
const slug = await getCollisionSafeSlug(videos[i].name);

const createdAt = new Date(videos[i].createdAt * 1000);

const video = await prisma.video.create({
data: {
title: videos[i].name,
url: videos[i].url,
thumbnailUrl,
slug,
createdAt,
user: { connect: { id: user.id } },
},
});

importedVideos.push(video);
}

return `Imported ${importedVideos.length} videos for ${user.email}`;
});

API methods used

Did this section clearly explain what you wanted to learn?

548 Market St PMB 31323
San Francisco, CA 94104

© 2024

Join our mailing list

Get 1-2 emails per month with a roundup of product updates, SDK releases, and more.