WebDesignDaddy Blog Setup Guide (Updated)
Fast, static Astro blog that pulls content from one WebDesignDaddy CMS site using the v1 API
Key Features
- Correct /api/v1/ endpoints with site_key query parameter
- Supports Posts (/posts/slug) and root Pages (/about, /contact)
- Auto-deploys on content publish via Netlify webhook
- Clean, responsive design with shared layout
- Secure environment variables
1. Project Structure
blog-webdesigndaddy/
├── src/
│ ├── pages/
│ │ ├── index.astro ← Blog homepage (list of posts)
│ │ ├── posts/
│ │ │ └── [slug].astro ← Individual blog posts
│ │ └── [slug].astro ← Root-level pages (About, Contact, etc.)
│ ├── layouts/
│ │ └── Layout.astro
│ ├── components/
│ │ └── Header.astro
│ └── lib/
│ └── api.js ← CMS API helper
├── public/
│ └── favicon.svg ← Optional
├── .env
├── astro.config.mjs
├── package.json
└── netlify.toml ← Optional
2. Key Files (Copy-Paste Ready)
.env (Never commit!)
PUBLIC_CMS_API_BASE=https://my.webdesigndaddy.com/api/v1
PUBLIC_CMS_SITE_KEY=wdd_U6Kt8ffr_yBzHLH5yourfullactualkeyhere src/lib/api.js
// src/lib/api.js
const API_BASE = import.meta.env.PUBLIC_CMS_API_BASE;
const SITE_KEY = import.meta.env.PUBLIC_CMS_SITE_KEY;
if (!API_BASE || !SITE_KEY) {
throw new Error('Missing PUBLIC_CMS_API_BASE or PUBLIC_CMS_SITE_KEY in .env');
}
const buildUrl = (path, params = {}) => {
const url = new URL(`${API_BASE}${path}`);
url.searchParams.append('site_key', SITE_KEY);
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.append(key, value);
}
});
return url.toString();
};
export async function getAllPosts() {
const url = buildUrl('/content', { type: 'post', status: 'published', limit: 100 });
const res = await fetch(url);
if (!res.ok) return [];
const json = await res.json();
return json.data || [];
}
export async function getAllPages() {
const url = buildUrl('/content', { type: 'page', status: 'published', limit: 100 });
const res = await fetch(url);
if (!res.ok) return [];
const json = await res.json();
return json.data || [];
}
export async function getContentBySlug(slug) {
const url = buildUrl(`/content/slug/${slug}`);
const res = await fetch(url);
if (!res.ok) return null;
const json = await res.json();
return json.data || null;
} src/layouts/Layout.astro
---
import Header from '../components/Header.astro';
const { title = 'WDD Blog' } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<link rel="icon" href="/favicon.svg" />
<TrackingHead />
</head>
<body class="min-h-screen bg-gray-50">
<Header />
<main class="max-w-4xl mx-auto px-6 py-12">
<slot />
</main>
<footer class="text-center py-8 text-gray-600">
© 2025 WDD Blog — Powered by WebDesignDaddy + Astro
</footer>
<TrackingBody />
</body>
</html> src/components/Header.astro
---
const response = await fetch(`https://my.webdesigndaddy.com/api/v1/content?site_key=${import.meta.env.PUBLIC_CMS_SITE_KEY}&limit=1`);
const json = await response.json();
const samplePost = json.data?.[0];
const siteName = samplePost ? 'WDD Blog' : 'WDD Blog'; // fallback
---
<header class="bg-blue-900 text-white py-8 mb-12">
<div class="max-w-4xl mx-auto px-6 text-center">
<h1 class="text-4xl font-bold">WDD Blog</h1>
<p class="mt-2 text-xl">blog.webdesigndaddy.com</p>
</div>
</header> src/pages/index.astro (Blog Homepage)
---
import Layout from '../layouts/Layout.astro';
import { getAllPosts } from '../lib/api';
const posts = await getAllPosts();
---
<Layout title="Home">
<h2 class="text-3xl font-bold mb-8 text-center">Latest Posts</h2>
{posts.length === 0 ? (
<p class="text-center text-gray-600">No posts yet. Add some in the CMS!</p>
) : (
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{posts.map(post => (
<article class="border rounded-lg p-6 hover:shadow-lg transition">
{post.featured_image_url && (
<img src={post.featured_image_url} alt={post.title} class="w-full h-48 object-cover rounded mb-4" />
)}
<h3 class="text-xl font-semibold mb-2">
<a href={`/posts/${post.slug}`} class="text-blue-800 hover:underline">
{post.title}
</a>
</h3>
{post.excerpt && <p class="text-gray-700 mb-4">{post.excerpt}</p>}
<time class="text-sm text-gray-500">
{new Date(post.created_at).toLocaleDateString()}
</time>
</article>
))}
</div>
)}
</Layout> src/pages/posts/[slug].astro (Blog Posts)
---
import Layout from '../../layouts/Layout.astro';
import { getAllPosts, getContentBySlug } from '../../lib/api';
export async function getStaticPaths() {
const posts = await getAllPosts();
return posts.map(post => ({ params: { slug: post.slug } }));
}
const { slug } = Astro.params;
const post = await getContentBySlug(slug);
if (!post) return Astro.redirect('/404');
---
<Layout title={post.title}>
<article class="prose max-w-none lg:prose-lg mx-auto">
<h1 class="text-5xl font-bold mb-6">{post.title}</h1>
{post.featured_image_url && <img src={post.featured_image_url} alt={post.title} class="w-full rounded-lg mb-8" />}
<time class="text-gray-600 block mb-8">
{new Date(post.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}
</time>
<div set:html={post.content_html} />
</article>
</Layout> src/pages/[slug].astro (Root Pages)
---
import Layout from '../layouts/Layout.astro';
import { getAllPages, getContentBySlug } from '../lib/api';
export async function getStaticPaths() {
const pages = await getAllPages();
return pages.map(page => ({ params: { slug: page.slug } }));
}
const { slug } = Astro.params;
const page = await getContentBySlug(slug);
if (!page) return Astro.redirect('/404');
---
<Layout title={page.title}>
<article class="prose max-w-none lg:prose-lg mx-auto">
<h1 class="text-5xl font-bold mb-6">{page.title}</h1>
{page.featured_image_url && <img src={page.featured_image_url} alt={page.title} class="w-full rounded-lg mb-8" />}
<div set:html={page.content_html} />
</article>
</Layout> astro.config.mjs & package.json
Use the standard Astro static setup shown in previous versions.
3. Deploy on Netlify
- Push code to GitHub repo
- Netlify → New site from Git → Connect repo
- Add environment variables: PUBLIC_CMS_API_BASE and PUBLIC_CMS_SITE_KEY
- Deploy!
4. Auto-Deploy on Publish
- Netlify → Site configuration → Build hooks → Add build hook ("CMS")
- Copy the generated webhook URL
- WebDesignDaddy CMS → Sites → Edit site → Webhook URLs → Paste Netlify URL
- Save → Instant rebuilds on every publish!
Done!
You now have blog posts at /posts/slug, static pages at root URLs, auto-deploys, and a secure modern stack. This is the battle-tested version for all future client blogs. Need navigation, search, RSS, or dark mode next? Let me know! 🚀
Comments
Approved comments appear below. Log in once with GFAVIP — it applies across the whole site. GFAVIP login
View comments archive