この記事の要点(TL;DR)
- 記事を自動で投稿する小さなスクリプトを Node.js で作りました。
- 最初は「投稿したかどうか」をローカルのファイルに記録して二重投稿を防ごうとしたのですが、これは意外とすぐ壊れます。
- 結論:「投稿済みかどうか」の判断は、投稿先(今回はQiita API)の状態を正として確認するのがいちばん安全でした。
- キーワードは 冪等性(べきとうせい)。「同じ操作を2回やっても、結果が1回ぶんにしかならない」性質のことです。
まず用語を軽く補足します。APIは「プログラムから外部サービスを操作するための窓口」のこと。ここではQiitaが用意している「記事を投稿する」「自分の投稿一覧を取る」といった窓口を使います。
何をしたかったか
個人開発で、書きためた記事を1日1本、自動で投稿する仕組みを作っていました。やることはシンプルで、
- 記事ファイルを読む
- Qiitaに投稿する
- 投稿できたらURLを記録する
だけです。ところが、この「自動」というのが曲者でした。
最初の失敗:ローカルファイルで「投稿済み」を管理した
最初はこう考えました。
投稿したら
posted.jsonみたいなファイルに「この記事はもう出した」と書いておく。次に実行するときはそれを見て、投稿済みならスキップすればいい。
一見よさそうですが、これは次のようなときに簡単に事故ります。
- 投稿はできたのに、記録を書く前にプログラムが落ちた → 記録には「未投稿」と残る → 次に実行するともう一度投稿してしまう(=二重投稿)
- 別のPCやアカウントで動かしたら、ローカルの記録ファイルが無くてまた投稿してしまう
つまり「自分のローカルの記録」と「実際にQiitaにある記事」がズレるとアウトなのに、この方式は簡単にズレてしまうのです。
直し方:投稿先(API)の状態を「正」にする
そこで発想を変えました。「もう投稿したか?」は、自分のメモではなく、投稿先に聞けばいいのです。
Qiitaには「自分の投稿一覧を取得する」APIがあります。投稿する前にまずこれを叩いて、
- 同じタイトルの記事がすでにあるか? → あるなら中止
- 今日すでに何か投稿していないか?(1日1本ルール)→ しているなら中止
をチェックします。こうすると、たとえ前回スクリプトが途中で落ちていても、実際にQiitaに記事がある以上「投稿済み」と正しく判断できるので、二重投稿になりません。
実際のコードはこんな感じです(要点だけ)。
// 1. 投稿する前に、自分の投稿一覧をAPIから取得する
const res = await fetch(
"https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=20",
{ headers }
);
const items = await res.json();
// 2. 同じタイトルが既にあれば中止
if (items.find(it => it.title === title)) {
console.error("SKIP: 同じタイトルの記事が投稿済み");
process.exit(2);
}
// 3. 今日すでに投稿していれば中止(1日1本)
const today = new Date().toLocaleDateString("sv-SE", { timeZone: "Asia/Tokyo" });
if (items.find(it => it.created_at.startsWith(today))) {
console.error("SKIP: 今日はすでに投稿済み");
process.exit(2);
}
// ここまで来たら、はじめて投稿する
ポイントは「ローカルに何も覚えさせない」ことです。判断材料をすべてAPI(=本当の状態)から取ってくるので、記録ファイルの管理ミスやクラッシュに影響されません。
もうひとつの工夫:投稿の直後に記録を更新する
それでも「投稿した瞬間にクラッシュしたら?」という不安は残ります。ここは2段構えにしました。
- 投稿が成功したら、すぐに記事ファイルの状態を
postedに書き換える(クラッシュしうる時間を最短にする) - 万が一その書き換え前に落ちても、次回はAPIチェックで「同じタイトルあり」と弾かれるので結局二重投稿にならない
「メインの安全装置(APIチェック)」+「補助のメモ更新」という順番です。メモはあくまで補助で、正解は常にAPI側にある、という設計にしておくと安心でした。
学び:冪等性は「記録」より「事実」で担保する
今回いちばんの学びは、二重実行を防ぎたいときは、自分のローカルの記録に頼らず、操作対象の“事実”を確認しにいくということでした。
- ローカルの記録は、ズレる・消える・壊れる
- 投稿先の状態は、いつ確認しても本当のこと
自動化スクリプトは「途中で落ちる」「二重で走る」が普通に起きます。そのときに壊れないかどうかは、「何を正としてチェックしているか」で決まるんだなと実感しました。個人開発で小さな自動化を作るときの参考になれば嬉しいです。