Contents
NextJs provide a routing system that can handle almost popular use cases. Some notables about this routing system:
- Fully supported for Client Side routing
- Accept url object
- Support for both
push
andreplace
actions - Usable with any element that support
onClick
event - Expose
href
attribute to child component - Shallow Routing
Mask Routing
Check out these tutorials about Next.js
Next.js + Ant Design with less – Advanced Nextjs and Ant design scaffolding
Convert HTML template into NextJs app
Create custom self-api Next.js api server
[Tutorial] Create react based server side rendering app with NextJS
NextJs Data fetching – Combine Server Side and Client Side fetching
Cloudreports – Next.js tutorials
(Check out https://nextjs.org/docs/#routing for more)
This tutorial will be based on basic example here: https://github.com/cloudreports/my-nextjs/tree/basic
Mask Routing
is a great feature, it help display url in any way you want, for example:
import Link from 'next/link';
export default () => (
<div>
<Link as="/contact" href="/?page=contact">
<a>contact</a>
</Link>{' '}
to read more
</div>
);
In above example, browser will use the value of as
attribute for rendered a
tag without care about Link
href
attribute ( <a href="/contact">
). When clicking on “contact” link, the browser will navigate to /contact
. It won’t send xhr request because contact
component do not existed in pages
directory, and you will get 404 page.
This feature is great but it’s also have disadvantages. Imagine, you have above component, then you want to share that page to your friend. By copying domain.com/contact
, you are sending broken link to other. Trust me, that is what happen if you use Mask Routing
without a custom server. In this post we will find out what is NextJs custom server
and how to build one for your own.
NextJs have it own built-in server, this server is running when npm run dev
or npm run start
is executed. But this server only serve request with path exactly existed in pages directory. If you request a non-existed component page, you will get a 404 error.
Because of this situation, NextJs allow us to add a custom server and serve any custom url in NextJs style.
Install KoaJs and koa-router
npm i koa koa-router -s
Create file server.js
at root of project
// Import prerequisite packages
const next = require('next');
const Koa = require('koa');
const Router = require('koa-router');
// Initialize KoaJs server and router
const server = new Koa();
const router = new Router();
// Initialize NextJs instance and expose request handler
const nextApp = next({ dev: true });
const handler = nextApp.getRequestHandler();
(async () => {
try {
await nextApp.prepare();
router.get('/custom-page', async ctx => {
await nextApp.render(ctx.req, ctx.res, '/myHandlerComponent', ctx.query);
ctx.respond = false;
});
router.get('*', async ctx => {
await handler(ctx.req, ctx.res);
ctx.respond = false;
});
server.use(router.routes());
server.listen(3000, _ => console.log('App running on port 3000'));
} catch (e) {
console.error(e);
process.exit();
}
})();
Let me explain about code inside async
block.
First, we need to call NextJs prepare
function. Behind this one line of code, NextJs generate and register required routes. NextJs app cannot run without these routes.
/_next/static/:path*
This route use for serve commons, chunks and assets from build resources/_next/:path*
This path is needed because `render()` does a check for `/_next` and the calls the routing again/static/:path*
For serving custom static resources/:path*
ifuseFileSystemPublicRoutes
are enabled, this path will be used for serving public resources
After preparing NextJs app, we have to define our custom route here. NextJs provide simple api for handle a request, the same as default server:
await nextApp.render(request, response, '/handleComponent', query);
request
is http request object. This object can be exposed from http/https module or expressjs, koajsresponse
is http responsehandleComponent
this is a normal NextJs page component that located atpages
directoryquery
is just an object, you can pass any data here, this object can be empty but it will be good practice that we pass query object here
handleComponent file:
import React from 'react'
export default class MyHandlerComponent extends React.Component {
render() {
return (
<div>My Handler component</div>
)
}
}
Important part:
router.get('*', async ctx => {
await handler(ctx.req, ctx.res);
ctx.respond = false;
});
At this part, we capture all un-handled requests, this is a must-have block, it care about these type of requests:
- All request to page component: all requests to existed page component will be handled here, if you remove this block, you will got an error even you have the page component
- Handle not found request
You should write routes in a separated file, in server.js
just import route file and add it as a handler
With custom server file, you lose file watching and hot module replacement. This mean, you have to restart nodejs server any time you modify a file
To avoid this inconvenience, you could apply these changes to your project:
Install nodemon:
npm i nodemon -g
(run with sudo
if required)
Create nodemon config file: nodemon.json
in root of your project
{
"verbose": true,
"ignore": ["node_modules", ".next", "static"],
"watch": [
"libs/*",
"libs/**/*",
"routes/**",
"routes/**/*",
"server.js",
"store.js",
"next.config.js",
".babelrc.js"
],
"ext": "js jsx json"
}
If you work with more files and dirs, add them into watch
array
Change your npm scripts:
"scripts": {
"dev": "nodemon server.js --exec",
"build": "next build",
"start": "next start",
"test": "echo \"Error: no test specified\" && exit 1"
}
Now whenever a file is updated, nodemon will automatically restart node app for you
Github repository: https://github.com/cloudreports/my-nextjs/tree/custom-server-koajs
Thank you for reading and see you in next post!
Discussion about this post