Have you ever think about connect to Mysql database or Mongodb database with NextJs?

Or have you asked some questions as:

How to connect to a database with NextJs?

How not to create a separate api server for NextJs?

How to require server file inside a NextJs page?

Well, you may know that Next.js is a great framework for creating server side rendering application. I just try Next.js for a few months and was very excited about it’s design and easy to use.

Next.js was fully supported server side rendering. For now, it may be the best choice for building react based SSR website.

Follow this post NextJs Data fetching, we have got a method for fetching data with Next.js through getInitialProps . But this method is still have disadvantages: You have to build an external api system for NextJs app to work!

Why external api system is a problem here?

Firstly, with external api, you have to build a separate app that work as data provider. Manage two separate Nodejs apps for a simple web is complex.

Secondly, when you have more than one app, you also have to manage more complex deploy, maintain, upgrade process.

And last, when in development, it is not convenience to work on two separated projects while you “should” work on single one.

Why NextJs support SSR, it work as a real SSR system and you still have to build api? Like a CSR SPA???

In this post, we will follow an idea that help answer some questions like these:

Can i merge external api service and NextJs app in to one? Can i use database or connect to a database in NextJs app? If i have important information and want to use it in getInitialProps , how can i secure these information?

Because NextJs will expose getInitialProps for both client side and server side, so if you put any code here, you it will be exposed to both side code. It will be bad practice if you put any secure information here. This is also the reason why NextJs official document only saying about fetching data from external api.

Any code inside getInitialProps must be compatible with server side and client side code, any script that only work on server side will cause error during NextJs build process. This is the reason why you cannot do Database connect inside getInitialProps , you shouldn’t put any server side logic here.

You may give up here but slow down, there is something else that help 😀

Before go deep into the solution, i will describe something about NextJs build process.

NextJs won’t build whole server side and client side code at once, because the runtime and environment for these sides are difference. So NextJs will use difference config, difference process for building each side code.

Beside that, NextJs also expose some special variables during the build process. We can use these variables for optimize our source code. Bellow are some use case of exposed NextJs variable:

Using as environment variables: these variables will be use while build process (not runtime)

Use for conditional building: you may change some setting based on build environment (development, production)

Use for minified source code without losing readability

Take a look at Use for conditional building can you guest what is it mean?

One of the build variables you should noted is: process.browser . In client side building, it will be passed as true and in opposite undefined for server side building.

Let me explain through this example:

Create file pages/conditionalBuild.jsx

import React from 'react' export default class ConditionalBuildingPage extends React.Component { static async getInitialProps() { let resultText = ''; if (process.browser) { resultText = 'This is text for CLIENT' } else { resultText = 'This is text for SERVER' } return {text: resultText} } render() { return <div>{this.props.text}</div> } }

run npm run dev then navigate your browser to http://127.0.0.1:3000/conditionalBuild

When NextJs running build, it will create two directory inside .next

server for storage server side code

for storage server side code static for storage client side code

Look at these files:

.next/static/development/pages/conditionalBuild.js

.next/server/static/development/pages/conditionalBuild.js

in both two file, find this text: key: "getInitialProps" and look into followed script

This is the result:

File: .next/static/development/pages/conditionalBuild.js

Built result for client side code

File: .next/server/static/development/pages/conditionalBuild.js

Built result for server side code

Surprised!!! Instead of expose full code in original getInitialProps , NextJs (with help of webpack/babel) was by pass the check condition. While building, NextJs can check the build variables and decided which code to be expose; it will remove unnecessary code on each side and only keep the usable part. So, the point here is that: Client side code won’t consists Server side code. Server side code won’t be mixed with Client side code too. So awesome!!!

Now, do you think about what you can do next? Did you got the answers for three questions at the starting :D? If not, keep calm and i will give you the answers :v

The idea is quite simple! Instead of calling api on both client side and server side, we will ony do it on client side code. The api used for client fetching will be a “local api”. These apis are served through our NextJs Custom Server.

On server side, we will call direct service inside server source code!

Let’s start on doing this awesome thing!

Imagining we have to create a component that display a list of some countries.

First of all, clone this repository and switch to branch custom-server-koajs

https://github.com/cloudreports/my-nextjs/tree/custom-server-koajs

Prepare some resources:

Create libs dir:

mkdir libs

Create file libs/dataProvider.js with this content

exports.countryList = () => { return [ {"name": "Australia", "code": "AU"}, {"name": "Austria", "code": "AT"}, {"name": "Azerbaijan", "code": "AZ"}, {"name": "Bahamas", "code": "BS"}, {"name": "Bahrain", "code": "BH"}, {"name": "Bangladesh", "code": "BD"}, {"name": "Barbados", "code": "BB"}, {"name": "Belarus", "code": "BY"}, {"name": "Belgium", "code": "BE"}, {"name": "Belize", "code": "BZ"}, {"name": "Benin", "code": "BJ"}, {"name": "Bermuda", "code": "BM"}, {"name": "Bhutan", "code": "BT"}, {"name": "Bolivia", "code": "BO"}, {"name": "Bosnia and Herzegovina", "code": "BA"}, {"name": "Botswana", "code": "BW"}, {"name": "Bouvet Island", "code": "BV"}, {"name": "Brazil", "code": "BR"}, {"name": "British Indian Ocean Territory", "code": "IO"}, {"name": "Brunei Darussalam", "code": "BN"}, {"name": "Bulgaria", "code": "BG"}, {"name": "Burkina Faso", "code": "BF"}, {"name": "Burundi", "code": "BI"}, {"name": "Cambodia", "code": "KH"}, {"name": "Cameroon", "code": "CM"}, {"name": "Canada", "code": "CA"}, {"name": "Cape Verde", "code": "CV"} ]; }

Modify server.js file:

Add this line after const handler = nextApp.getRequestHandler();

Add these lines before router.get('*', async ctx => { (this is important)

global.SERVER_APP_ROOT = __dirname; const dataProvider = require(SERVER_APP_ROOT + '/libs/dataProvider');

router.get('/api/country', async ctx => { ctx.body = dataProvider.countryList(); });

Start NextJs server and check if api http://127.0.0.1:3000/api/country worked.

We will continue by working with ConditionalBuildingPage component inside pages/conditionalBuild.jsx

Look at this script (we will call this: S1), it will describe the idea of self-api-server

let conditionalDataProvider = null; if (process.browser) { conditionalDataProvider = async () => { let res = await fetch('http://127.0.0.1:3000/api/country'); let countries = await res.json(); return countries; } } else { conditionalDataProvider = async () => { const dataProvider = require(SERVER_APP_ROOT + '/libs/dataProvider.js'); let countries = dataProvider.countryList(); return countries; } }

As you can see, in above script we check if the current built is for client or server. If this built is for client, we will call api that we defined before. If it is for server, we will directly get data from dataProvider

Modify pages/conditionalBuild.jsx and change getInitialProps to

static async getInitialProps() { let countries = await conditionalDataProvider(); return {countries} }

Add function S1 after line import React from 'react'

Change function render to

render() { return ( <div> <h1>Country list</h1> <ul> {this.props.countries.map((country, i) => { return ( <li key={'country-' + i}>{country.name}</li> ) })} </ul> </div> ) }

Restart NextJs server and check http://127.0.0.1:3000/conditionalBuild

wow, we got an error: Unhandled Rejection (Error): Cannot find module

The reason is that, when webpack compile source, it will convert require to __webpack_require__ and __webpack_require__ does not understand dataProvider.js file path. We can config web pack to include libs/* into it built. But there are some reasons prevent us do this:

Our idea is that we will put some logic code that work only on server side inside libs directory. These code may be do database connecting, hash or any thing else that should be secure. We don’t need to build these code by webpack, it should be run as a normal javascript file.

There are many library that writen in es5, they just use require and exports . When mixing with component file ( jsx files), we have to config so many complicated thing. If not, webpack will throw error on build process, or if it success on build process, then the built file should not work as expected

Re-building all the files that does not required will only cause the build process take more time, and it is not a good practice too.

Now it is time for our tricky require:

Modify pages/conditionalBuild.jsx and change

const dataProvider = require(SERVER_APP_ROOT + '/libs/dataProvider.js');

to

const dataProvider = eval("require('" + SERVER_APP_ROOT + "/libs/dataProvider')");

Restart your NextJs server and reload 127.0.0.1:3000/conditionalBuild

now it work as expected!

Let me explain about the reason for using eval in this case

When using eval , require command is passed as string. Webpack will not analyze any thing inside this string, this require part is only existed in webpack build as a normal parameter!

Conclusion

This tutorial present a simple idea for design a better NextJs app (IMO), follow this tutorial, you may have a generality look about this idea.

Look at file libs/dataProvider.js , this file is just a normal js file. But the way it was called inside server.js and conditionalBuild.jsx is the key here

Inside libs/dataProvider.js you can do almost thing as a normal NodeJs file, these can be:

Connect to database (it is what called “Connect database inside NextJs app`)

Hashing/Encrypting/Decrypting…

Storage server side secure logic

Well, i hope you understand my idea and can apply this on your project! If i have any mistakes or if you have any problem while following this tutorial, please let me know.

Thank you!

Git repo for this turotial:

https://github.com/cloudreports/my-nextjs/tree/custom-self-api-server