Skip to content

Instantly share code, notes, and snippets.

@ndom91
Last active July 25, 2025 17:42
Show Gist options
  • Save ndom91/7384ac80398af1f849166fd674012bc7 to your computer and use it in GitHub Desktop.
Save ndom91/7384ac80398af1f849166fd674012bc7 to your computer and use it in GitHub Desktop.
Plain Help Center markdown Migration
import path from "node:path";
import fs from "node:fs";
import { GraphQLClient, gql } from "graphql-request";
import { marked } from "marked";
type HelpCenterArticle = {
id: string;
title: string;
description: string;
contentHtml: string;
slug: string;
status: "PUBLISHED" | "DRAFT";
statusChangedAt: string;
statusChangedBy: string;
createdAt: string;
createdBy: string;
updatedAt: string;
updatedBy: string;
articleGroup: string;
};
type UpsertHelpCenterArticleOutput = {
upsertHelpCenterArticle: {
helpCenterArticle: HelpCenterArticle;
};
};
// Config - UPDATE FIELDS HERE
const API_TOKEN = "<your_api_token_here>"; // UPDATE WITH YOUR API KEY FROM PLAIN (Generated under "Machine Users" in your settings)
const HELP_CENTER_ID = "<your_helpCenterId_here>"; // UPDATE WITH YOUR HELP CENTER ID FROM PLAIN (i.e. `hc_abc123`)
const PLAIN_GRAPHQL_ENDPOINT = "https://core-api.uk.plain.com/graphql/v1";
const MARKDOWN_DIR = "./markdown-export"; // folder of markdown files relative to this file
// Setup GraphQL Client
const graphQLClient = new GraphQLClient(PLAIN_GRAPHQL_ENDPOINT, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
});
// GraphQL Mutation
const mutation = gql`
mutation UpsertHelpCenterArticle(
$helpCenterId: ID!
# $helpCenterArticleGroupId: ID
$title: String!
$description: String
$contentHtml: String!
$slug: String
$status: HelpCenterArticleStatus
) {
upsertHelpCenterArticle(
input: {
helpCenterId: $helpCenterId
# helpCenterArticleGroupId: $helpCenterArticleGroupId
title: $title
description: $description
contentHtml: $contentHtml
slug: $slug
status: $status
}
) {
helpCenterArticle {
id
title
slug
}
}
}
`;
// Helper: Convert file name to slug
function toSlug(filename: string) {
return filename
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9-_]/g, "")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
// Meat of the script
async function main() {
const files = fs.readdirSync(MARKDOWN_DIR).filter((f) => f.endsWith(".md"));
for (const file of files) {
const filePath = path.join(MARKDOWN_DIR, file);
const mdContent = fs.readFileSync(filePath, "utf-8");
// Use first non-empty line as title (or fallback to filename)
let title = mdContent.split("\n").find((line) => line.trim() !== "");
if (!title) title = path.parse(file).name;
let description = "";
// We'll take the first paragraph as a description (optional)
const paragraphs = mdContent.split(/\n\s*\n/);
if (paragraphs.length > 1) {
description = paragraphs[0].trim().slice(0, 160); // limit description length
}
const contentHtml = marked(mdContent);
const slug = toSlug(path.parse(file).name);
// You can set status as 'PUBLISHED' or 'DRAFT' - defaulting to PUBLISHED here
const status = "PUBLISHED";
const variables = {
helpCenterId: HELP_CENTER_ID,
title,
description,
contentHtml,
slug,
status,
};
try {
const data = await graphQLClient.request<UpsertHelpCenterArticleOutput>(
mutation,
variables,
);
console.log(
`Upserted article: "${title}" (slug: ${data.upsertHelpCenterArticle.helpCenterArticle.slug})`,
);
} catch (error) {
console.error(
`Failed to upsert article from file ${file}`,
error.response?.errors || error,
);
}
}
}
main().catch(console.error);
@ndom91
Copy link
Author

ndom91 commented Jul 22, 2025

This script should get you started with quickly importing your content from markdown files into the Plain Help Center knowledgebase.

To spin up a little node project to execute this, you'll want to execute the following commands.

mkdir migrate-to-plain-helpcenter # Create a directory for this project to live in
cd migrate-to-plain-helpcenter # Go into the directory
mkdir markdown-export # Create another sub-directory into which you can dump all your exported markdown files from GitBook, etc.
npm init -y # Initialize an npm project (with package.json, etc.)
touch index.ts # Create an empty file into which you'll copy the script above
npm install graphql-request marked # Install required dependencies
npm install -D @types/node tsx # Install required dev dependencies

Then in your package.json file, add this "dev" script:

{
  "scripts": {
    "dev": "tsx index.ts"
  }
}

Finally, you can run npm run dev in your migrate-to-plain-helpcenter directory to execute this script. Don't forget to copy the script above into your index.ts file and update the config values toward the top first.

@ndom91
Copy link
Author

ndom91 commented Jul 22, 2025

This script will generate a flat hierarchy of all your markdown files. If you want to create folders (called Article Groups in Help Center), check out the CreateHelpCenterArticleGroup mutation in our API Explorer or create a group in the UI and add a helpCenterArticleGroupId field to the mutation with that group's ID to tell the created articles to belong to that specific article group id.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment