diff --git a/docs/guides/extensions/creating-a-custom-api-endpoint.md b/docs/guides/extensions/creating-a-custom-api-endpoint.md index 59a7984bf0..fc1c8cf2ff 100644 --- a/docs/guides/extensions/creating-a-custom-api-endpoint.md +++ b/docs/guides/extensions/creating-a-custom-api-endpoint.md @@ -1,3 +1,43 @@ # Creating a Custom API Endpoint -> TK +Custom endpoints are dynamically loaded from your configured extensions folder. + +Custom endpoints are registered using a registration function: + +```js +// extensions/endpoints/my-endpoint/index.js + +module.exports = function registerEndpoint(router) { + router.get('/', (req, res) => res.send('Hello, World!')); +} +``` + +The `registerEndpoint` function receives two parameters: `router` and `context`. Router is an express Router +instance that's scoped to `/custom/`. `context` holds the following properties: + +* `services` — All API interal services +* `exceptions` — API exception objects that can be used to throw "proper" errors +* `database` — Knex instance that's connected to the current DB +* `env` — Parsed environment variables + +--- + +## Full example: + +```js +// extensions/endpoints/recipes/index.js + +module.exports = function registerEndpoint(router, { services, exceptions }) { + const { ItemsService } = services; + const { ServiceUnavailableException } = exceptions; + + const recipeService = new ItemsService('recipes'); + + router.get('/', (req, res) => { + recipeService + .readByQuery({ sort: 'name', fields: '*' }) + .then(results => res.json(results)) + .catch(error => { throw new ServiceUnavailableException(error.message) }); + }); +} +``` diff --git a/docs/guides/extensions/creating-a-custom-api-hook.md b/docs/guides/extensions/creating-a-custom-api-hook.md index 126800be87..28962e7049 100644 --- a/docs/guides/extensions/creating-a-custom-api-hook.md +++ b/docs/guides/extensions/creating-a-custom-api-hook.md @@ -1,3 +1,68 @@ # Creating a Custom API Hook -> TK +Custom hooks are dynamically loaded from your configured extensions folder. + +Custom hooks are registered using a registration function: + +```js +// extensions/hooks/my-hook/index.js + +module.exports = function registerHook() { + return { + 'item.create.articles': function() { + axios.post('http://example.com/webhook'); + } + } +} +``` + +Register function return an object with key = event, value = handler function. + +The `registerHook` function receives one parameter: `context`. `context` holds the following properties: + +* `services` — All API interal services +* `exceptions` — API exception objects that can be used to throw "proper" errors +* `database` — Knex instance that's connected to the current DB +* `env` — Parsed environment variables + +Each handler function gets a `context` parameter with the following properties: + +* `event` — Full event string +* `accountability` — Information about the current user +* `collection` — Collection that's being modified +* `item` — Primary key(s) of the item(s) that's being modified +* `action` — Action that's performed +* `payload` — Payload of the request + +Events that are prefixed with `.before` run before the event is completed, and are blocking. These allow you to check / modify the payload before it's processed. + +--- + +## Full example: + +```js +// extensions/hooks/sync-with-external/index.js + +module.exports = function registerHook({ services, exceptions }) { + const { ServiceUnavailableException, ForbiddenException } = exceptions; + + return { + // Force everything to be admin only at all times + 'item.*.*': async function({ item, accountability }) { + if (accountability.admin !== true) throw new ForbiddenException(); + }, + // Sync with external recipes service, cancel creation on failure + 'item.recipes.create.before': async function(input) { + try { + await axios.post('https://example.com/recipes', input); + } catch (error) { + throw new ServiceUnavailableException(error); + } + + input[0].syncedWithExample = true; + + return input; + } + } +} +```