Frameworks
Nuxt
Use a Nuxt server endpoint and server-rendered page for published articles.
PeopleTide Content · HTTP integration guides
1. Prepare your project.
Use Nuxt with Node 22+. Save the shared helper as server/utils/delivery.js. Add runtimeConfig entries contentOrigin, contentOrganizationId, and contentSiteId, then configure NUXT_CONTENT_ORIGIN, NUXT_CONTENT_ORGANIZATION_ID, and NUXT_CONTENT_SITE_ID.
2. Add the shared delivery helper.
Copy this helper into the path named above. It follows a single published release without exposing authoring credentials.
// delivery.js — no SDK or API key required. Node 22+ or a modern browser.
export async function getArticle({ origin, organization, site, slug }) {
const base = new URL(origin);
if (base.protocol !== 'https:' && !['localhost', '127.0.0.1'].includes(base.hostname)) {
throw new Error('Use an HTTPS Content origin');
}
if (![organization, site].every(id => /^[a-f0-9-]{36}$/.test(id))) {
throw new Error('Set your organization and site UUIDs');
}
const prefix = '/delivery/v1/' + organization + '/' + site + '/';
async function read(path) {
const url = new URL(path, base);
if (url.origin !== base.origin || !url.pathname.startsWith(prefix) || url.search || url.hash) {
throw new Error('Unexpected delivery URL');
}
const response = await fetch(url, {
credentials: 'omit', cache: 'no-store', redirect: 'error',
signal: AbortSignal.timeout(8000),
});
if (!response.ok) throw new Error('Content delivery returned ' + response.status);
return response.json();
}
const current = await read(prefix + 'current.json');
const manifest = await read(current.url);
const entry = manifest.entries.find(item => item.type === 'article' && item.slug === slug);
if (!entry) return null;
const document = await read(entry.url);
return { releaseId: current.releaseId, document };
}Connect and render.
Publish an article of type article with slug welcome and a body field before running the example. Split the labelled files into the paths shown.
// nuxt.config.ts
export default defineNuxtConfig({ runtimeConfig: {
contentOrigin: '', contentOrganizationId: '', contentSiteId: '',
} });
// server/api/articles/[slug].get.ts
import { getArticle } from '../../utils/delivery.js';
export default defineEventHandler(async event => {
const config = useRuntimeConfig(event);
let result;
try {
result = await getArticle({ origin: config.contentOrigin,
organization: config.contentOrganizationId, site: config.contentSiteId,
slug: getRouterParam(event, 'slug') });
} catch { throw createError({ statusCode: 503, statusMessage: 'Content unavailable' }); }
if (!result) throw createError({ statusCode: 404, statusMessage: 'Article not published' });
return result.document;
});
<!-- pages/articles/[slug].vue -->
<script setup>
const route = useRoute();
const { data: article, error } = await useFetch('/api/articles/' + encodeURIComponent(route.params.slug));
if (error.value) throw createError({ statusCode: error.value.statusCode || 503, statusMessage: 'Article unavailable' });
</script>
<template><article v-if="article"><h1>{{ article.title }}</h1>
<p style="white-space: pre-wrap">{{ article.fields.body }}</p></article></template>Choose when content refreshes.
The server endpoint uses no-store upstream requests. Do not add an unbounded cachedEventHandler or routeRules cache unless the resulting freshness delay is intentional. Static generation requires a rebuild to refresh content.
Verify success and failure.
Open /articles/welcome and inspect the server-rendered HTML. Confirm an unknown slug gives 404 and an upstream failure gives 503. Publish a new release and check the next request after discovery expiry.