import type { Where } from 'payload/types' import type { ParsedQs } from 'qs' import QueryString from 'qs' import type { SanitizedConfig } from '../../packages/payload/src/config/types' import { GRAPHQL_POST as createGraphqlPOST } from '../../packages/next/src/routes/graphql' import { DELETE as createDELETE, GET as createGET, PATCH as createPATCH, POST as createPOST, } from '../../packages/next/src/routes/rest' import { devUser } from '../credentials' type ValidPath = `/${string}` type RequestOptions = { auth?: boolean file?: boolean query?: { depth?: number fallbackLocale?: string limit?: number locale?: string page?: number sort?: string where?: Where } } function generateQueryString(query: RequestOptions['query'], params: ParsedQs): string { return QueryString.stringify( { ...(params || {}), ...(query || {}), }, { addQueryPrefix: true, }, ) } export class NextRESTClient { private _DELETE: (request: Request, args: { params: { slug: string[] } }) => Promise private _GET: (request: Request, args: { params: { slug: string[] } }) => Promise private _GRAPHQL_POST: (request: Request) => Promise private _PATCH: (request: Request, args: { params: { slug: string[] } }) => Promise private _POST: (request: Request, args: { params: { slug: string[] } }) => Promise private readonly config: SanitizedConfig private token: string serverURL: string = 'http://localhost:3000' constructor(config: SanitizedConfig) { this.config = config if (config?.serverURL) this.serverURL = config.serverURL this._GET = createGET(config) this._POST = createPOST(config) this._DELETE = createDELETE(config) this._PATCH = createPATCH(config) this._GRAPHQL_POST = createGraphqlPOST(config) } private buildHeaders(options: RequestInit & RequestOptions): Headers { const defaultHeaders = { 'Content-Type': 'application/json', } const headers = new Headers({ ...(options?.file ? {} : defaultHeaders), ...(options?.headers || {}), }) if (options.auth !== false && this.token) { headers.set('Authorization', `JWT ${this.token}`) } return headers } private generateRequestParts(path: ValidPath): { params?: ParsedQs slug: string[] url: string } { const [slugs, params] = path.slice(1).split('?') const url = `${this.serverURL}${this.config.routes.api}/${slugs}` return { url, slug: slugs.split('/'), params: params ? QueryString.parse(params) : undefined, } } async DELETE(path: ValidPath, options: RequestInit & RequestOptions = {}): Promise { const { url, slug, params } = this.generateRequestParts(path) const { query, ...rest } = options || {} const queryParams = generateQueryString(query, params) const request = new Request(`${url}${queryParams}`, { ...rest, method: 'DELETE', headers: this.buildHeaders(options), }) return this._DELETE(request, { params: { slug } }) } async GET( path: ValidPath, options: Omit & RequestOptions = {}, ): Promise { const { url, slug, params } = this.generateRequestParts(path) const { query, ...rest } = options || {} const queryParams = generateQueryString(query, params) const request = new Request(`${url}${queryParams}`, { ...rest, method: 'GET', headers: this.buildHeaders(options), }) return this._GET(request, { params: { slug } }) } async GRAPHQL_POST(options: RequestInit & RequestOptions): Promise { const { query, ...rest } = options const queryParams = generateQueryString(query, {}) const request = new Request( `${this.serverURL}${this.config.routes.api}${this.config.routes.graphQL}${queryParams}`, { ...rest, method: 'POST', headers: this.buildHeaders(options), }, ) return this._GRAPHQL_POST(request) } async PATCH(path: ValidPath, options: RequestInit & RequestOptions): Promise { const { url, slug, params } = this.generateRequestParts(path) const { query, ...rest } = options const queryParams = generateQueryString(query, params) const request = new Request(`${url}${queryParams}`, { ...rest, method: 'PATCH', headers: this.buildHeaders(options), }) return this._PATCH(request, { params: { slug } }) } async POST(path: ValidPath, options: RequestInit & { file?: boolean } = {}): Promise { const { url, slug, params } = this.generateRequestParts(path) const queryParams = generateQueryString({}, params) const request = new Request(`${url}${queryParams}`, { ...options, method: 'POST', headers: this.buildHeaders(options), }) return this._POST(request, { params: { slug } }) } async login({ slug, credentials, }: { credentials?: { email: string password: string } slug: string }): Promise { this.token = await this.POST(`/${slug}/login`, { body: JSON.stringify( credentials ? { ...credentials } : { email: devUser.email, password: devUser.password }, ), }) .then((res) => res.json()) .then((data) => data.token) return this.token } }