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;
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.
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.
- TypeScript
- JavaScript
import type { NextApiHandler } from 'next';
import { createEndpoint, withFoo } from '../../src/api';
export const endpoint = createEndpoint({
methods: () => ({}),
decorators: [(handler: NextApiHandler) => withFoo(handler, { bar: true })],
});
import { createEndpoint, withFoo } from '../../src/api';
export const endpoint = createEndpoint({
methods: () => ({}),
decorators: [(handler) => 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).
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
.