Skip to content

Commit 70bb9ce

Browse files
authored
Migrate to v5 of the notion SDK and switch from databases to data sources (#694)
* wip * wip * migrate to new version of the notion api * update claude-md
1 parent 8a5894e commit 70bb9ce

File tree

9 files changed

+117
-28
lines changed

9 files changed

+117
-28
lines changed

.claude/settings.local.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
"Bash(npm ls:*)",
2121
"Bash(npm install)",
2222
"Bash(git add package.json)",
23-
"Bash(git add package-lock.json)"
23+
"Bash(git add package-lock.json)",
24+
"WebSearch",
25+
"WebFetch(domain:github.com)",
26+
"Bash(npm test)",
27+
"Bash(npm run typecheck:*)",
28+
"WebFetch(domain:developers.notion.com)",
29+
"Bash(node:*)",
30+
"Bash(npm run build:*)"
2431
],
2532
"deny": [],
2633
"ask": []

CLAUDE.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Claude Code Memory Bank
2+
3+
## Project Information
4+
5+
- **Project**: Personal homepage/blog built with Next.js and Notion as CMS
6+
- **Tech Stack**: Next.js 15.5.4, TypeScript, @notionhq/client 5.1.0, notion-to-md, Tailwind CSS
7+
- **Testing**: Vitest with 71 tests
8+
- **Node Version**: 22.x
9+
10+
## Recent Updates
11+
12+
### @notion/client Migration (v4.0.2 → v5.1.0) - Completed
13+
14+
**Date**: 2025-09-28
15+
16+
Successfully migrated from @notion/client v4.0.2 to v5.1.0 without breaking functionality.
17+
18+
**Key Changes Made**:
19+
20+
1. **API Method Change**: `notion.databases.query()``notion.dataSources.query()`
21+
2. **Parameter Change**: `database_id``data_source_id`
22+
3. **Import Path Fix**: `ImageBlockObjectResponse` now imported from main module
23+
4. **API Version**: Using default API version (2025-09-03) - older versions had compatibility issues
24+
5. **Test Updates**: Updated mocks to use `dataSources` instead of `databases`
25+
26+
**Important Notes**:
27+
28+
- Switched to use notion data source ids instead of Database IDs
29+
30+
**Files Modified**:
31+
32+
- `src/lib/notion.ts` - Core Notion API interactions
33+
- `scripts/cache-posts.ts` - Post caching script
34+
- `__tests__/lib/notion.test.ts` - Unit tests
35+
36+
**Verification**:
37+
38+
- ✅ All 71 tests pass
39+
- ✅ TypeScript compilation successful
40+
- ✅ Build completes without errors
41+
- ✅ Static generation works correctly
42+
43+
## Commands to Remember
44+
45+
### Development
46+
47+
- `npm run dev` - Start development server
48+
- `npm run build` - Production build
49+
- `npm test` - Run all tests
50+
- `npm run typecheck` - TypeScript validation
51+
- `npm run lint` - ESLint
52+
- `npm run cache:posts` - Cache posts from Notion
53+
54+
### Testing
55+
56+
- `npm run test:watch` - Watch mode testing
57+
- `npm run test:coverage` - Coverage report
58+
- `npm run test:ui` - Vitest UI
59+
60+
## Architecture Notes
61+
62+
### Notion Integration
63+
64+
- Uses Notion as headless CMS for blog posts and photos
65+
- Posts cached locally in `posts-cache.json` and `photos-cache.json`
66+
- Supports two sections: "All" (general posts) and "VBC" (Value-Based Care posts)
67+
- Image handling with local download and optimization
68+
69+
### File Structure
70+
71+
- `src/lib/notion.ts` - Main Notion API wrapper
72+
- `scripts/cache-posts.ts` - Caching script for posts and photos
73+
- `__tests__/` - Test files using Vitest
74+
- Posts cached as JSON files in project root
75+
76+
### API Compatibility
77+
78+
- Database queries work with both posts and photos databases

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ This is a [Next.js](https://nextjs.org/) blog written in [TypeScript](https://ww
5656
Follow the [Notion API Getting Started Guide](https://developers.notion.com/docs/getting-started) to obtain:
5757

5858
- `NOTION_TOKEN`: Your Notion integration token.
59-
- `NOTION_POSTS_DATABASE_ID`: The database ID for your blog posts.
60-
- `NOTION_PHOTOS_DATABASE_ID`: The database ID for your photo gallery.
59+
- `NOTION_POSTS_DATA_SOURCE_ID`: The data source ID for your blog posts.
60+
- `NOTION_PHOTOS_DATA_SOURCE_ID`: The data source ID for your photo gallery.
6161

6262
Save these in a `.env.local` file:
6363

6464
```env
6565
NOTION_TOKEN=your-notion-token
66-
NOTION_POSTS_DATABASE_ID=your-posts-database-id
67-
NOTION_PHOTOS_DATABASE_ID=your-photos-database-id
66+
NOTION_POSTS_DATA_SOURCE_ID=your-posts-data-source-id
67+
NOTION_PHOTOS_DATA_SOURCE_ID=your-photos-data-source-id
6868
```
6969

7070
### 2. **Install Dependencies**

__tests__/lib/notion.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,18 +243,18 @@ describe('notion.ts - Unit Tests', () => {
243243
describe('fetchPublishedPosts', () => {
244244
it('should fetch published posts from Notion', async () => {
245245
const mockNotionClient = {
246-
databases: {
246+
dataSources: {
247247
query: vi.fn().mockResolvedValue({
248248
results: [{ id: '1' }, { id: '2' }],
249249
}),
250250
},
251251
};
252252

253-
const databaseId = 'test-database-id';
254-
const posts = await fetchPublishedPosts(mockNotionClient as unknown as Client, databaseId);
253+
const dataSourceID = 'test-database-id';
254+
const posts = await fetchPublishedPosts(mockNotionClient as unknown as Client, dataSourceID);
255255

256-
expect(mockNotionClient.databases.query).toHaveBeenCalledWith({
257-
database_id: databaseId,
256+
expect(mockNotionClient.dataSources.query).toHaveBeenCalledWith({
257+
data_source_id: dataSourceID,
258258
filter: {
259259
and: [
260260
{
@@ -278,7 +278,7 @@ describe('notion.ts - Unit Tests', () => {
278278

279279
it('should handle Notion API errors', async () => {
280280
const mockNotionClient = {
281-
databases: {
281+
dataSources: {
282282
query: vi.fn().mockRejectedValue(new Error('Notion API error')),
283283
},
284284
};

__tests__/setup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ vi.mock('next/navigation', () => ({
2424

2525
// Mock environment variables
2626
process.env.NOTION_TOKEN = 'test-notion-token';
27-
process.env.NOTION_DATABASE_ID = 'test-database-id';
28-
process.env.NOTION_PHOTOS_DATABASE_ID = 'test-photos-database-id';
27+
process.env.NOTION_DATA_SOURCE_ID = 'test-database-id';
28+
process.env.NOTION_PHOTOS_DATA_SOURCE_ID = 'test-photos-database-id';
2929

3030
// Suppress console errors in tests
3131
global.console = {

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"typecheck": "tsc --noEmit"
2222
},
2323
"dependencies": {
24-
"@notionhq/client": "4.0.2",
24+
"@notionhq/client": "5.1.0",
2525
"@radix-ui/react-label": "2.1.7",
2626
"@radix-ui/react-slot": "1.2.3",
2727
"@types/react-syntax-highlighter": "15.5.13",

scripts/cache-posts.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import path from 'path';
55
import { fetchPublishedPosts, getPostFromNotion } from '../src/lib/notion';
66

77
interface CacheConfig {
8-
databaseId: string;
8+
dataSourceID: string;
99
cacheFileName: string;
1010
itemName: string;
1111
}
1212

1313
const cacheItems = async (notion: Client, config: CacheConfig) => {
1414
try {
1515
console.log(`Fetching ${config.itemName} from Notion...`);
16-
const posts = await fetchPublishedPosts(notion, config.databaseId);
16+
const posts = await fetchPublishedPosts(notion, config.dataSourceID);
1717

1818
const allPosts = [];
1919

@@ -34,18 +34,20 @@ const cacheItems = async (notion: Client, config: CacheConfig) => {
3434
}
3535
};
3636

37-
const notion = new Client({ auth: process.env.NOTION_TOKEN });
37+
const notion = new Client({
38+
auth: process.env.NOTION_TOKEN,
39+
});
3840

3941
// Cache posts and photos using the generic function
4042
(async () => {
4143
await Promise.all([
4244
cacheItems(notion, {
43-
databaseId: process.env.NOTION_DATABASE_ID!,
45+
dataSourceID: process.env.NOTION_DATA_SOURCE_ID!,
4446
cacheFileName: 'posts-cache.json',
4547
itemName: 'posts',
4648
}),
4749
cacheItems(notion, {
48-
databaseId: process.env.NOTION_PHOTOS_DATABASE_ID!,
50+
dataSourceID: process.env.NOTION_PHOTOS_DATA_SOURCE_ID!,
4951
cacheFileName: 'photos-cache.json',
5052
itemName: 'photos',
5153
}),

src/lib/notion.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Client } from '@notionhq/client';
2+
import { ImageBlockObjectResponse } from '@notionhq/client';
23
import { PageObjectResponse } from '@notionhq/client/';
3-
import { ImageBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints';
44
import dotenv from 'dotenv';
55
import fs from 'fs';
66
import { NotionToMarkdown } from 'notion-to-md';
@@ -66,10 +66,10 @@ export function getPhotosFromCache(): Post[] {
6666
return [];
6767
}
6868

69-
export const fetchPublishedPosts = async (notion: Client, databaseID: string) => {
69+
export const fetchPublishedPosts = async (notion: Client, dataSourceID: string) => {
7070
// This function is now intended to be used only by the caching script.
71-
const posts = await notion.databases.query({
72-
database_id: databaseID!,
71+
const posts = await notion.dataSources.query({
72+
data_source_id: dataSourceID!,
7373
filter: {
7474
and: [
7575
{
@@ -99,7 +99,9 @@ export async function getPost(slug: string): Promise<Post | null> {
9999

100100
export async function getPostFromNotion(pageId: string): Promise<Post | null> {
101101
try {
102-
const notion = new Client({ auth: process.env.NOTION_TOKEN });
102+
const notion = new Client({
103+
auth: process.env.NOTION_TOKEN,
104+
});
103105
const n2m = new NotionToMarkdown({ notionClient: notion });
104106

105107
n2m.setCustomTransformer('image', async (block) => {

0 commit comments

Comments
 (0)