October 05, 2023
Development Cycle with Next.js
Every application depends on a service reliability and configuration. If the service breaks down it is not a good experience. To get as much service reliability as possible a proper configuration is a must. Things happen and they happen for a reason. However, picking reliable tools eliminates a lot of concern on a user end as well as a developer end. I use JavaScript/ReactJS, Next.js and MongoDB for a smooth developer experience, configuration reasons and user experience at the end of the wire. Here is how I set up application with the tools provided.
Front part
First of all my concern is to get a front part of application running with ReactJS.
import { useAuthCtx } from './authProvider';
import AppComponent from './AppComponent';
import { useRouter } from 'next/router';
export default function Home() {
const { isUserLoggedIn: isAuthenticated } = useAuthCtx();
const router = useRouter();
if (!isAuthenticated) {
router.push('/login');
return <></>;
}
return <AppComponent />;
}
export async function getServerSideProps(context: any) {
return { props: {} };
}
A preceding code concerns about exposing an application to only authenticated users. If user is not authenticated then user is redirected to login page and then Home component returns React fragment so nothing else is exposed. If user is authenticated and authentication is successful inside useAuthCtx() then the main <AppComponent /> is returned and application is exposed.
Back part
Secondly I choose to setup Next.js part through Express.js framework. This allows me to run application in a backend environment with a simple node server.js command instead of npm run dev command.
const express = require('express');
const bodyParser = require('body-parser');
const next = require('next');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true }));
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
In a preceding code a server configuration happens through next() function which accepts an object with an environment property. In this case we are in a 'development' environment. Then we need to run the application with prepare function and do a further configuration through express framework so the app is exposed through express.js but next.js part is handled by handle(req, res) function. As a result your browser request to 3000 port returns user environment we talked about earlier.
Database part
Thirdly we need a database. In this case we decided to choose MongoDB. This approach allows us to choose reliability service to store our data.
const mongoose = require('mongoose');
const options = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
async function opendb() {
try {
return await mongoose.createConnection(process.env.MONGODB_URI, options);
} catch (err) {
console.log('Mongoose connection error', err);
}
}
module.exports = { opendb };
In a preceding code we setup database connection inside a function. We can use this function to open a database connection when we need it. opendb() function returns a promise with a database connection. To create a connection we use mongoose.createConnection() function and pass connection string which we store inside env poperty and options parameter which is an object of key value pairs. Finally we export a function so it can be used throughout a development cycle.
Final part
Here is what we did. We setup frontend through backend. We have express.js app running as well as next.js. Now everything can be hosted inside a server as one application frontend and backend. We do not need to write backend and frontend separately. We can handle all requests inside backend and handle all frontend concerns inside frontend as we were writing a usual frontend application. This cuts down development time and we can focus on a more important tasks like user experience and data flow.
191 views