BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 14, 2026ReactBlog

Import Blog Content with BTST Trusted Operations

Preserve validation, tags, timestamps, and lifecycle behavior during editorial imports, then reconcile retries and verify public cache refreshes.

Import Blog Content with BTST Trusted Operations

An editorial import should retain the Blog plugin's validation, tags, dates, and lifecycle behavior. Writing a row directly can bypass those behaviors; making a browser request with an administrator's cookie adds an unnecessary dependency on an interactive session.

BTST v3 provides a trusted operation surface for application-owned jobs. This guide explains when to use it and how to design a repeatable import around the released @btst/stack@3.0.2 Blog contract.

Choose the right operation surface#

CallerSurface
A visitor or editor making a server requeststack.forRequest(request).operations.blog
An authorized administrative import or scheduled jobstack.trusted.blog
Plugin internals or a migration that explicitly owns lower-level behaviorStandalone adapter primitives

The trusted surface skips identity resolution and user authorization. It still runs input validation, derives operation facts, executes domain behavior, and invokes lifecycle hooks. Keep access to that surface inside the trusted server process. A public endpoint must not gain operator authority just because it calls an import helper.

Standalone getters and mutations have a different contract: their caller owns access control and lifecycle composition. Use them only when that lower-level behavior is intentional.

Prepare one record before writing#

The Blog package exports the creation operation's input schema. Use it to validate prepared source data before a batch starts:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
import { CreatePostOperationInputSchema } from "@btst/stack/plugins/blog/api";

export function prepareImportedPost(content: string) {
  return CreatePostOperationInputSchema.parse({
    slug: "moving-an-editorial-guide",
    title: "Moving an Editorial Guide",
    excerpt: "A practical guide moved from the previous publication.",
    content,
    published: false,
    tags: [{ name: "Engineering" }],
  });
}

This creates input for a draft, not a public article. Pass the validated result to stack.trusted.blog.createPost(...) in your already configured server job. Keep database credentials, storage tokens, and the configured stack module out of browser bundles.

When preserving an existing publication, the operation also accepts publication and creation timestamps. Use source dates you actually know. Record any missing dates rather than making up a publication history.

Normalize a stable slug before the import and keep it in the source manifest. In this release, tag names are resolved by slug, so differently formatted names can reuse the same stored tag. Read back the stored display name and use it consistently on later comparisons.

Make a retry distinguishable from a second publication#

A successful database write can be followed by a lost connection or a failed receipt write. A retry should first look up the stable slug through the trusted list operation.

Compare the stored article with the intended title, body, excerpt, publication state, image, and normalized tags. If they match, record that the item already exists and proceed. If they differ, stop for reconciliation rather than silently overwriting editorial changes or generating a second slug.

This check alone does not solve concurrent imports. Use a unique slug constraint and serialize cooperating import jobs, or use the appropriate database transaction and lock for your application. If two jobs race, investigate the resulting conflict before retrying. Keep an execution receipt containing the stable slug, content hash, stored ID, timestamps, and outcome.

For a batch that must be all-or-nothing, explicitly bind the operation adapter to a transaction. Do not assume that several independent calls form one atomic batch. Lifecycle hooks that send email or write to another service are external effects; a database rollback cannot undo them. Review those hooks before reusing your request-time stack in a migration process.

Publication also includes cache and asset work#

Uploading a feature image, storing its URL, and refreshing a public page are separate steps. Verify the stored image bytes before assigning the URL. For a published article, check its card, header, and social metadata after the cache refresh.

If your web application's post hooks invalidate framework caches, determine whether those hooks can run from the import process. A shell job is not automatically executing inside a Next.js request. Use an authorized cache-invalidation or deployment workflow when needed, then verify the ordinary public URL.

The drafts and caching guide describes the visibility boundary for Next.js. The same principle applies elsewhere: a successful write is evidence of persistence, while a verified rendered page is evidence of publication.

Verify before expanding the batch#

In a local or staging database, test invalid input, a repeated import, a conflicting slug, tag reuse, and a failed later item in an atomic batch. Confirm that unrelated posts and drafts remain unchanged. If hooks perform external effects, test their retry behavior separately.

After publishing real content, verify its response, body text, canonical URL, metadata, links, sitemap inclusion, and image loading. Retain authored source files and the publication record so the next job can reconcile an uncertain outcome.

The Blog documentation, released operation schemas and hooks, and tag and post mutations define the behaviors to preserve.

In This Post

Choose the right operation surfacePrepare one record before writingMake a retry distinguishable from a second publicationPublication also includes cache and asset workVerify before expanding the batch