Skip to content

Instantly share code, notes, and snippets.

@r1tsuu
Last active April 23, 2024 11:06
Show Gist options
  • Save r1tsuu/b9b86d72027a7d152ddb763ac50e8032 to your computer and use it in GitHub Desktop.
Save r1tsuu/b9b86d72027a7d152ddb763ac50e8032 to your computer and use it in GitHub Desktop.
Payload 3.0 REST client with generated types
/* eslint-disable require-await */
import type { BulkOperationResult } from 'node_modules/payload/dist/collections/config/types';
import type { PaginatedDocs } from 'payload/database';
import type { Where } from 'payload/types';
import type { Config } from 'payload-types';
import type { DeepPartial } from 'ts-essentials';
import type { APIBuilderArgs } from './types';
import { buildQueryString } from './buildQueryString';
import { headersBuilder } from './headersBuilder';
export const createPayloadApi = ({ baseURL, getHeaders }: APIBuilderArgs) => {
const headers = headersBuilder({ getHeaders });
return {
// eslint-disable-next-line perfectionist/sort-objects
create: async <T extends keyof Config['collections']>({
collection,
data,
depth,
fallbackLocale,
locale,
}: {
collection: T;
data: Config['collections'][T];
depth?: number;
fallbackLocale?: string;
locale?: string;
}): Promise<PaginatedDocs<Config['collections'][T]>> => {
const query = buildQueryString({ depth, fallbackLocale, locale });
return fetch(`${baseURL}/${collection}${query}`, {
body: JSON.stringify(data),
headers: headers({ contentType: 'application/json' }),
method: 'POST',
}).then((response) => response.json());
},
delete: async <T extends keyof Config['collections']>({
collection,
depth,
fallbackLocale,
locale,
where,
}: {
collection: T;
depth?: number;
fallbackLocale?: string;
locale?: string;
where: Where;
}): Promise<BulkOperationResult<T>> => {
const query = buildQueryString({ depth, fallbackLocale, locale, where });
return fetch(`${baseURL}/${collection}${query}`, {
headers: headers(),
method: 'DELETE',
}).then((response) => response.json());
},
deleteById: async <T extends keyof Config['collections']>({
collection,
id,
}: {
collection: T;
id: number;
}): Promise<Config['collections'][T]> => {
return fetch(`${baseURL}/${collection}/${id}`, {
headers: headers(),
method: 'DELETE',
}).then((response) => response.json());
},
find: async <T extends keyof Config['collections']>({
collection,
depth,
fallbackLocale,
limit,
locale,
page,
sort,
where,
}: {
collection: T;
depth?: number;
fallbackLocale?: string;
limit?: number;
locale?: string;
page?: number;
// eslint-disable-next-line perfectionist/sort-union-types
// @ts-expect-error err
// eslint-disable-next-line perfectionist/sort-union-types
sort?: keyof Config['collections'][T] | `-${keyof Config['collections'][T]}`;
where?: Where;
}): Promise<PaginatedDocs<Config['collections'][T]>> => {
const query = buildQueryString({
depth,
fallbackLocale,
limit,
locale,
page,
sort,
where,
});
return fetch(`${baseURL}/${collection}${query}`, {
headers: headers(),
}).then((response) => response.json());
},
findById: async <T extends keyof Config['collections']>({
collection,
depth,
fallbackLocale,
id,
locale,
}: {
collection: T;
depth?: number;
fallbackLocale?: string;
id: number;
locale?: string;
}): Promise<Config['collections'][T]> => {
const query = buildQueryString({ depth, fallbackLocale, locale });
return fetch(`${baseURL}/${collection}/${id}${query}`, {
headers: headers(),
}).then((response) => response.json());
},
findGlobal: async <T extends keyof Config['globals']>({
depth,
fallbackLocale,
global,
locale,
}: {
depth?: number;
fallbackLocale?: string;
global: T;
locale?: string;
}): Promise<Config['globals'][T]> => {
const query = buildQueryString({ depth, fallbackLocale, locale });
return fetch(`${baseURL}/globals/${global}${query}`).then((response) => response.json());
},
update: async <T extends keyof Config['collections']>({
collection,
data,
depth,
fallbackLocale,
locale,
where,
}: {
collection: T;
data: DeepPartial<Config['collections'][T]>;
depth?: number;
fallbackLocale?: string;
locale?: string;
where: Where;
}): Promise<BulkOperationResult<T>> => {
const query = buildQueryString({ depth, fallbackLocale, locale, where });
return fetch(`${baseURL}/${collection}${query}`, {
body: JSON.stringify(data),
headers: headers({ contentType: 'application/json' }),
method: 'PATCH',
}).then((response) => response.json());
},
updateById: async <T extends keyof Config['collections']>({
collection,
data,
depth,
fallbackLocale,
id,
locale,
}: {
collection: T;
data: DeepPartial<Config['collections'][T]>;
depth?: number;
fallbackLocale?: string;
id: number;
locale?: string;
}): Promise<Config['collections'][T]> => {
const query = buildQueryString({ depth, fallbackLocale, locale });
return fetch(`${baseURL}/${collection}/${id}${query}`, {
body: JSON.stringify(data),
headers: headers({ contentType: 'application/json' }),
method: 'PATCH',
}).then((response) => response.json());
},
updateGlobal: async <T extends keyof Config['globals']>({
data,
depth,
fallbackLocale,
global,
locale,
}: {
data: DeepPartial<Config['globals'][T]>;
depth?: number;
fallbackLocale?: string;
global: T;
locale?: string;
}): Promise<Config['globals'][T]> => {
const query = buildQueryString({ depth, fallbackLocale, locale });
return fetch(`${baseURL}/globals/${global}${query}`, {
body: JSON.stringify(data),
headers: headers({ contentType: 'application/json' }),
method: 'PATCH',
}).then((response) => response.json());
},
};
};
import qs from 'qs';
export const buildQueryString = (args: Record<string, unknown> | undefined) => {
if (!args) return '';
if (args['fallbackLocale']) {
args['fallback-locale'] = args['fallbackLocale'];
delete args['fallbackLocale'];
}
return qs.stringify(args, { addQueryPrefix: true });
};
import type { APIBuilderArgs } from './types';
const args: APIBuilderArgs = {
baseURL: `/api`,
};
export const api = createPayloadApi(args)
import { headers } from 'next/headers';
import type { APIBuilderArgs } from './types';
const args: APIBuilderArgs = {
baseURL: `${process.env.NEXT_PUBLIC_URL}/api`,
// if we want to include user's credentials in the requests.
getHeaders: headers,
};
export const api = createPayloadApi(args)
import type { ReadonlyHeaders } from 'next/dist/server/web/spec-extension/adapters/headers';
export const headersBuilder =
({ getHeaders }: { getHeaders?: () => ReadonlyHeaders }) =>
({ contentType }: { contentType?: 'application/json' } = {}) => {
const headers = getHeaders ? new Headers(getHeaders()) : new Headers();
if (contentType) headers.set('Content-Type', contentType);
return headers;
};
import type { ReadonlyHeaders } from 'next/dist/server/web/spec-extension/adapters/headers';
export type APIBuilderArgs = {
baseURL: string;
getHeaders?: () => ReadonlyHeaders;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment