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

  1. Push code to GitHub repo
  2. Netlify → New site from Git → Connect repo
  3. Add environment variables: PUBLIC_CMS_API_BASE and PUBLIC_CMS_SITE_KEY
  4. Deploy!

4. Auto-Deploy on Publish

  1. Netlify → Site configuration → Build hooks → Add build hook ("CMS")
  2. Copy the generated webhook URL
  3. WebDesignDaddy CMS → Sites → Edit site → Webhook URLs → Paste Netlify URL
  4. 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