Home / Blog / API & webhooks

API & webhooks

Sample REST API Call: Submit One JSON Resource Safely

Construct one JSON POST request, distinguish creation from acceptance, validate the returned resource, and avoid accidental duplicate writes when retrying.

Choose a specific write operation

A sample REST API call is most useful when it describes one complete operation. Here the fictional endpoint POST /notes creates a note from a title and returns a created resource. The example assumes a service you control for testing, with synthetic note content and no real credentials.

Before sending a write to any other service, confirm that you are using its intended test environment and documented body shape. A successful connection is not authorization to create records, send notifications or modify someone else's account.

Serialize the request and require the promised response

async function createExampleNote(url, title, fetcher = fetch) {
  if (typeof title !== "string" || !title.trim()) {
    throw new Error("A nonempty title is required.");
  }
  const response = await fetcher(url, {
    method: "POST",
    headers: {"Content-Type": "application/json", Accept: "application/json"},
    body: JSON.stringify({title})
  });
  if (response.status !== 201) {
    throw new Error(`Expected creation, received HTTP ${response.status}`);
  }
  const note = await response.json();
  if (!note || Array.isArray(note) || typeof note.id !== "string" ||
      !note.id || note.title !== title) {
    throw new Error("Unexpected created-note representation.");
  }
  return note;
}

The helper requires 201 because that is the illustrative endpoint's stated contract. It verifies a nonempty string identifier and the submitted title. Another endpoint can legitimately return a different status or representation; change the client based on that endpoint's documentation rather than accepting any response indiscriminately.

JSON.stringify performs JSON serialization. It preserves a title containing quotation marks or Unicode when the service parses the body correctly. The Content-Type header describes those bytes; it does not perform serialization by itself.

Distinguish created work from accepted work

The POST method documentation describes processing submitted content according to the resource's semantics. Creation is one common use, but the method alone does not guarantee that every endpoint creates a resource synchronously.

A 202 response can indicate accepted work whose processing has not completed. If the service uses an asynchronous job model, the client needs its documented job identifier, status endpoint and terminal states. Treating 202 as an immediate created note would hide that distinction.

A 204 response has no body to parse. If the contract uses it, the client must handle that branch explicitly. Calling response.json on every non-error response creates an avoidable failure even when the server behaved correctly.

Do not retry an uncertain write blindly

A connection can fail after the server has committed a change but before the client receives the result. The rejected promise does not prove that no note was created. Decide how the application reconciles this uncertainty before implementing automatic retries.

If the service offers an idempotency key, follow its documented scope, retention and body-reuse rules. A made-up Idempotency-Key header has no effect unless the server implements that contract. The helper above deliberately performs one attempt and does not claim duplicate-write protection.

A malformed body after a 201 is especially important: creation may already have succeeded even though this helper rejects the representation. Surface that uncertainty for reconciliation. Do not automatically rerun the POST merely because the returned identifier could not be parsed or validated.

Tie the visible result to the current submission

Disable or otherwise coordinate repeated submissions while an accepted request is running. If the user edits the title, distinguish the pending request's original title from the new draft. Do not display an old created identifier as the result of a title that was never submitted.

Render returned text safely and retain only the diagnostics needed for recovery. A response body can contain untrusted or private content; a debugging result should not become executable HTML or an unrestricted analytics event.

Verify the exact submitted values and failure behavior

Use two different titles in a local fixture, including one with a quotation mark. Assert the POST method, JSON media type, parsed request body and distinct created identifiers. Empty and non-string titles should be rejected before the fixture receives a request.

Also test a validation error, a 202, a 204 and a mismatched returned title. The REST API Testing Tool can send an authorized write and display the readable response, but it does not supply the endpoint's idempotency policy or this example's resource-specific assertions.