Aller au contenu principal

Page Management API - Quick Start Guide

Overview

This guide demonstrates how to use the Page Management API to build a dynamic, SEO-optimized website where the API is the source of truth for all page content.

Quick Start

1. Create Your First Page (Admin)

POST /page/new
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

{
"module_id": "60a7b8c9d0e1f2a3b4c5d6e7",
"slug": "about-us",
"path": "/about-us",
"title": "About Us - Our Story",
"pageType": "standard",
"active": true,
"indexed": true,
"translation": {
"lang": "en"
},
"content": "<h1>About Us</h1><p>We are a leading company...</p>",
"description": "Learn more about our company history and values",
"seo": {
"metaTitle": "About Us | Our Company Story",
"metaDescription": "Discover our journey, values, and the team behind our success. Founded in 2020, we've grown to become industry leaders.",
"ogTitle": "About Us - Our Company Story",
"ogDescription": "Discover our journey and values",
"ogImage": "https://yoursite.com/images/about-og.jpg",
"ogType": "website",
"robots": "index,follow"
},
"sitemap": {
"include": true,
"priority": 0.8,
"changefreq": "monthly"
}
}

Response:

{
"_id": "60a7b8c9d0e1f2a3b4c5d6e8",
"slug": "about-us",
"path": "/about-us",
"title": "About Us - Our Story",
"active": true,
"publishedAt": "2025-12-22T10:00:00.000Z",
"createdAt": "2025-12-22T10:00:00.000Z",
"updatedAt": "2025-12-22T10:00:00.000Z"
}

2. Fetch Page for Frontend Rendering

GET /page/path/about-us?module_id=60a7b8c9d0e1f2a3b4c5d6e7

Response:

{
"_id": "60a7b8c9d0e1f2a3b4c5d6e8",
"module_id": "60a7b8c9d0e1f2a3b4c5d6e7",
"slug": "about-us",
"path": "/about-us",
"title": "About Us - Our Story",
"pageType": "standard",
"active": true,
"indexed": true,
"content": "<h1>About Us</h1><p>We are a leading company...</p>",
"description": "Learn more about our company history and values",
"seo": {
"metaTitle": "About Us | Our Company Story",
"metaDescription": "Discover our journey, values, and the team behind our success...",
"ogTitle": "About Us - Our Company Story",
"ogImage": "https://yoursite.com/images/about-og.jpg",
"robots": "index,follow"
},
"sitemap": {
"include": true,
"priority": 0.8,
"changefreq": "monthly"
},
"viewCount": 0,
"publishedAt": "2025-12-22T10:00:00.000Z"
}

3. Create a Redirect (When Changing URLs)

POST /redirect/new
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

{
"module_id": "60a7b8c9d0e1f2a3b4c5d6e7",
"sourcePath": "/old-about-page",
"targetPath": "/about-us",
"statusCode": 301,
"matchType": "exact",
"active": true,
"reason": "URL structure update for better SEO"
}

4. Handle 404 with Redirect Resolution (Frontend)

GET /redirect/resolve/old-about-page?module_id=60a7b8c9d0e1f2a3b4c5d6e7

Response:

{
"sourcePath": "/old-about-page",
"targetPath": "/about-us",
"statusCode": 301,
"redirectType": "permanent"
}

Frontend Integration Examples

Next.js App Router (React)

// app/[...path]/page.tsx
import { notFound, redirect } from 'next/navigation';

const API_URL = process.env.API_URL;
const MODULE_ID = process.env.MODULE_ID;

async function getPage(path: string) {
// Try to fetch page
const pageResponse = await fetch(
`${API_URL}/page/path${path}?module_id=${MODULE_ID}`,
{ cache: 'no-store' }
);

if (pageResponse.ok) {
return { type: 'page', data: await pageResponse.json() };
}

// Check for redirect
const redirectResponse = await fetch(
`${API_URL}/redirect/resolve${path}?module_id=${MODULE_ID}`
);

if (redirectResponse.ok) {
const redirectData = await redirectResponse.json();
return { type: 'redirect', data: redirectData };
}

return { type: 'not-found' };
}

export default async function Page({ params }: { params: { path: string[] } }) {
const path = '/' + (params.path?.join('/') || '');
const result = await getPage(path);

if (result.type === 'redirect') {
redirect(result.data.targetPath);
}

if (result.type === 'not-found') {
notFound();
}

const page = result.data;

return (
<div>
<h1>{page.title}</h1>
<div dangerouslySetInnerHTML={{ __html: page.content }} />
</div>
);
}

// SEO Metadata
export async function generateMetadata({ params }: { params: { path: string[] } }) {
const path = '/' + (params.path?.join('/') || '');
const response = await fetch(
`${API_URL}/page/seo${path}?module_id=${MODULE_ID}`
);

if (!response.ok) return {};

const page = await response.json();

return {
title: page.seo.metaTitle || page.title,
description: page.seo.metaDescription,
keywords: page.seo.metaKeywords,
robots: page.seo.robots,
openGraph: {
title: page.seo.ogTitle || page.title,
description: page.seo.ogDescription,
images: page.seo.ogImage ? [page.seo.ogImage] : [],
type: page.seo.ogType || 'website',
},
twitter: {
card: page.seo.twitterCard || 'summary',
title: page.seo.twitterTitle || page.title,
description: page.seo.twitterDescription,
images: page.seo.twitterImage ? [page.seo.twitterImage] : [],
site: page.seo.twitterSite,
creator: page.seo.twitterCreator,
},
alternates: {
canonical: page.seo.canonicalUrl,
languages: page.seo.alternateUrls?.reduce((acc: any, alt: any) => {
acc[alt.lang] = alt.url;
return acc;
}, {}),
},
};
}

Vue.js 3 + Nuxt (Composition API)

<!-- pages/[...path].vue -->
<script setup lang="ts">
const route = useRoute();
const config = useRuntimeConfig();

const path = computed(() => '/' + (route.params.path as string[] || []).join('/'));

const { data: page, error } = await useFetch(
`${config.public.apiUrl}/page/path${path.value}`,
{
params: { module_id: config.public.moduleId },
}
);

if (error.value) {
// Try redirect
const { data: redirectData } = await useFetch(
`${config.public.apiUrl}/redirect/resolve${path.value}`,
{
params: { module_id: config.public.moduleId },
}
);

if (redirectData.value) {
navigateTo(redirectData.value.targetPath, {
redirectCode: redirectData.value.statusCode,
});
} else {
throw createError({ statusCode: 404, statusMessage: 'Page not found' });
}
}

// SEO Head
useHead({
title: page.value?.seo.metaTitle || page.value?.title,
meta: [
{ name: 'description', content: page.value?.seo.metaDescription },
{ name: 'keywords', content: page.value?.seo.metaKeywords?.join(', ') },
{ name: 'robots', content: page.value?.seo.robots },
{ property: 'og:title', content: page.value?.seo.ogTitle },
{ property: 'og:description', content: page.value?.seo.ogDescription },
{ property: 'og:image', content: page.value?.seo.ogImage },
{ name: 'twitter:card', content: page.value?.seo.twitterCard },
],
link: [
{ rel: 'canonical', href: page.value?.seo.canonicalUrl },
],
});
</script>

<template>
<div v-if="page">
<h1>{{ page.title }}</h1>
<div v-html="page.content"></div>
</div>
</template>

Express.js Backend (Server-Side Rendering)

import express from 'express';
import axios from 'axios';

const app = express();

app.get('*', async (req, res) => {
const path = req.path;
const moduleId = process.env.MODULE_ID;
const apiUrl = process.env.API_URL;

try {
// Try to fetch page
const pageResponse = await axios.get(
`${apiUrl}/page/path${path}`,
{ params: { module_id: moduleId } }
);

const page = pageResponse.data;

// Render HTML with SEO
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${page.seo.metaTitle || page.title}</title>
<meta name="description" content="${page.seo.metaDescription}">
<meta name="robots" content="${page.seo.robots}">
<link rel="canonical" href="${page.seo.canonicalUrl}">

<!-- OpenGraph -->
<meta property="og:title" content="${page.seo.ogTitle || page.title}">
<meta property="og:description" content="${page.seo.ogDescription}">
<meta property="og:image" content="${page.seo.ogImage}">
<meta property="og:type" content="${page.seo.ogType}">

<!-- Twitter -->
<meta name="twitter:card" content="${page.seo.twitterCard}">
<meta name="twitter:title" content="${page.seo.twitterTitle || page.title}">
<meta name="twitter:description" content="${page.seo.twitterDescription}">
<meta name="twitter:image" content="${page.seo.twitterImage}">
</head>
<body>
<h1>${page.title}</h1>
${page.content}
</body>
</html>
`;

res.send(html);

} catch (error: any) {
// Try redirect
try {
const redirectResponse = await axios.get(
`${apiUrl}/redirect/resolve${path}`,
{ params: { module_id: moduleId } }
);

const redirectData = redirectResponse.data;
res.redirect(redirectData.statusCode, redirectData.targetPath);

} catch {
res.status(404).send('Page not found');
}
}
});

app.listen(3000);

Common Use Cases

1. Homepage

{
"slug": "home",
"path": "/",
"title": "Welcome to Our Website",
"pageType": "homepage",
"seo": {
"metaTitle": "Website Name | Industry Leader",
"metaDescription": "Your trusted partner for...",
"structuredData": {
"@context": "https://schema.org",
"@type": "Organization",
"name": "Your Company",
"url": "https://yoursite.com",
"logo": "https://yoursite.com/logo.png"
}
},
"sitemap": {
"include": true,
"priority": 1.0,
"changefreq": "daily"
}
}

2. Blog Article with Author

{
"slug": "how-to-improve-seo",
"path": "/blog/how-to-improve-seo",
"title": "How to Improve SEO in 2025",
"pageType": "article",
"seo": {
"metaTitle": "How to Improve SEO in 2025 | Complete Guide",
"metaDescription": "Learn proven SEO strategies for 2025...",
"ogType": "article",
"structuredData": {
"@context": "https://schema.org",
"@type": "Article",
"headline": "How to Improve SEO in 2025",
"author": {
"@type": "Person",
"name": "John Doe"
},
"datePublished": "2025-12-22",
"dateModified": "2025-12-22",
"image": "https://yoursite.com/blog/seo-2025.jpg"
}
}
}

3. Product Page

{
"slug": "premium-headphones",
"path": "/products/premium-headphones",
"title": "Premium Wireless Headphones",
"pageType": "standard",
"seo": {
"metaTitle": "Premium Wireless Headphones | Buy Now",
"metaDescription": "High-quality wireless headphones with noise cancellation...",
"structuredData": {
"@context": "https://schema.org",
"@type": "Product",
"name": "Premium Wireless Headphones",
"description": "High-quality wireless headphones...",
"image": "https://yoursite.com/products/headphones.jpg",
"offers": {
"@type": "Offer",
"price": "299.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "127"
}
}
}
}

4. Landing Page with Scheduled Publishing

{
"slug": "black-friday-2025",
"path": "/promotions/black-friday-2025",
"title": "Black Friday Sale 2025",
"pageType": "landing",
"active": true,
"indexed": true,
"scheduledAt": "2025-11-29T00:00:00.000Z",
"expiresAt": "2025-12-01T23:59:59.000Z",
"seo": {
"metaTitle": "Black Friday Sale 2025 | Up to 70% Off",
"metaDescription": "Don't miss our biggest sale of the year!",
"robots": "noindex,follow"
}
}

5. Multi-Language Pages

// English version
{
"slug": "about-us",
"path": "/en/about-us",
"title": "About Us",
"translation": {
"lang": "en",
"ref_item": null
},
"seo": {
"canonicalUrl": "https://yoursite.com/en/about-us",
"alternateUrls": [
{ "lang": "fr", "url": "https://yoursite.com/fr/a-propos" },
{ "lang": "es", "url": "https://yoursite.com/es/acerca-de" }
]
}
}

// French version
{
"slug": "a-propos",
"path": "/fr/a-propos",
"title": "À Propos",
"translation": {
"lang": "fr",
"ref_item": "60a7b8c9d0e1f2a3b4c5d6e8"
},
"seo": {
"canonicalUrl": "https://yoursite.com/fr/a-propos",
"alternateUrls": [
{ "lang": "en", "url": "https://yoursite.com/en/about-us" },
{ "lang": "es", "url": "https://yoursite.com/es/acerca-de" }
]
}
}

Sitemap Generation

// Generate sitemap.xml
async function generateSitemap() {
const response = await fetch(
`${API_URL}/page/sitemap?module_id=${MODULE_ID}`
);
const { items } = await response.json();

return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${items.map((page: any) => `
<url>
<loc>${WEBSITE_URL}${page.path}</loc>
<lastmod>${new Date(page.updatedAt).toISOString()}</lastmod>
<changefreq>${page.sitemap.changefreq}</changefreq>
<priority>${page.sitemap.priority}</priority>
</url>
`).join('')}
</urlset>`;
}

// Next.js route: app/sitemap.xml/route.ts
export async function GET() {
const sitemap = await generateSitemap();

return new Response(sitemap, {
headers: {
'Content-Type': 'application/xml',
'Cache-Control': 'public, max-age=3600',
},
});
}

Testing

Test Page Creation

curl -X POST http://localhost:3000/api/page/new \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"module_id": "60a7b8c9d0e1f2a3b4c5d6e7",
"slug": "test-page",
"path": "/test-page",
"title": "Test Page",
"active": true,
"indexed": true,
"translation": { "lang": "en" }
}'

Test Page Retrieval

curl http://localhost:3000/api/page/path/test-page?module_id=60a7b8c9d0e1f2a3b4c5d6e7

Test Redirect Creation

curl -X POST http://localhost:3000/api/redirect/new \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"module_id": "60a7b8c9d0e1f2a3b4c5d6e7",
"sourcePath": "/old-test-page",
"targetPath": "/test-page",
"statusCode": 301,
"matchType": "exact",
"active": true
}'

Test Redirect Resolution

curl http://localhost:3000/api/redirect/resolve/old-test-page?module_id=60a7b8c9d0e1f2a3b4c5d6e7

Performance Tips

  1. Cache Published Pages: Frontend can cache pages for 1 hour
  2. Use CDN: Serve static content via CDN
  3. Lazy Load Images: Use lazy loading for page images
  4. Preload Critical Pages: Preload homepage and key pages
  5. Database Indexes: All critical indexes are already configured

Security Best Practices

  1. Protect Admin Endpoints: All write operations require authentication
  2. Sanitize HTML: Sanitize user-generated HTML content
  3. Rate Limiting: Implement rate limiting on public endpoints
  4. CORS Configuration: Configure CORS for your domain only
  5. Input Validation: All inputs are validated server-side

Troubleshooting

Page Returns 404

  • Check active status is true
  • Verify path matches exactly (case-insensitive)
  • Check scheduledAt and expiresAt dates
  • Ensure module_id is correct

Redirect Not Working

  • Verify redirect is active
  • Check matchType matches your use case
  • Ensure no redirect loops exist
  • Check expiration date

SEO Meta Tags Not Showing

  • Verify seo object is populated
  • Check frontend meta tag rendering
  • Use browser dev tools to inspect HTML
  • Validate structured data with Google's Rich Results Test

Need Help?

  • Review full documentation in PAGE_MANAGEMENT_SYSTEM.md
  • Check API errors in response body
  • Monitor server logs for detailed errors
  • Test endpoints with Postman or Insomnia