Files
gerbeur/shared/api.ts

77 lines
2.1 KiB
TypeScript

/**
* The envelope every endpoint answers with, the error codes it carries, the
* shape of a paginated list, and {@link Wire}, which maps a type to what JSON
* makes of it.
*
* @module
*/
/** The error codes an {@link APIError} carries, by name. */
export const APIErrorCode = {
/** The request is malformed or not allowed in the current state. */
BAD_REQUEST: "BAD_REQUEST",
/** What the request names doesn't exist, or the caller may not see it. */
NOT_FOUND: "NOT_FOUND",
/** The server failed. */
SERVER_ERROR: "SERVER_ERROR",
/** A remote site took too long to answer. */
TIMEOUT: "TIMEOUT",
/** The caller isn't signed in, or may not do this. */
UNAUTHORIZED: "UNAUTHORIZED",
/** The request body failed validation. */
VALIDATION_ERROR: "VALIDATION_ERROR",
} as const;
/** One of the {@link APIErrorCode} values. */
export type APIErrorCode = typeof APIErrorCode[keyof typeof APIErrorCode];
/** What went wrong, in a failed response. */
export interface APIError {
/** The kind of failure. */
code: APIErrorCode;
/** A human-readable explanation. */
message: string;
}
/** A successful response, carrying its payload. */
export interface APISuccess<T> {
/** Always true. */
success: true;
/** The payload. */
data: T;
/** Never set on success. */
error?: never;
}
/** A failed response, carrying its error. */
export interface APIFailure {
/** Always false. */
success: false;
/** Never set on failure. */
data?: never;
/** What went wrong. */
error: APIError;
}
/** What every endpoint answers with, narrowed by `success`. */
export type APIResponse<T> = APISuccess<T> | APIFailure;
/** One page of a list. */
export interface PaginatedData<T> {
/** The page's items. */
items: T[];
/** Whether another page follows. */
hasMore: boolean;
/** The whole list's size — only where it's shown (search results). */
total?: number;
}
/**
* `T` as it arrives over JSON (HTTP, the socket, localStorage): every Date is
* an ISO string.
*/
export type Wire<T> = T extends Date ? string
: T extends readonly (infer U)[] ? Wire<U>[]
: T extends object ? { [K in keyof T]: Wire<T[K]> }
: T;