Skip to main content

Method

A method definition describes how an individual request to an endpoint should be handled, for a given HTTP method.

Configuration

A method definition can be configured to change its behaviour when executed.

Handler

The handler defines the main logic used when executing the method. It will be called with two objects, handlerData and handlerApi.

It's expected to execute some logic, and optionally return some data. Depending on what the method handler returns, the final endpoint handler will send a different status and response:

Handler resultStatusFinal response
Data returned200Data sent via res.json()
No data returned (undefined)204res.end() called
Error thrown500Serialised error sent via res.json()
pages/api/foo.ts
import { createEndpoint } from '../../src/api';

const endpoint = createEndpoint({
methods: (method) => ({
get: method({
// res.status(200).json('foo')
handler: () => 'foo',
}),
put: method({
// res.status(204).end();
handler: ({ body }) => {
console.log(body);
},
}),
post: method({
// res.status(500).json(serializeError(new Error('oops!')));
handler: () => {
throw new Error('oops!');
},
}),
}),
});

export default endpoint.handler;

Custom codes

In cases where you want to use a different status code, you can use the failWithCode and succeedWithCode utilities provided as part of the handlerApi object (second parameter for handler callback).

pages/api/foo.ts
import { createEndpoint } from '../../src/api';

const endpoint = createEndpoint({
methods: (method) => ({
post: method<{ created: true }>({
handler: ({ body }, { failWithCode, succeedWithCode }) => {
console.log(body);
if (!body) {
throw failWithCode(400, 'No body provided');
}
return succeedWithCode(201, { created: true });
},
}),
}),
});

export default endpoint.handler;

failWithCode can be returned or thrown, but it's generally better to throw it yourself.

Disabling default response handling

Typically, the default behaviour is preferable as it leads to less code overall.

However, sometimes it might be desireable to use the res parameter to send your response yourself (using it as a writeable stream, for example).

To indicate that you have already sent the response and don't want CEF to conduct its usual response handling, you can return the nothing symbol exported from CEF inside your handler.

import { nothing } from 'next-create-endpoint-factory';
import { createEndpoint } from '../../src/api';
import stream from 'stream';
import { promisify } from 'util';

const pipeline = promisify(stream.pipeline);

const endpoint = createEndpoint({
methods: (method) => ({
get: method({
handler: (data, { res, failWithCode }) => {
const response = await fetch(
'https://w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'
);
if (!response.ok) {
throw failWithCode(response.status, response.statusText);
}
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=dummy.pdf');
await pipeline(await response.blob(), res);
return nothing;
},
}),
}),
});

export default endpoint.handler;

Parsing

To verify and optionally transform parts of the original request, you can provide parsers. These will receive the original data from a request, and are expected to return data that will be passed to the final handler, or throw an error.

Currently, you can provide parsers for body and query.

pages/api/foo.ts
import { createEndpoint } from '../../src/api';

const endpoint = createEndpoint({
methods: (method) => ({
post: method<{ created: true }>()({
parsers: {
body: (body, failWithCode) => {
if (!body) {
throw failWithCode(400, 'no body provided');
}
if (typeof body !== 'string') {
throw failWithCode(400, 'invalid body');
}
return body; // now string
},
},
handler: ({ body }, { failWithCode, succeedWithCode }) => {
console.log(body); // typed as string thanks to parser
return succeedWithCode(201, { created: true });
},
}),
}),
});

export default endpoint.handler;
caution

When using Typescript, it's required to use the double call syntax method<ReturnType>()(definition) to use parsers.

This allows the ReturnType type to be provided explicitly, while inferring the parsed types.

If you're using Javascript, this is not required.