feat: supports root endpoints

This commit is contained in:
Jacob Fletcher
2022-09-22 10:43:20 -04:00
parent 686085496a
commit 52cd3b4a7e
12 changed files with 150 additions and 17 deletions

View File

@@ -86,6 +86,7 @@ Each endpoint object needs to have:
| **`path`** | A string for the endpoint route after the collection or globals slug |
| **`method`** | The lowercase HTTP verb to use: 'get', 'head', 'post', 'put', 'delete', 'connect' or 'options' |
| **`handler`** | A function or array of functions to be called with **req**, **res** and **next** arguments. [Express](https://expressjs.com/en/guide/routing.html#route-handlers) |
| **`root`** | When `true`, defines the endpoint on the root Express app, bypassing Payload handler and the `routes.api` subpath. Note: this only applies to top-level endpoints of your Payload config, endpoints defined on `collections` or `globals` cannot be root. |
Example:

View File

@@ -226,7 +226,7 @@ export type CollectionConfig = {
/**
* Custom rest api endpoints
*/
endpoints?: Endpoint[]
endpoints?: Omit<Endpoint, 'root'>[]
/**
* Access control
*/

View File

@@ -113,7 +113,7 @@ export default function registerCollections(ctx: Payload): void {
}
const endpoints = buildEndpoints(collection);
mountEndpoints(router, endpoints);
mountEndpoints(ctx.express, router, endpoints);
ctx.router.use(`/${slug}`, router);
}

View File

@@ -8,6 +8,7 @@ const component = joi.alternatives().try(
export const endpointsSchema = joi.array().items(joi.object({
path: joi.string(),
method: joi.string().valid('get', 'head', 'post', 'put', 'patch', 'delete', 'connect', 'options'),
root: joi.bool(),
handler: joi.alternatives().try(
joi.array().items(joi.func()),
joi.func(),

View File

@@ -79,16 +79,19 @@ export type AccessResult = boolean | Where;
*/
export type Access = (args?: any) => AccessResult | Promise<AccessResult>;
export interface PayloadHandler {(
export interface PayloadHandler {
(
req: PayloadRequest,
res: Response,
next: NextFunction,
): void }
): void
}
export type Endpoint = {
path: string
method: 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete' | 'connect' | 'options' | string
handler: PayloadHandler | PayloadHandler[]
root?: boolean
}
export type AdminView = React.ComponentType<{ user: User, canAccessAdmin: boolean }>

View File

@@ -1,9 +1,13 @@
import { Router } from 'express';
import { Express, Router } from 'express';
import { Endpoint } from '../config/types';
function mountEndpoints(router: Router, endpoints: Endpoint[]): void {
function mountEndpoints(express: Express, router: Router, endpoints: Endpoint[]): void {
endpoints.forEach((endpoint) => {
if (!endpoint.root) {
router[endpoint.method](endpoint.path, endpoint.handler);
} else {
express[endpoint.method](endpoint.path, endpoint.handler);
}
});
}

View File

@@ -56,7 +56,7 @@ export type GlobalConfig = {
beforeRead?: BeforeReadHook[]
afterRead?: AfterReadHook[]
}
endpoints?: Endpoint[],
endpoints?: Omit<Endpoint, 'root'>[],
access?: {
read?: Access;
readDrafts?: Access;

View File

@@ -48,7 +48,7 @@ export default function initGlobals(ctx: Payload): void {
const { slug } = global;
const endpoints = buildEndpoints(global);
mountEndpoints(router, endpoints);
mountEndpoints(ctx.express, router, endpoints);
ctx.router.use(`/globals/${slug}`, router);
});

View File

@@ -106,7 +106,7 @@ export const init = (payload: Payload, options: InitOptions): void => {
initGraphQLPlayground(payload);
}
mountEndpoints(payload.router, payload.config.endpoints);
mountEndpoints(options.express, payload.router, payload.config.endpoints);
// Bind router to API
payload.express.use(payload.config.routes.api, payload.router);

View File

@@ -16,14 +16,19 @@ export interface Global {
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts".
* via the `definition` "group-globals-one".
*/
export interface Post {
export interface GroupGlobalsOne {
id: string;
title?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group-globals-two".
*/
export interface GroupGlobalsTwo {
id: string;
title?: string;
description?: string;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@@ -39,3 +44,55 @@ export interface User {
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts".
*/
export interface Post {
id: string;
title?: string;
description?: string;
number?: number;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group-one-collection-ones".
*/
export interface GroupOneCollectionOne {
id: string;
title?: string;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group-one-collection-twos".
*/
export interface GroupOneCollectionTwo {
id: string;
title?: string;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group-two-collection-ones".
*/
export interface GroupTwoCollectionOne {
id: string;
title?: string;
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "group-two-collection-twos".
*/
export interface GroupTwoCollectionTwo {
id: string;
title?: string;
createdAt: string;
updatedAt: string;
}

View File

@@ -3,14 +3,16 @@ import { devUser } from '../credentials';
import { buildConfig } from '../buildConfig';
import { openAccess } from '../helpers/configHelpers';
import { PayloadRequest } from '../../src/express/types';
import { Config } from '../../src/config/types';
export const collectionSlug = 'endpoints';
export const globalSlug = 'global-endpoints';
export const globalEndpoint = 'global';
export const applicationEndpoint = 'path';
export const rootEndpoint = 'root';
export default buildConfig({
const MyConfig: Config = {
collections: [
{
slug: collectionSlug,
@@ -77,6 +79,21 @@ export default buildConfig({
res.json(req.body);
},
},
{
path: `/${applicationEndpoint}`,
method: 'get',
handler: (req: PayloadRequest, res: Response): void => {
res.json({ message: 'Hello, world!' });
},
},
{
path: `/${rootEndpoint}`,
method: 'get',
root: true,
handler: (req: PayloadRequest, res: Response): void => {
res.json({ message: 'Root.' });
},
},
],
onInit: async (payload) => {
await payload.create({
@@ -87,4 +104,6 @@ export default buildConfig({
},
});
},
});
}
export default buildConfig(MyConfig);

View File

@@ -16,6 +16,10 @@ export interface ArrayField {
text: string;
id?: string;
}[];
collapsedArray: {
text: string;
id?: string;
}[];
localized: {
text: string;
id?: string;
@@ -80,6 +84,49 @@ export interface BlockField {
blockType: 'tabs';
}
)[];
collapsedByDefaultBlocks: (
| {
text: string;
richText?: {
[k: string]: unknown;
}[];
id?: string;
blockName?: string;
blockType: 'text';
}
| {
number: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
subBlocks: (
| {
text: string;
id?: string;
blockName?: string;
blockType: 'text';
}
| {
number: number;
id?: string;
blockName?: string;
blockType: 'number';
}
)[];
id?: string;
blockName?: string;
blockType: 'subBlocks';
}
| {
textInCollapsible?: string;
textInRow?: string;
id?: string;
blockName?: string;
blockType: 'tabs';
}
)[];
localizedBlocks: (
| {
text: string;
@@ -153,6 +200,7 @@ export interface CollapsibleField {
textWithinSubGroup?: string;
};
};
someText?: string;
createdAt: string;
updatedAt: string;
}