Files
directus/docs/extensions/endpoints.md
Rijk van Zanten ca3e7f521f Upgrade previous "Extensions" system to new "Errors" model (#18797)
* Typecheck across packages that are built with esbuild

* Boilerplate new Errors package

* No need, tsup checks with --dts

* Switch to tsup

* Setup dev script

* Add readme

* More boilerplaty things

* Finish createError function

* Install @directus/random

* Downgrade node types

* Add utility function to check if an error is a DirectusError

* Use new is-error check

* Install errors package

* Add failed validation common error

* Export common errors

* Move joi convertion to utils

* Export failed validation

* Use new failed validation error in validate-batch

* Enhance typing output of createError

* Remove outdir (handled by tsup now)

* Replace Exception with Error

* Replace exception in test

* Remove exceptions from app

* Remove exceptions from app

* Remove failed validation exception from users service

* Remove old failed validation exception from shared

* Remove exceptions package in favor of errors

* Uninstall exceptions

* Replace baseexception check

* Migrate content too large error

* Critical detail

* Replace ForbiddenException

* WIP remove exceptions

* Add ForbiddenError to errors

* HitRateLimitError

* Move validation related error/helper to new validation package

* Add index

* Add docs

* Install random

* Convert TokenExpired

* Convert user-suspended

* Convert invalid-credentials

* Move UnsupportedMediaType

* Replace wrong imports for forbidden

* Convert invalid-ip

* Move invalid provider

* Move InvalidOtp

* Convert InvalidToken

* Move MethodNotAllowed

* Convert range not satisfiable

* Move unexpect response

* Move UnprocessableContent

* Move IllegalAssetTransformation

* Move RouteNotFound

* Finalize not found

* Various db errors

* Move value too long

* Move not null

* Move record-not-unique

* Move value out of range

* Finish db errors

* Service unavailable

* GQL errors

* Update packages/validation/src/errors/failed-validation.ts

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* Update packages/validation/src/errors/failed-validation.ts

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* InvalidQuery

* Add test for invalid query message constructor

* Invalid Payload

* Finalize exceptions move

* Improve type of isDirectusError

* Various fixes

* Fix build in api

* Update websocket exceptions use

* Allow optional reason for invalid config

* Update errors usage in utils

* Remove unused package from errors

* Update lockfile

* Update api/src/auth/drivers/ldap.ts

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* Update packages/validation/src/utils/joi-to-error-extensions.ts

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* Put error codes in shared enum

* Replace instanceof checks in api

* Fix tests I think

* Tweak override names

* Fix linter warnings

* Set snapshots

* Start fixing BB tests

* Fix blackbox tests

* Add changeset

* Update changeset

* Update extension docs to use new createError abstraction

* 🙄

* Fix graphql validation error name

* 🥳

* use ErrorCode.Forbidden

* fix blackbox auth login test

* Add license files

* Rename preMutationException to preMutationError

* Remove unused ms dep & sort package.json

* Remove periods from error messages for consistency

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>

* Add optional code check

* Use updated error code checker

* Rename InvalidConfigError to InvalidProviderConfigError

---------

Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com>
Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
Co-authored-by: ian <licitdev@gmail.com>
2023-06-27 17:22:26 -04:00

2.9 KiB

description, readTime
description readTime
A guide on how to build custom API endpoints in Directus. 3 min read

Custom API Endpoints

Custom API Endpoints register new API routes which can be used to infinitely extend the core functionality of the platform.

Extension Entrypoint

The entrypoint of your endpoint is the index file inside the src/ folder of your extension package. It exports a register function to register one or more custom routes. Each route of your endpoint will be a sub-route of /<extension-name>.

::: tip Extension Name

The extension name is usually the name of the folder where you put your extension when deploying it.

:::

Example of an entrypoint:

export default (router) => {
	router.get('/', (req, res) => res.send('Hello, World!'));
};

Alternatively, you can export a configuration object to be able to customize the root route:

export default {
	id: 'greet',
	handler: (router) => {
		router.get('/', (req, res) => res.send('Hello, World!'));
		router.get('/intro', (req, res) => res.send('Nice to meet you.'));
		router.get('/goodbye', (req, res) => res.send('Goodbye!'));
	},
};

The routes of this endpoint are accessible at /greet, /greet/intro and /greet/goodbye.

Available Options

  • id — The unique key for this endpoint. Each route of your endpoint will be a sub-route of /<id>.
  • handler — The endpoint's registration handler function.

Register Function

The register function receives the two parameters router and context. router is an Express router instance. context is an object with the following properties:

  • services — All API internal services.
  • database — Knex instance that is connected to the current database.
  • getSchema — Async function that reads the full available schema for use in services
  • env — Parsed environment variables.
  • loggerPino instance.
  • emitterEvent emitter instance that can be used to trigger custom events for other extensions.

::: warning Event loop

When implementing custom events using the emitter make sure you never directly or indirectly emit the same event your hook is currently handling as that would result in an infinite loop!

:::

Example: Recipes

import { createError } from '@directus/errors';

const MyExtensionError = createError('MY_EXTENSION_ERROR', 'Something went wrong...', 500);

export default (router, { services }) => {
	const { ItemsService } = services;

	router.get('/', (req, res, next) => {
		const recipeService = new ItemsService('recipes', { schema: req.schema, accountability: req.accountability });

		recipeService
			.readByQuery({ sort: ['name'], fields: ['*'] })
			.then((results) => res.json(results))
			.catch((error) => {
				return next(new MyExtensionError());
			});
	});
};