HTTP Method Semantics: Safe, Idempotent, Cacheable
Classify HTTP methods as safe, idempotent and cacheable by default, and decide whether a failed request may be retried.
Code
export type HttpMethod =
| "GET" | "HEAD" | "OPTIONS" | "TRACE"
| "POST" | "PUT" | "PATCH" | "DELETE" | "CONNECT";
export interface MethodSemantics {
method: HttpMethod;
safe: boolean;
idempotent: boolean;
cacheableByDefault: boolean;
}
const SAFE: ReadonlySet<HttpMethod> = new Set(["GET", "HEAD", "OPTIONS", "TRACE"]);
const IDEMPOTENT: ReadonlySet<HttpMethod> = new Set([
"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE",
]);
const CACHEABLE: ReadonlySet<HttpMethod> = new Set(["GET", "HEAD"]);
const KNOWN: ReadonlySet<HttpMethod> = new Set([
"GET", "HEAD", "OPTIONS", "TRACE", "POST", "PUT", "PATCH", "DELETE", "CONNECT",
]);
export function methodSemantics(method: string): MethodSemantics {
if (typeof method !== "string" || method.length === 0) {
throw new TypeError("method must be a non-empty string");
}
const normalized = method.toUpperCase() as HttpMethod;
if (!KNOWN.has(normalized)) throw new RangeError("unknown HTTP method: " + method);
return {
method: normalized,
safe: SAFE.has(normalized),
idempotent: IDEMPOTENT.has(normalized),
cacheableByDefault: CACHEABLE.has(normalized),
};
}
export function isRetryable(method: string, status: number): boolean {
if (!Number.isInteger(status) || status < 100 || status > 599) {
throw new RangeError("status must be a valid HTTP status code");
}
const semantics = methodSemantics(method);
return status >= 500 && (semantics.idempotent || status === 503);
}Report a problem with this exampleOpens a pre-filled issue on GitHub.