Skip to content

Handlers

Handlers are universal functions, that runs only on the server and can serve as API endpoints, middlewares, props for pages or completely replace the page rendering.

Handlers are defined in the ./pages directory and have the .js or .ts extension.

Each handler needs to export a default function, that receives the context ZubyPageContext as a first argument and optionally the next function as a second argument.

The context ZubyPageContext object contains all the information about the current request, matched routes params, page props and more. The context is shared between all handlers and the page component and can be used to pass data between them and props to pages.

Handlers can be also async functions that return a promise and can be used to fetch data from the database or external API. Zuby.js will wait for the promise to resolve before the page is rendered.

Let’s take a look at following examples.

Handler as API endpoint

Every handler can return a response object that will be sent to the client as a response to the request and will stop the execution of the next handlers in the chain as well as the page rendering.

Example of a simple handler that will return a JSON response:

pages/users/[id].js
export default function Handler(context) {
return new Response(JSON.stringify({ id: context.params.id }), {
headers: {
'Content-Type': 'application/json'
}
});
}

If the handler returns just object, Zuby.js will automatically serialize it and send it to the client as JSON response with correct content type header.

pages/users/[id].js
export default function Handler(context) {
return {
id: context.params.id,
date: new Date()
}
}

Handler as separate page

If the handler returns just a string, Zuby.js will automatically convert it and send it to the client as HTML response with correct content type header.

pages/users/[id].js
export default function Handler(context) {
return '<h1>Hello world!</h1>';
}

Handler as middleware

The next function can be used to execute the next handler in the chain and allows handler to act as a middleware.

Example of a simple handler that will log a message before the next handler is executed:

pages/users/[id].js
export default function Handler(context, next) {
console.log('First handler');
return next();
}
pages/[id].js
export default function Handler(context, next) {
console.log('Second handler');
return next();
}

Handler as auth middleware

The handlers can be used to implement security middlewares that will run before the page is rendered and can be used to check if the user is authenticated.

pages/users/[id].js
export default async function Handler(context, next) {
const bearerToken = context.request.headers.get('Authorization');
const authorized = await checkToken(bearerToken);
if (!authorized) {
return new Response('Unauthorized', { status: 401 });
}
return next();
}

Handler for dynamic redirects

The handlers can be used to implement complex redirects.

pages/users/[id].js
export default function Handler(context, next) {
return new Response(null, {
status: 302,
headers: {
Location: `/users/${context.params.id}/profile?${context.request.url.searchParams.toString()`
}
});
}

Handler as page props

The context ZubyPageContext object can be used to pass props to the page component. The following handler servers as a page props provider.

This design pattern is alternative to the getServerSideProps and getStaticProps functions from Next.js and allows to define the props for both pre-rendered and not pre-rendered pages in Zuby.js.

Example of a simple handler that will pass the props to the page component:

pages/users/[id].js
export default function Handler(context, next) {
context.props = {
title: 'About us',
description: 'Learn more about us'
};
}

Execution order

Zuby.js will always try to find the handler for the current route and execute it before the page is rendered. Only one handler can run by default, unless you use the next function to execute the next handler in the chain.

The handlers are executed from the most to least specific.
Example:

  • /users/123.js - Runs first
  • /users/[id].js - Runs second if it’s called by the next() function
  • /[...path].js - Runs third if it’s called by the next() function