Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions .claude/article-brief-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Article Brief Template

Use this template when planning new blog articles for Node.js Design Patterns.

---

## [Article Title]

**Target keyword:** [primary keyword]
**Secondary keywords:** [comma-separated list]
**Estimated search volume:** [monthly searches]
**Search intent:** [informational / tutorial / reference]
**Buyer stage:** [awareness / consideration / implementation]
**Content pillar:** [Core APIs / Modern Features / Patterns & Architecture]

---

### Outline

1. **Introduction**
- Problem statement / why this matters
- What readers will learn

2. **Quick Answer** (for featured snippets)
- 2-3 sentence summary
- Code snippet if applicable

3. **Main Content**
- Section 1: [topic]
- Section 2: [topic]
- Section 3: [topic]
- (Add more sections as needed)

4. **Best Practices / Common Mistakes**
- List of dos and don'ts
- Common pitfalls to avoid

5. **Related Book Content** (if applicable)
- Light excerpt or reference to relevant chapter
- Connection to book patterns

6. **FAQ Section**
- 3-5 frequently asked questions
- Keep answers concise (2-3 sentences)

7. **Conclusion**
- Summary of key points
- CTA: Free chapter download

---

### Code Examples Checklist

- [ ] Modern ESM syntax (`import`/`export`)
- [ ] `async`/`await` patterns (avoid callbacks)
- [ ] Proper error handling included
- [ ] Practical, real-world examples
- [ ] Comments explain non-obvious code
- [ ] No external dependencies unless necessary

---

### Internal Links

Link to related existing articles:

- [ ] [Article 1 title](/blog/slug)
- [ ] [Article 2 title](/blog/slug)

Link to book chapters:

- [ ] [Relevant chapter](/)

---

### SEO Checklist

- [ ] Keyword in title
- [ ] Keyword in H1
- [ ] Keyword in first paragraph
- [ ] Keyword in at least one H2
- [ ] Meta description (150-160 characters)
- [ ] Alt text for all images
- [ ] FAQ section with 3-5 questions (for FAQ schema)
- [ ] Internal links to 2+ related articles
- [ ] External links to official docs where relevant

---

### Frontmatter Template

```yaml
---
date: YYYY-MM-DDTHH:MM:SS
updatedAt: YYYY-MM-DDTHH:MM:SS
title: [Article Title]
slug: [url-friendly-slug]
description: [150-160 character meta description with target keyword]
authors: ['luciano-mammino']
tags: ['blog']
faq:
- question: [FAQ Question 1]?
answer: [Concise 2-3 sentence answer]
- question: [FAQ Question 2]?
answer: [Concise 2-3 sentence answer]
- question: [FAQ Question 3]?
answer: [Concise 2-3 sentence answer]
---
```

---

### Content Calendar Reference

| Month | # | Article | Primary Keyword | Est. Volume |
| ----- | --- | -------------------------------- | ------------------------------- | ----------- |
| 1-2 | 1 | HTTP request in Node.js | "node js http request" | 15,000+ |
| 1-2 | 2 | Environment variables in Node.js | "node js environment variables" | 8,000+ |
| 1-2 | 3 | Using Node.js built-in SQLite | "node js sqlite" | 5,000+ |
| 1-2 | 4 | Writing a CLI with Node.js | "node js cli" | 4,000+ |
| 3-4 | 5 | API server in Node.js (no deps) | "node js http server" | 6,000+ |
| 3-4 | 6 | Hashing files with Node.js | "node js hash file" | 2,000+ |
| 3-4 | 7 | Base64 encode/decode | "base64 node js" | 3,000+ |
| 3-4 | 8 | Files and paths (node:path) | "node js path" | 4,000+ |
| 5-6 | 9 | Node.js Event Emitter | "node js event emitter" | 6,000+ |
| 5-6 | 10 | Interactive streams guide | "node js streams" | 10,000+ |
| 5-6 | 11 | How CommonJS works | "commonjs node js" | 3,000+ |
| 5-6 | 12 | Node.js console tips | "node js console" | 2,000+ |
| 7-8 | 13 | Type-stripping in Node.js | "node js typescript" | Growing |
| 7-8 | 14 | Node.js built-in test runner | "node js test runner" | 3,000+ |
| 7-8 | 15 | Import maps in Node.js | "node js import maps" | 1,000+ |
| 7-8 | 16 | Encrypting files with Node.js | "node js encrypt file" | 1,500+ |

---

### Success Metrics

After publishing, track:

- Organic traffic (Google Search Console)
- Keyword rankings for target terms
- Free chapter downloads from blog CTAs
- Time on page and scroll depth
- Internal link clicks to book pages
125 changes: 125 additions & 0 deletions .claude/content-calendar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Content Calendar - Node.js Design Patterns Blog

**Publishing cadence:** 2 articles/month
**Primary goal:** SEO traffic → Build authority → Drive book sales
**Content approach:** Original content with light book excerpts

---

## Content Pillars

1. **Node.js Core APIs & Built-ins** - High-volume foundational topics
2. **Modern Node.js Features** - New capabilities (22+, 23+) with early-mover SEO advantage
3. **Node.js Patterns & Architecture** - Advanced patterns (light connection to book)

---

## 12-Month Calendar

### Month 1-2: Foundation

| # | Status | Article | Primary Keyword | Est. Volume | Pillar |
| --- | ----------- | -------------------------------- | ------------------------------- | ----------- | --------- |
| 1 | COMPLETE | **HTTP request in Node.js** | "node js http request" | 15,000+ | Core APIs |
| 2 | NOT STARTED | Environment variables in Node.js | "node js environment variables" | 8,000+ | Core APIs |
| 3 | NOT STARTED | Using Node.js built-in SQLite | "node js sqlite" | 5,000+ | Modern |
| 4 | NOT STARTED | Writing a CLI with Node.js | "node js cli" | 4,000+ | Core APIs |

### Month 3-4: Core APIs

| # | Status | Article | Primary Keyword | Est. Volume | Pillar |
| --- | ----------- | ------------------------------- | --------------------- | ----------- | --------- |
| 5 | NOT STARTED | API server in Node.js (no deps) | "node js http server" | 6,000+ | Core APIs |
| 6 | NOT STARTED | Hashing files with Node.js | "node js hash file" | 2,000+ | Core APIs |
| 7 | NOT STARTED | Base64 encode/decode | "base64 node js" | 3,000+ | Core APIs |
| 8 | NOT STARTED | Files and paths (node:path) | "node js path" | 4,000+ | Core APIs |

### Month 5-6: Patterns (Book-Adjacent)

| # | Status | Article | Primary Keyword | Est. Volume | Pillar |
| --- | ----------- | --------------------------- | ----------------------- | ----------- | --------- |
| 9 | NOT STARTED | Node.js Event Emitter guide | "node js event emitter" | 6,000+ | Patterns |
| 10 | NOT STARTED | Interactive streams guide | "node js streams" | 10,000+ | Patterns |
| 11 | NOT STARTED | How CommonJS works | "commonjs node js" | 3,000+ | Patterns |
| 12 | NOT STARTED | Node.js console tips | "node js console" | 2,000+ | Core APIs |

### Month 7-8: Modern Node.js

| # | Status | Article | Primary Keyword | Est. Volume | Pillar |
| --- | ----------- | ----------------------------- | ---------------------- | ----------- | --------- |
| 13 | NOT STARTED | Type-stripping in Node.js | "node js typescript" | Growing | Modern |
| 14 | NOT STARTED | Node.js built-in test runner | "node js test runner" | 3,000+ | Modern |
| 15 | NOT STARTED | Import maps in Node.js | "node js import maps" | 1,000+ | Modern |
| 16 | NOT STARTED | Encrypting files with Node.js | "node js encrypt file" | 1,500+ | Core APIs |

### Month 9-12: Advanced & Experimental

| # | Status | Article | Primary Keyword | Notes |
| --- | ----------- | ------------------------------ | --------------- | --------------------- |
| 17+ | NOT STARTED | Rust/Zig + Node.js integration | niche | Thought leadership |
| 18+ | NOT STARTED | TBD based on analytics | TBD | Iterate based on data |

---

## Existing Content

| Topic | Coverage | Link |
| ------------------------ | -------- | -------------------------------------------------------- |
| Reading/writing files | COMPLETE | /blog/reading-writing-files-nodejs |
| Stream consumers | COMPLETE | /blog/node-js-stream-consumer |
| Async iterators | COMPLETE | /blog/javascript-async-iterators |
| Race conditions | COMPLETE | /blog/node-js-race-conditions |
| Checking Node.js version | COMPLETE | /blog/checking-node-js-version |
| Installing Node.js | UPDATED | /blog/5-ways-to-install-node-js |
| Docker development | COMPLETE | /blog/node-js-development-with-docker-and-docker-compose |

---

## Internal Linking Map

```
HTTP Requests
└── links to → API Server (no deps)
└── links to → Reading/Writing Files (existing)

Environment Variables
└── links to → CLI with parseArgs

Files & Paths
└── links to → Reading/Writing Files (existing)
└── links to → Hashing Files
└── links to → Encrypting Files

Streams Guide
└── links to → Stream Consumer (existing)
└── links to → Async Iterators (existing)
└── links to → Reading/Writing Files (existing)

Event Emitter
└── links to → Streams Guide
└── links to → Race Conditions (existing)

Installing Node.js
└── links to → Checking Node.js version (existing)
└── links to → Docker development (existing)
```

---

## Quick Wins Checklist

- [x] Add FAQ schema support to blog layout
- [x] Update "5 Ways to Install Node.js" with fnm, Volta, Docker
- [x] Cross-link existing articles
- [x] Add CTAs for free chapter download to all posts
- [ ] Monitor keyword rankings for existing content
- [ ] A/B test CTA placements

---

## Notes

- Focus on zero-dependency approaches using modern Node.js built-ins
- Most competing articles still recommend axios/got first - opportunity to differentiate
- New Node.js features (22+, 23+) have early-mover advantage
- Light book excerpts where relevant (Ch 6 streams, Ch 10 testing)
11 changes: 10 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,16 @@
"allow": [
"Bash(pnpm build:*)",
"Bash(pnpm exec astro build:*)",
"Bash(pnpm install:*)"
"Bash(pnpm install:*)",
"Skill(marketing-skills:content-strategy)",
"Bash(find:*)",
"Bash(npm run build:*)",
"WebSearch",
"Bash(node fetch-get.js:*)",
"Bash(node fetch-post.js)",
"Bash(node http-get.js:*)",
"Bash(node http-post.js:*)",
"Bash(node:*)"
]
}
}
22 changes: 15 additions & 7 deletions src/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface Props {
ogDescription?: string
ogImage?: string
canonical?: string
additionalSchema?: object
additionalSchema?: object | object[]
noindex?: boolean
}

Expand Down Expand Up @@ -143,12 +143,20 @@ const {
</script>

{
additionalSchema && (
<script
type="application/ld+json"
set:html={JSON.stringify(additionalSchema)}
/>
)
additionalSchema &&
(Array.isArray(additionalSchema) ? (
additionalSchema.map((schema) => (
<script
type="application/ld+json"
set:html={JSON.stringify(schema)}
/>
))
) : (
<script
type="application/ld+json"
set:html={JSON.stringify(additionalSchema)}
/>
))
}

<script>
Expand Down
10 changes: 7 additions & 3 deletions src/components/blog/BlogLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Footer from '@components/Footer.astro'
import BookPromo from '@components/blog/BookPromo.astro'
import { List } from '@lucide/astro'
import { calculateReadingTime, formatDate } from '@lib/utils'
import { generateBlogPostingSchema } from '@lib/schema'
import { generateBlogPostingSchema, generateFAQPageSchema } from '@lib/schema'
import Breadcrumb from './Breadcrumb.astro'
import BlogCard from './BlogCard.astro'

Expand All @@ -31,7 +31,7 @@ type NestedCollectionHeadingsItem = CollectionEntryHeadingsItem & {
}

const { post, authors, prevPost, nextPost } = Astro.props
const { title, description, date, updatedAt } = post.data
const { title, description, date, updatedAt, faq } = post.data

const formattedDate = formatDate(date)

Expand All @@ -54,6 +54,10 @@ const blogPostingSchema = generateBlogPostingSchema({
siteUrl: SITE_URL,
})

// Generate FAQ schema if FAQ items are present
const faqSchema = faq && faq.length > 0 ? generateFAQPageSchema(faq) : null
const schemas = faqSchema ? [blogPostingSchema, faqSchema] : blogPostingSchema

// Groups ToC in a way that it's easier to render as nested lists
const nestedToc: NestedCollectionHeadingsItem[] = []
let lastNestedTocEntry: NestedCollectionHeadingsItem | null = null
Expand All @@ -78,7 +82,7 @@ if (post.rendered?.metadata?.headings) {
ogTitle={title}
ogDescription={description}
canonical={`${SITE_URL}/blog/${post.id}/`}
additionalSchema={blogPostingSchema}
additionalSchema={schemas}
>
<main class="min-h-screen bg-base-100">
<!-- Blog Header -->
Expand Down
8 changes: 8 additions & 0 deletions src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ const blog = defineCollection({
description: z.string(),
authors: z.array(reference('authors')),
tags: z.array(z.string()),
faq: z
.array(
z.object({
question: z.string(),
answer: z.string(),
}),
)
.optional(),
}),
})

Expand Down
Loading