Skip to main content

Endpoint

An endpoint instance describes an individual API route, and its handling of requests by HTTP method.

It is returned from calling a factory (called createEndpoint in this documentation).

export const endpoint = createEndpoint({
methods: (method) => ({
get: method({
handler: () => 'foo',
}),
}),
});

export default endpoint.handler;

Configuration

The endpoint can be configured to affect the final behaviour of the route handler.

Method handling

The main way of defining an endpoint's behaviour is by creating method definitions. This is created with a callback receiving a builder object, and returning an object with each definition under its corresponding method.

export const endpoint = createEndpoint({
methods: (method) => ({
get: method({
handler: () => 'foo',
}),
}),
});

Default handler

By default, an endpoint will respond to an unrecognised method request by setting the Allow header to a list of supported methods, and setting the status to 405.

If you wish to replace this behaviour, you can provide a default handler:

export const endpoint = createEndpoint({
methods: (method) => ({
get: method({
handler: () => 'foo',
}),
}),
default: (method) =>
method({
handler: (data, { res, failWithCode }) => {
res.setHeader('Allow', 'GET');
throw new failWithCode(405, 'Unrecognised method');
},
}),
});

Handler decoration

It's a fairly common pattern to create a function that receives a NextJS handler and returns a new handler wrapping it. CEF supports this via decorators.

import type { NextApiHandler } from 'next';
import { createEndpoint, withAuth, withFoo } from '../../src/api';

export const endpoint = createEndpoint({
methods: () => ({}),
decorators: [withFoo, withAuth],
});

export default endpoint.handler;
info

Decorators will be called from right to left, meaning that withAuth will receive the original handler, and withFoo will receive the handler that withAuth returns.

This means that the above is equivalent to:

import type { NextApiHandler } from 'next';
import { createEndpoint, withAuth, withFoo } from '../../src/api';

export const endpoint = createEndpoint({
methods: () => ({}),
});

export default withFoo(withAuth(endpoint.handler));

However, this approach doesn't decorate the included individual method handlers, only the combined handler.

caution

Each decorator is expected to take one parameter, the handler. If the original wrapper needs extra parameters, for example configuration, then a secondary function would be necessary.

import type { NextApiHandler } from 'next';
import { createEndpoint, withFoo } from '../../src/api';

export const endpoint = createEndpoint({
methods: () => ({}),
decorators: [(handler: NextApiHandler) => withFoo(handler, { bar: true })],
});

Instance

Calling the factory function with configuration will return an object with a collection of useful handlers attached.

Combined handler

The endpoint handler (handler on the instance returned) combines all of the method definitions and (if provided) default into a final handler which will automatically execute the correct method definition based on the request method (or default behaviour if method is unsupported).

tip

This is usually what you'll want to export as default from your API route file.

export default endpoint.handler;

Individual method handlers

For each defined method, the final endpoint instance will have a handler attached under the methods key. For example, if you only had a GET handler defined, then there would be a handler defined for endpoints.methods.get.

Default handler

If a default is defined, a wrapped handler for this will be attached as endpoint.default.