Install Node.js Package
Install the SEO Core package, configure middleware and sync, and verify server-side deployment delivery in your Node.js application.
What it does
The @seo-core-app-ai/node-deploypackage integrates into your Node.js server as middleware. It maintains an up-to-date local copy of your active deployments, and applies them to every HTML response before it's sent to the client. Changes you make in the dashboard propagate to your server automatically — no app restart, no rebuild, no deployment pipeline required.
Server-side rendering
Deployments are applied in the HTML before it leaves your server — visible to crawlers and browsers without any JavaScript dependency.
Real-time push sync
Register your server's webhook URL and new deployments reach your app in under 3 seconds after you save them in the dashboard.
Automatic fallback polling
Even without a registered webhook, the package polls for changes every 30 seconds so deployments stay current regardless of your infrastructure.
Zero-restart updates
The deployment cache updates in the background. Your application keeps serving traffic throughout — no downtime, no restarts.
Requirements
- Node.js 18 or later.
- A SEO Core project with a verified domain and a deploy token.
- Your server must be able to make outbound HTTPS requests to the SEO Core API.
- For real-time push sync: your server must be publicly reachable via HTTPS.
Installation
- 1
Install the package
npm install @seo-core-app-ai/node-deploy - 2
Add your credentials as environment variables
Find your Project ID and Deploy Token in your dashboard under Projects → Deploy Script → Node.js. Add them to your environment:
SEO_DEPLOY_PROJECT_ID=your_project_id SEO_DEPLOY_TOKEN=your_deploy_tokenImportant
Never hard-code your deploy token in source files. Use environment variables so the token stays out of version control.
- 3
Initialize the client at startup
Create a singleton client — typically in a shared file like
lib/seo-client.ts— and callinitialize()once when your server starts:import { SeoDeployClient } from "@seo-core-app-ai/node-deploy"; export const seoClient = new SeoDeployClient({ projectId: parseInt(process.env.SEO_DEPLOY_PROJECT_ID!), token: process.env.SEO_DEPLOY_TOKEN!, }); await seoClient.initialize();Tip
Call
initialize()before your server starts accepting requests so the deployment cache is populated from the first request.
Express integration
Add the middleware before your route handlers. It automatically intercepts HTML responses and applies any active deployments for the requested URL.
import express from "express";
import { createExpressMiddleware } from "@seo-core-app-ai/node-deploy/express";
import { seoClient } from "./lib/seo-client";
const app = express();
// Add before your routes
app.use(createExpressMiddleware(seoClient));
app.get("/", (req, res) => {
res.send("<html><head><title>My Site</title></head>...</html>");
// The middleware patches the title if a deployment is active for this URL
});
app.listen(3000);Note
The middleware only modifies responses with a Content-Type: text/html header. JSON, images, and other response types are passed through unchanged.
Fastify integration
Register the plugin on your Fastify instance. It uses a lifecycle hook to patch HTML responses before they're sent.
import Fastify from "fastify";
import { seoDeployPlugin } from "@seo-core-app-ai/node-deploy/fastify";
import { seoClient } from "./lib/seo-client";
const app = Fastify();
await app.register(seoDeployPlugin, { client: seoClient });
app.get("/", async () => {
return "<html><head><title>My Site</title></head>...</html>";
});
await app.listen({ port: 3000 });Next.js integration (App Router)
The package ships a ready-made SeoDeployHead server component. Add it once to your root layout — no changes to individual pages required. It injects title, description, and canonical tags directly into the HTML on every request.
1. Create a singleton client
Create lib/seo-deploy-client.ts to hold a shared client instance that persists across hot reloads in development:
// lib/seo-deploy-client.ts
import { SeoDeployClient } from "@seo-core-app-ai/node-deploy";
declare global { var _seoDeployClient: SeoDeployClient | undefined; }
if (!global._seoDeployClient) {
global._seoDeployClient = new SeoDeployClient({
projectId: parseInt(process.env.SEO_DEPLOY_PROJECT_ID!),
token: process.env.SEO_DEPLOY_TOKEN!,
});
global._seoDeployClient.initialize().catch(console.error);
}
export const seoDeployClient = global._seoDeployClient;2. Pass the pathname via middleware
SeoDeployHead needs to know which page is being rendered. Add one line to your middleware.ts to forward the pathname as a request header:
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(req: NextRequest) {
const res = NextResponse.next();
res.headers.set("x-seo-core-pathname", req.nextUrl.pathname);
return res;
}
export const config = { matcher: ["/((?!_next|favicon.ico).*)"] };Note
If you already have a middleware.ts, just add the res.headers.set line — you don't need a separate middleware.
3. Add SeoDeployHead to your root layout
This is the only change needed to your app. Every page automatically gets deployments applied — no per-page code required.
// app/layout.tsx
import { SeoDeployHead } from "@seo-core-app-ai/node-deploy/nextjs/head";
import { seoDeployClient } from "@/lib/seo-deploy-client";
export default function RootLayout({ children }) {
return (
<html>
<head>
{/* Applies active deployments — title, description, canonical */}
<SeoDeployHead client={seoDeployClient} />
</head>
<body>{children}</body>
</html>
);
}Tip
SeoDeployHead returns null when no deployment is active for the current URL, so there is no performance cost on undeployed pages.
4. Set environment variables
SEO_DEPLOY_PROJECT_ID=your_project_id
SEO_DEPLOY_TOKEN=your_deploy_tokenImportant
Never commit your deploy token. Use environment variables.
Real-time push sync (optional)
By default, the client polls for deployment changes every 30 seconds. Push sync is optional — it delivers changes to your server the moment you save them in the dashboard (under 3 seconds), instead of waiting for the next poll.
Express and Fastify handle the push webhook automatically — no extra setup needed. Next.js users can optionally add a route handler:
// app/seo-core/sync/route.ts
import { seoDeployClient } from "@/lib/seo-deploy-client";
import { handleNextjsPushSync } from "@seo-core-app-ai/node-deploy/nextjs";
export async function POST(req: Request) {
return handleNextjsPushSync(req, seoDeployClient);
}Then register https://your-site.com/seo-core/sync as your webhook URL in Projects → Deploy Script → Node.js → Push sync webhook.
Note
If your server is behind a private network and push sync isn't reachable, the package falls back to polling automatically. You can adjust the interval with the pollIntervalMs option, or set it to 0 to disable polling entirely.
Configuration options
new SeoDeployClient({
projectId: 123, // Required. Your project ID.
token: "your_token", // Required. Keep in environment variables.
apiOrigin: "https://www.seocoreapp.com/api", // Optional. Override API base URL.
pollIntervalMs: 30_000, // Optional. Background poll interval. Set 0 to disable.
heartbeatIntervalMs: 60_000, // Optional. Lightweight freshness check interval.
webhookPath: "/seo-core/sync",// Optional. Path where your server receives push sync.
publicUrl: "https://your-site.com", // Optional. Enables auto-registration of push sync webhook.
deploymentTypes: ["title", "description"], // Optional. Limit which types apply.
onError: (err) => console.error(err), // Optional. Error callback.
});Serverless environments
Important
Serverless functions (Vercel Functions, AWS Lambda) don't maintain persistent memory between invocations. The client fetches deployments on each cold start and reuses them for warm invocations within the same function lifetime. Set pollIntervalMs: 0 to avoid unnecessary background timers in serverless contexts.
For shared state across multiple serverless invocations, the client supports a custom cacheAdapter interface that you can back with Redis, Upstash, or any key-value store.
