書き込みが保存される JSONPlaceholder の代わり
同じ種類のリソースで、書き込みは保存され、不正な入力は拒否され、エラーも好きなときに起こせます。
例を API に対して最後に実行した日:
JSONPlaceholder の書き込みは見せかけです。POST は id: 101 を返しますが、その id に GET すると 404 が返り、DELETE してもレコードは残ります。アプリが書き込んだ内容を読み直す必要があるなら、https://api.testingapis.com/apiサインインして表示 を使ってください。同じ種類のリソース(投稿、コメント、アルバム、写真、ToDo、 ユーザー)があり、作成・更新・削除は保存されます。不正な入力は 400 で拒否し、どのリクエストも好きなときに失敗させられる ので、エラー状態も作ってテストできます。無料で、キーも不要、どのオリジンからのリクエストも受け付けます。
保存される書き込み
新しい投稿に対して JSONPlaceholder がすることはこうです。
$ 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こちらで同じ手順を踏みます。投稿を作り、読み直し、編集し、削除し、消えたことを確かめます。
$ 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."}ルートの対応表
| JSONPlaceholder | testingapis.com | レコード |
|---|---|---|
/posts | /api/posts | 15(JSONPlaceholder:100) |
/posts/1/comments | /api/posts/1/comments | 投稿 1 へのコメント |
/comments | /api/commentsdocs | 50(500) |
/albums | /api/albumsdocs | 10(100) |
/albums/1/photos | /api/albums/1/photosサインインして表示 | アルバムごとに 5 枚 |
/photos | /api/photosdocs | 50(5,000) |
/todos | /api/todos | 30(200) |
/users | /api/peopledocs | 20(10)。住所、緯度経度、会社付き |
/users/1/posts | /api/posts/user/101 | ユーザー id は 101 から |
パスは大文字と小文字を区別しません。/api/posts と /api/Posts は同じです。レコード数は JSONPlaceholder より少ないものの、それぞれのフィールドは多くなっています。投稿にはタグ、いいね、公開日があり、ToDo には優先度と期限、 人物にはネストした住所と会社があります。API ドキュメントには、商品やカートから書籍、映画、国まで、さらに 13 種類のリソースが載っています。
/api/posts/1不正な入力にはエラーが返る
JSONPlaceholder はどんなボディでも、空でも受け付けます。こちらでは必須フィールドが欠けていると、メッセージ付きの 400 が返るので、フォームのエラー表示に本物の内容を出せます。
$ 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 と組み合わせれば、読み込み中の表示の後にエラーが出ます。エラーレスポンスのガイドでは、タイムアウト、リトライ、レート制限を扱っています。
/api/todos/1?error=503絞り込み、並べ替え、ページング
どちらの API も ?userId=101 のようにフィールドで一覧を絞り込め、並べ替え、検索、ページングもできます。違うのは パラメータ名です。
| JSONPlaceholder | testingapis.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-Count | X-Total-Count、X-Total-Pages |
アプリを切り替える
読み取りは JSONPlaceholder と同じくレコードをそのまま返します。書き込みは { message, data } のようにメッセージで包み、ユーザー id は 101 から始まります。ほとんどのアプリに必要な変更はこの 2 つです。
// 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 テストのレッスンで、作成・更新・削除が本当に行われたかを確かめる方法を説明しています。