はじめに
記事を書いたとき、同じ内容を3つのサービスに手動で投稿するのって地味にしんどい。
ポートフォリオサイト、Qiita、Zenn。それぞれエディタが違って、タグの付け方も違って、毎回コピペしてちょっと整形して……という作業が積み重なっていた。
あと正直に言うと、最近の開発ってAIの使用制限があるじゃないですか。なんかソシャゲのスタミナみたいで、使い切らないともったいない気がして。CopilotもClaude Codeも上限まで使いたい。
そういう動機が重なって、Notionをデータソースにした記事自動投稿パイプラインを作った。
全体構成
Notion DB(記事を書く・管理する)
├─ Next.js ポートフォリオサイト(Notion API を直叩き)
├─ Qiita(GitHub Actions 定期実行 → 公式 API で投稿)
└─ Zenn(GitHub Actions 定期実行 → 別リポジトリに push)
ポイントは 「Notionが唯一の記事ソース」 という設計。記事はNotionだけで書けばいい。
Notionのプロパティ設計
Notion DBのプロパティで「どこに投稿するか」を制御している。
記事ごとにチェックを入れるだけで、投稿先を柔軟に変えられる。
ポートフォリオサイト(Next.js)
Next.jsからはNotion APIを直叩きしてSSG/ISRで記事を表示している。statusが公開になっているページだけ取得する構成。
NotionのリッチテキストブロックはAPIのレスポンスをそのままMarkdownに変換して描画している。
Qiita への自動投稿
GitHub Actionsの定期実行(cron) でスクリプトを動かし、Qiita APIで投稿している。
on:
schedule:
- cron: '0 0 * * *' # 毎日0時に実行
publish_qiita にチェックが入っている記事だけを取得 → NotionブロックをMarkdownに変換 → Qiita APIで新規作成 or 更新。
注意:Too many request エラー
Qiita APIはレートリミットがある。複数記事を一気に投稿しようとするとエラーになるので、記事数が増えてきたら処理間隔を入れるか、差分のみを処理する設計にする必要がある。
Zenn への自動投稿
ZennはAPIが公開されていないため、zenn-cli + 専用リポジトリを使う方式。
- Zenn記事用のGitHubリポジトリを別途作成
- GitHub ActionsでNotionから記事を取得 → Markdownファイルを生成
- PAT(Personal Access Token) を使って、そのリポジトリにpush
- ZennがGitHubリポジトリと連携しているので自動反映される
your-name/zenn-articles ← このリポジトリへActionsがpushする
一番ハマったところ:Notion画像URLの期限切れ
Notionに貼った画像のURLは一定時間で期限切れになる。
最初はURLをそのままMarkdownに埋め込んでいたが、しばらく経つと画像が表示されなくなる問題が発生した。
対処法:リクエスト時に都度取得
Notionブロックを変換する際に、画像ブロックだけはAPIを都度叩いて最新のURLを取得するように変更した。毎回取得するので少し重いが、確実に画像が表示されるのでこれで落ち着いている。
AI駆動の実装
コーディング部分はほぼ GitHub Copilot と Claude Code に任せた。
API周りの実装も「こういうことやりたい」と伝えれば大体書いてくれる。詰まったのはAIが解決できない部分、つまりNotion画像URLの仕様やQiitaのレートリミットといった「動かしてみないとわからない」系の問題だった。
ロジックを書く時間より、仕様調査と動作確認に時間がかかるというのが最近の開発の実感。
まとめ
「3サイトに別々に投稿するの面倒」という課題はちゃんと解決できた。
ただ、同じ仕組みをすでに作っている人は絶対いると思うので、より洗練された実装を探すなら他の記事も参考にしてほしい。自分の場合は「Notionで管理したい」「ポートフォリオと一緒に整備したい」という個人的な文脈があったので、自作する価値があった。
参考
この記事は norinori1 ポートフォリオ から自動投稿されました