• Home
BuildWithMatija
Get In Touch
  1. Home
  2. Blog
  3. Payload
  4. Payload CMS SDK: CLI Toolkit for Faster Migrations

Payload CMS SDK: CLI Toolkit for Faster Migrations

Set up an authenticated Payload CMS SDK client and run CLI scripts with tsx to speed migrations, content cleanup, and…

30th January 2026·Updated on:28th January 2026·MŽMatija Žiberna·
Payload
Payload CMS SDK: CLI Toolkit for Faster Migrations

📚 Comprehensive Payload CMS Guides

Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.

No spam. Unsubscribe anytime.

During a recent migration from WordPress to Payload CMS, I ran into a familiar problem. Getting the data across was only half the work. The real time sink was all the small fixes afterward. Tweaking titles, correcting fields, cleaning up content. Doing all of that through the Admin UI quickly became slow and error prone.

Payload gives you several ways to input and modify data, but what helped me most during this phase was the SDK. Especially for targeted, one off changes, having a way to run authenticated scripts from the terminal was a huge productivity boost.

In this article, I will show how I set up a small SDK based toolkit for CLI scripts. It lets you run find, create, update, and delete operations directly against Payload without opening the Admin UI. This approach works particularly well during migrations, cleanups, and ongoing maintenance.

Step 1: Install dependencies and confirm the SDK is available

The toolkit relies on @payloadcms/sdk and runs scripts with tsx, so you need dependencies installed before anything else.

pnpm install

Once that completes, you’re ready to configure the SDK client used by all scripts in this folder.

Step 2: Configure the toolkit environment

The shared client loads credentials from a local .env.toolkit file so you can authenticate from the CLI.

# File: scripts/toolkit/.env.toolkit
PAYLOAD_URL=http://localhost:80/api
# OR set NEXT_PUBLIC_SERVER_URL and it will append /api
# NEXT_PUBLIC_SERVER_URL=http://localhost:80

PAYLOAD_EMAIL=you@example.com
PAYLOAD_PASSWORD=your-password

This file gives the SDK a base URL and login credentials. The client reads it automatically before it initializes, so every script can rely on the same configuration.

Step 3: Use the shared SDK client

All scripts should import the shared client from scripts/toolkit/client.ts. If you need authenticated access, call getAuthenticatedSdk() to get a JWT-backed SDK instance.

// File: scripts/toolkit/fix-titles.ts
import { getAuthenticatedSdk } from "./client";

async function run() {
  const sdk = await getAuthenticatedSdk();

  const pages = await sdk.find({ collection: "page", limit: 5 });
  if (pages.docs[0]) {
    await sdk.update({
      collection: "page",
      id: pages.docs[0].id,
      data: { title: "Updated from CLI" },
    });
  }
}

run().catch(console.error);

This example logs in, queries a few pages, and updates one document. The key idea is that authentication happens once in getAuthenticatedSdk(), and the returned SDK carries the JWT for all subsequent requests.

Example: find, update, create, delete

Use the SDK for quick CRUD tasks while staying within Payload permissions.

import { getAuthenticatedSdk } from "./client";

async function run() {
  const sdk = await getAuthenticatedSdk();

  // Find
  const pages = await sdk.find({ collection: "page", limit: 5 });
  console.log("Found:", pages.totalDocs);

  // Update
  if (pages.docs[0]) {
    await sdk.update({
      collection: "page",
      id: pages.docs[0].id,
      data: { title: "Updated from CLI" },
    });
  }

  // Create (remember tenant, see below)
  await sdk.create({
    collection: "page",
    data: {
      title: "CLI Page",
      // tenant: 1,
    },
  });

  // Delete
  // await sdk.delete({ collection: 'page', id: pages.docs[0].id })
}

run().catch(console.error);

Step 4: Run the script from the terminal

With the script in place, run it using tsx so TypeScript executes without a build step.

npx tsx scripts/toolkit/fix-titles.ts

At this point you’re running real SDK operations from the CLI, with the same permissions your user has in Payload—just without the Admin UI.

Step 5: Multi-tenant note for this project

This repo supports multiple tenants (adart, making-light). When you create documents in tenant-aware collections, include the tenant field in data. Updates by id are safe because IDs are unique. If you need a tenant-aware example, review scripts/toolkit/example-bulk-update.ts.

Tenant-aware create example

import { getAuthenticatedSdk } from "./client";

async function run() {
  const sdk = await getAuthenticatedSdk();

  await sdk.create({
    collection: "page",
    data: {
      title: "CLI Page (AdArt)",
      tenant: 1,
    },
  });
}

run().catch(console.error);

Conclusion

The problem was simple but painful: CLI scripts were blocked by Admin-only workflows. By wiring a shared Payload SDK client and a local .env.toolkit, you can run authenticated operations directly from the terminal. You now know how to configure the SDK, write a script, and execute it in this repo without the Admin UI. Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks, Matija

📄View markdown version
0

Frequently Asked Questions

Comments

Leave a Comment

Your email will not be published

Stay updated! Get our weekly digest with the latest learnings on NextJS, React, AI, and web development tips delivered straight to your inbox.

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.

Table of Contents

  • Step 1: Install dependencies and confirm the SDK is available
  • Step 2: Configure the toolkit environment
  • Step 3: Use the shared SDK client
  • Example: find, update, create, delete
  • Step 4: Run the script from the terminal
  • Step 5: Multi-tenant note for this project
  • Tenant-aware create example
  • Conclusion
On this page:
  • Step 1: Install dependencies and confirm the SDK is available
  • Step 2: Configure the toolkit environment
  • Step 3: Use the shared SDK client
  • Example: find, update, create, delete
  • Step 4: Run the script from the terminal
Build With Matija Logo

Build with Matija

Matija Žiberna

Full Stack Developer specializing in Next.js and TypeScript. Co-founder of We Hate Copy Pasting, building solutions for D2C brands.

Quick Links

About
  • Projects
  • Commands
  • Blog
  • Contact
  • Get in Touch

    Have a project in mind? Let's discuss how we can help your business grow.

    Contact me →
    © 2026BuildWithMatija•Crafting digital experiences with code•All rights reserved