Declaring schemas
A central part of @zimic/http
is an HTTP schema, which is a TypeScript type describing the structure of an API. The
schema serves as a centralized source of truth about paths, parameters, and returns of the service, and can be used to
automatically type your requests and responses with @zimic/fetch
and
@zimic/interceptor
.
For APIs with an OpenAPI documentation (e.g. Swagger), the
zimic-http typegen
CLI can automatically infer the types and generate the
schema for you. This is a great way to keep your schema is up to date and save time on manual type definitions.
A @zimic/http
schema is defined with the HttpSchema
type.
import { HttpSchema } from '@zimic/http';
// Declaring the base types
interface User {
name: string;
username: string;
}
interface UserCreationBody {
name: string;
username: string;
}
interface UserListSearchParams {
name?: string;
orderBy?: ('name:asc' | 'name:desc')[];
}
// Declaring the schema
type Schema = HttpSchema<{
'/users': {
POST: {
request: {
body: UserCreationBody;
};
response: {
201: { body: User };
400: { body: { message?: string; issues?: string[] } };
409: { body: { message?: string } };
};
};
GET: {
request: {
headers: { authorization: string };
searchParams: UserListSearchParams;
};
response: {
200: { body: User[] };
401: { body: { message?: string } };
500: { body: { message?: string } };
};
};
};
'/users/:id': {
GET: {
request: {
headers: { authorization: string };
};
response: {
200: { body: User };
401: { body: { message?: string } };
403: { body: { message?: string } };
404: { body: { message?: string } };
500: { body: { message?: string } };
};
};
};
}>;
This example contains three endpoints, POST /users
, GET /users
, and GET /users/:id
, each with their own request
and response types. GET /users
and GET /users/:userId
require authentication via the authorization
header.
POST /users
receive a body with some data, while GET /users
accepts search parameters to filter the results. All
three endpoints have successful and error responses, detailed with their respective status codes and body types.
Declaring paths​
At the root of the schema, you declare the paths of the API as keys. Path parameters are automatically inferred from the
path and have :
as prefix, followed by the name of the parameter.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/users': {
// ...
};
'/users/:id': {
// ...
};
'/posts': {
// ...
};
}>;
Declaring methods​
Each path can have one or more HTTP methods (GET
, POST
, PUT
, PATCH
, DELETE
, HEAD
, and OPTIONS
). The method
names are case-sensitive and must be in uppercase.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/users': {
GET: {
// ...
};
POST: {
// ...
};
};
// ...
}>;
Declaring requests​
Each method can have a request
property, which defines the shape of the requests accepted in the endpoint.
Declaring requests with search params​
Search parameters (query) are declared in the searchParams
property.
import { HttpSchema } from '@zimic/http';
interface UserListSearchParams {
query?: string;
}
type Schema = HttpSchema<{
'/users': {
GET: {
request: {
searchParams: UserListSearchParams;
};
};
};
}>;
Declaring requests with body​
Declaring requests with JSON body​
To declare a JSON body, use the type directly.
import { HttpSchema } from '@zimic/http';
interface UserCreationBody {
username: string;
}
type Schema = HttpSchema<{
'/users': {
POST: {
request: {
body: UserCreationBody;
};
};
};
}>;
Declaring requests with FormData
body​
Use HttpFormData
to declare a request with FormData
body.
import { HttpSchema, HttpFormData } from '@zimic/http';
interface FileUploadData {
files: File[];
description?: string;
}
type Schema = HttpSchema<{
'/files': {
POST: {
request: {
body: HttpFormData<FileUploadData>;
};
};
};
}>;
Declaring requests with binary body​
Blob
,
ArrayBuffer
, and
ReadableStream
are common types for binary data. Use one
of these types to declare a binary body.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/upload': {
POST: {
request: {
body: Blob;
};
};
};
}>;
Declaring requests with plain-text body​
Plain-text bodies can be declared as a string.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/content': {
POST: {
request: {
body: string;
};
};
};
}>;
Declaring requests with URL-encoded body​
Bodies with URL-encoded data can be declared with HttpSearchParams
.
import { HttpSchema, HttpSearchParams } from '@zimic/http';
interface UserCreationSearchParams {
query?: string;
}
type Schema = HttpSchema<{
'/users': {
POST: {
request: {
body: HttpSearchParams<UserCreationSearchParams>;
};
};
};
}>;
Declaring responses​
Each method can also have a response
, which defines the schema of the results returned by the server. The status codes
are used as keys.
Declaring responses with body​
Declaring responses with JSON body​
To declare a response with a JSON body, use the type directly.
interface User {
name: string;
username: string;
}
type Schema = HttpSchema<{
'/users/:id': {
GET: {
response: {
200: {
body: User;
};
404: {
body: { message?: string };
};
};
};
};
}>;
Declaring responses with FormData
body​
To declare a response with FormData
, use HttpFormData
.
import { HttpSchema, HttpFormData } from '@zimic/http';
interface FileUploadData {
files: File[];
description?: string;
}
type Schema = HttpSchema<{
'/files': {
POST: {
response: {
200: {
body: HttpFormData<FileUploadData>;
};
};
};
};
}>;
Declaring responses with binary body​
To define a response with binary data, use Blob
,
ArrayBuffer
, or
ReadableStream
, similar to how you would declare a
request with binary data.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/upload': {
POST: {
response: {
200: { body: Blob };
};
};
};
}>;
Declaring responses with plain-text body​
Plain-text bodies can be declared as a string.
import { HttpSchema } from '@zimic/http';
type Schema = HttpSchema<{
'/content': {
POST: {
response: {
200: {
body: string;
};
};
};
};
}>;
Declaring responses with URL-encoded body​
Use HttpSearchParams
to declare a response with URL-encoded data.
import { HttpSchema, HttpSearchParams } from '@zimic/http';
interface UserCreationSearchParams {
query?: string;
}
type Schema = HttpSchema<{
'/users': {
POST: {
response: {
200: {
body: HttpSearchParams<UserCreationSearchParams>;
};
};
};
};
}>;
Declaring responses with status code ranges​
Sometimes, endpoints can return a range of status codes, such as 5XX
, meaning any status greater than or equal to 500.
In these cases, you can use the HttpStatusCode
type, which contains all standard HTTP status codes:
Type | Range |
---|---|
HttpStatusCode.Information | 1XX |
HttpStatusCode.Success | 2XX |
HttpStatusCode.Redirection | 3XX |
HttpStatusCode.ClientError | 4XX |
HttpStatusCode.ServerError | 5XX |
import { HttpSchema, HttpStatusCode } from '@zimic/http';
interface User {
name: string;
username: string;
}
type Schema = HttpSchema<{
'/users': {
GET: {
response: {
200: User[];
} & {
// 4XX
[StatusCode in HttpStatusCode.ClientError]: {
body: { message?: string; code: 'client_error' };
};
} & {
// 5XX
[StatusCode in HttpStatusCode.ServerError]: {
body: { message?: string; code: 'server_error' };
};
};
};
};
}>;
In this example, GET /users
may return a successful response with a 200
status code, or any status code in the 4XX
(HttpStatusCode.ClientError
) or 5XX
(HttpStatusCode.ServerError
) ranges.