Skip to main content

Next.js

What is Next.js?

Next.js is a JavaScript web framework that combines frontend and backend functionality under one hood. Working on both layers of your software is often referred to as Full-Stack development.

Read before Starting

  • React.js - Next.js is based on React.js.
  • Express.js - The following tutorial builds on our Express.js tutorial.

Backend

Let's implement our Express.js example in Next.js. To get started, create a new Fullstack Next.js project in StackBlitz.

Create a new file called dad.js under /pages/api. This automatically creates a new route that can be used to access your app. Add the following code to the new file.

const store = ['Welcome to the Board! Send in your best dad jokes.'];

Similar to our Express.js tutorial, we will create a temporary store in memory for now. Please see SQL, NoSQL, Firebase for permanent storage.

export default (req, res) => {
if (req.method == 'GET') {
res.status(200).json({ jokes: store });
}

if (req.method == 'POST') {
const { newJoke } = req.body;
store.push(newJoke);
res.status(200).send();
}
};

That's it! As you might have noticed, this task was much simpler to code in Next.js compared to Express.js.

One major difference in syntax is that get and post are not separate methods here. You export one function for both, and check the value of req.method to decide what procedure to perform.

Frontend

Let's use what we learned in React to make a simple client for this API. We want our users to be able to view a list jokes, and send new jokes.

Delete all existing code from index.js and use this file to write the following code.

Imports

import { useState, useEffect } from 'react';

Page

export default function Home() {
// The rest of your code goes here...
}

We declare our page as a functional component. All the remaining code below will go inside the { } (body) of this function.

State

const [jokes, setJokes] = useState([]);
const [myJoke, setMyJoke] = useState('');

We declare 2 state variables along with their setters:

  1. jokes – a list of jokes (strings) sent by the backend to the frontend
  2. myJoke – a new joke (string) that the frontend wants to send to the backend

Backend to Frontend Communication

const refresh = () => {
fetch('/api/dad')
.then((res) => res.json())
.then(({ jokes }) => {
setJokes(jokes);
});
};

We define a refresh function that fetches /api/dad. Since the HTTP Method is not defined for fetch, the default GET is assumed. When this function is called by the frontend, it will trigger the GET case from our backend code. As a result, the list of jokes will be sent to the frontend.

We use the then syntax to define the chain of events that happen when the data is received by the frontend. First, it is converted from a JSON string to a JSON object. Next, we extract jokes from the object and assign it to our state variable for later use.

Frontend to Backend Communication

const send = () => {
const method = 'POST';
const headers = { 'Content-Type': 'application/json' };
const body = JSON.stringify({ newJoke: myJoke });
fetch('/api/dad', {
method,
headers,
body,
}).then(refresh);
};

We define a send function that sends the state variable myJoke under field name newJoke to /api/dad using HTTP POST. As a result, the backend adds the new joke to its store and sends an OK response. Upon receiving OK, the frontend then calls the refresh function we defined above to update the user's view of the list.

Executing Refresh on Page Load

useEffect(refresh, []);

React's useEffect calls the function passed in first argument, whenever one of the items in the list in second argument changes. Here, we wanted React to call refresh once on page load, without any dependencies. So, we pass refresh as the first argument, and an empty list [] as the second.

Mapping

const items = jokes.map((joke) => <li>{joke}</li>);

We map jokes, an array of strings, to items, an array of HTML elements.

Return

const handleChange = ({ target: { value } }) => {
setMyJoke(value);
};

return (
<>
<div>
<input value={myJoke} onChange={handleChange} />
<button onClick={send}>Send</button>
</div>
<ul>{items}</ul>
</>
);

Finally, we return our desired page structure.

The <div> on top is used for sending jokes. This further contains a (text-based) <input> and a <button>.

We want the value of the <input> to be the current state of myJoke. Also, when the <input> notices a change in its value, it calls the handleChange function to copy the current value from the event target to the state variable. This is one way of synchronizing an input to the state of a React app.

The <button> calls send when it is clicked.

The <ul> on bottom displays the items array that resulted from the mapping in the previous step. Upon successful execution of the refresh function, this area should display the list of all jokes on server.

Complete Example