書き込みが保存される JSONPlaceholder の代わり

同じ種類のリソースで、書き込みは保存され、不正な入力は拒否され、エラーも好きなときに起こせます。

例を API に対して最後に実行した日:

JSONPlaceholder の書き込みは見せかけです。POST は id: 101 を返しますが、その id に GET すると 404 が返り、DELETE してもレコードは残ります。アプリが書き込んだ内容を読み直す必要があるなら、https://api.testingapis.com/apiサインインして表示 を使ってください。同じ種類のリソース(投稿、コメント、アルバム、写真、ToDo、 ユーザー)があり、作成・更新・削除は保存されます。不正な入力は 400 で拒否し、どのリクエストも好きなときに失敗させられる ので、エラー状態も作ってテストできます。無料で、キーも不要、どのオリジンからのリクエストも受け付けます。

保存される書き込み

新しい投稿に対して JSONPlaceholder がすることはこうです。

Terminal
$ curl -X POST https://jsonplaceholder.typicode.com/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"hello","body":"world","userId":1}'
{ "title": "hello", "body": "world", "userId": 1, "id": 101 }

$ curl -i https://jsonplaceholder.typicode.com/posts/101
HTTP/1.1 404 Not Found

こちらで同じ手順を踏みます。投稿を作り、読み直し、編集し、削除し、消えたことを確かめます。

Terminal
$ curl -X POST https://api.testingapis.com/api/Posts \
  -H "Content-Type: application/json" \
  -d '{"userId":101,"title":"Hello from the guide","body":"Checking that writes persist."}'
{"message":"Post created successfully","data":{"id":16,"userId":101,"title":"Hello from the guide","body":"Checking that writes persist.","tags":[],"publishedAt":"2026-10-07T02:58:58Z","likes":0}}

$ curl https://api.testingapis.com/api/Posts/16
{"id":16,"userId":101,"title":"Hello from the guide","body":"Checking that writes persist.","tags":[],"publishedAt":"2026-10-07T02:58:58Z","likes":0}

$ curl -X PUT https://api.testingapis.com/api/Posts/16 \
  -H "Content-Type: application/json" \
  -d '{"userId":101,"title":"Edited title","body":"Checking that writes persist."}'
{"message":"Post updated successfully","data":{"id":16,"userId":101,"title":"Edited title",...}}

$ curl -X DELETE https://api.testingapis.com/api/Posts/16
{"message":"Post deleted successfully"}

$ curl -i https://api.testingapis.com/api/Posts/16
HTTP/1.1 404 Not Found

{"status":404,"error":"Not Found","message":"Post with ID 16 does not exist."}

ルートの対応表

JSONPlaceholdertestingapis.comレコード
/posts/api/posts15(JSONPlaceholder:100)
/posts/1/comments/api/posts/1/comments投稿 1 へのコメント
/comments/api/commentsdocs50(500)
/albums/api/albumsdocs10(100)
/albums/1/photos/api/albums/1/photosサインインして表示アルバムごとに 5 枚
/photos/api/photosdocs50(5,000)
/todos/api/todos30(200)
/users/api/peopledocs20(10)。住所、緯度経度、会社付き
/users/1/posts/api/posts/user/101ユーザー id は 101 から

パスは大文字と小文字を区別しません。/api/posts と /api/Posts は同じです。レコード数は JSONPlaceholder より少ないものの、それぞれのフィールドは多くなっています。投稿にはタグ、いいね、公開日があり、ToDo には優先度と期限、 人物にはネストした住所と会社があります。API ドキュメントには、商品やカートから書籍、映画、国まで、さらに 13 種類のリソースが載っています。

GET/api/posts/1

不正な入力にはエラーが返る

JSONPlaceholder はどんなボディでも、空でも受け付けます。こちらでは必須フィールドが欠けていると、メッセージ付きの 400 が返るので、フォームのエラー表示に本物の内容を出せます。

Terminal
$ curl -i -X POST https://jsonplaceholder.typicode.com/posts -H "Content-Type: application/json" -d '{}'
HTTP/1.1 201 Created

{ "id": 101 }

$ curl -i -X POST https://api.testingapis.com/api/Posts -H "Content-Type: application/json" -d '{"title":""}'
HTTP/1.1 400 Bad Request

{"status":400,"error":"Bad Request","message":"Missing required field: title"}

好きなときにエラーと遅いレスポンス

どのリクエストにも ?error=500 を付ければそのステータスで失敗し、?delay=3 を付ければ 3 秒遅れて応答します。?delay=2&error=503 と組み合わせれば、読み込み中の表示の後にエラーが出ます。エラーレスポンスのガイドでは、タイムアウト、リトライ、レート制限を扱っています。

GET/api/todos/1?error=503

絞り込み、並べ替え、ページング

どちらの API も ?userId=101 のようにフィールドで一覧を絞り込め、並べ替え、検索、ページングもできます。違うのは パラメータ名です。

JSONPlaceholdertestingapis.com
絞り込み?userId=1?userId=101
ページ?_page=2&_limit=10?page=2&limit=10
並べ替え?_sort=id&_order=desc?sort=id&order=desc
検索?q=word?q=word
総数X-Total-CountX-Total-Count、X-Total-Pages

アプリを切り替える

読み取りは JSONPlaceholder と同じくレコードをそのまま返します。書き込みは { message, data } のようにメッセージで包み、ユーザー id は 101 から始まります。ほとんどのアプリに必要な変更はこの 2 つです。

JavaScript
// Before
const BASE = "https://jsonplaceholder.typicode.com";
const post = await (await fetch(`${BASE}/posts/1`)).json();

const created = await (await fetch(`${BASE}/posts`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ userId: 1, title: "Hi", body: "There" }),
})).json();

// After
const BASE = "https://api.testingapis.com/api";
const post = await (await fetch(`${BASE}/posts/1`)).json();

const { data: created } = await (await fetch(`${BASE}/posts`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ userId: 101, title: "Hi", body: "There" }),
})).json();
この例はサインインしたメンバー向けのエンドポイントを使います。サインイン無料アカウントを作成

JSONPlaceholder のほうがよい場合

  • 仮想スクロールをテストするための 5,000 枚の写真のような、大きな一覧が必要なとき。
  • JSONPlaceholder の id やフィールドをそのまま使うチュートリアルに沿っているとき。
  • データを読むだけで、書き込みが後で反映される必要がないとき。

さらに進むなら、CRUD テストのレッスンで、作成・更新・削除が本当に行われたかを確かめる方法を説明しています。