Skip to main content

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.

Get started with this example
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

Try it out

To create a fresh project based off this example, run:

npx create-interval-app --template=account-migration
The full source code for this tool can also be found in our examples repo.

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 "dotenv/config";
import Interval, { io } from "@interval/sdk";
import { findUsers } from "../util";

const interval = new Interval({
apiKey: process.env.INTERVAL_KEY,
actions: {
import_videos: 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.listen();
interval.com

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

import "dotenv/config";
import Interval, { io } from "@interval/sdk";
import { findUsers } from "../util";

const interval = new Interval({
apiKey: process.env.INTERVAL_KEY,
actions: {
import_videos: 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.listen();
interval.com

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

import "dotenv/config";
import Interval, { io } from "@interval/sdk";
import { findUsers } from "../util";
import {
findUsers,
generateThumbnail,
getCollisionSafeSlug,
prisma,
} from "../util";
import { Video } from "@prisma/client";

const interval = new Interval({
apiKey: process.env.INTERVAL_KEY,
actions: {
import_videos: 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}`;
},
},
});

interval.listen();

API methods used

Did this section clearly explain what you wanted to learn?

548 Market St PMB 31323
San Francisco, CA 94104

© 2022

Join our mailing list

Every Friday we send an email with the latest from Interval, including events, product updates, SDK releases, and more.