directus-sitemap
v0.1.0
Published
Webhook-driven multi-locale sitemap generator for Directus. Debounces, generates XML, uploads to S3, optionally pings IndexNow. Library + AWS Lambda template.
Maintainers
Readme
directus-sitemap
Webhook-driven multi-locale sitemap generator for Directus. Describe your collections once, wire a webhook, get a production-grade sitemap.xml + child sitemaps in S3 — with debouncing, IndexNow pinging, and proper hreflang alternates.
npm install directus-sitemap
# optional: notify Bing/Yandex when sitemaps change
npm install indexnow-notifyWhy
Every Directus + Next.js project hand-rolls this. The DIY version:
- queries collections, paginates, renders XML, uploads to S3
- generates one sitemap per locale + a sitemap index
- splits at the 50,000-URL spec limit
- emits
<xhtml:link rel="alternate" hreflang="...">alternates - debounces a flood of webhook hits when an editor saves 50 records in 10 seconds
- secures the webhook endpoint with a shared secret
- pings IndexNow after upload
This package does all of that, config-driven.
Configure
sitemap.config.ts:
import type { SitemapConfig } from "directus-sitemap";
export const config: SitemapConfig = {
siteUrl: "https://example.com",
directusUrl: "https://cms.example.com",
directusToken: process.env.DIRECTUS_TOKEN,
locales: ["en", "de", "fr"],
s3Bucket: "example-sitemaps",
staticBundles: [
{
name: "static",
locales: ["en", "de", "fr"],
build: (locale) => [
{ loc: `https://example.com/${locale}`, changefreq: "daily", priority: 1.0 },
],
},
],
collections: [
{
name: "posts",
collection: "blog_posts",
fields: ["slug", "date_updated", "status"],
filter: { status: { _eq: "published" } },
buildUrl: (item, locale) =>
item.slug ? `https://example.com/${locale}/blog/${item.slug}` : null,
lastmod: (item) => item.date_updated,
changefreq: "weekly",
priority: 0.7,
},
],
};Deploy as Lambda
// handler.ts
import { createSitemapHandler } from "directus-sitemap/lambda";
import { submitToIndexNow } from "indexnow-notify";
import { config } from "./sitemap.config.js";
export const handler = createSitemapHandler({
config,
webhookSecret: process.env.WEBHOOK_SECRET,
notify: () =>
submitToIndexNow([config.siteUrl], {
host: new URL(config.siteUrl).host,
key: process.env.INDEXNOW_KEY!,
}),
});Point a Directus webhook at the Lambda Function URL with x-webhook-secret set. Every save triggers a rebuild.
Run anywhere
The library is runtime-agnostic — call buildAndUploadSitemaps(config) from a cron job, a Next.js route handler, or a package.json script.
import { buildAndUploadSitemaps } from "directus-sitemap";
const result = await buildAndUploadSitemaps(config);
console.log(`${result.totalUrls} URLs across ${result.uploaded.length} sitemaps`);Output
s3://example-sitemaps/
sitemap.xml ← index
sitemap-static-en.xml
sitemap-static-de.xml
sitemap-posts-en.xml
sitemap-posts-de.xml
...Serve them via CloudFront, or proxy from your Next.js app/sitemap.xml/route.ts.
Features
- Per-collection:
fields,filter,pageSize,buildUrl,lastmod,changefreq,priority - Automatic splitting at 50,000 URLs (
sitemap-name-locale-1.xml,-2.xml, …) hreflangalternates (return analternatesarray from your URL builder)- Debouncer helper for webhook bursts
- Webhook secret validation
- IndexNow integration via optional peer dep
License
MIT
