⚔️ Code Conqueror

💽 Fetching Data in Next.js

Nov 30, 2019

Next.js gives you a lot of flexibility in different ways to fetch data, but there are some best-practice patterns / methods. Recent additions from the Zeit / Next team like SWR, and upcoming build-time data fetching have also opened up a few more paths to getting data in your Next.js application.

From API Routes (eg. with a database)

Using API routes is a great way to get data into your Next.js pages. For example you can connect to a database to fetch information in an API route and then fetch it from there in the Next application. For example let's say that we need to fetch todos from a database:

// pages/api/todos.js
const MongoClient = require("mongodb").MongoClient;
const url = "mongodb://localhost:27017/";
export default async (req, res) => {
try {
const db = await MongoClient.connect(url);
const dbo = db.db("mydb");
const result = await dbo.collection("todos").findOne({});
res.status(200).json(result);
} catch (e) {
res.status(500).json({ error: e });
}
};

There we connected to a local mongo database and extracted todos from the todos collection. There's clearly a lot of room for abstracting some of this into an api-route middleware (eg that handles the connection, the responses including error state etc).

To fetch this data in the front-end pages we can simply fetch in getInitialProps:

// pages/todos.js
const Todos = ({ data }) => {
return (
<div>
{data.map((todo) => (
<div key={todo.id}>{todo.item}</div>
))}
</div>
);
};
Todos.getInitialProps = async () => {
const resp = await fetch(`/api/todos`);
const data = await resp.json();
return { data };
};

Here we just simply fetch the data from the api route and pass it to our

Todos
page component.

We wrote an article on using a database in Next.js. Which has some more good-practices.

From a GraphQL API

The best way to know how to implement GraphQL fetching in Next.js is to follow the official Apollo example. This changes somewhat frequently so it's good to look there for changes.

Basically the way it works is:

  • Wrap every page with an Apollo HOC to ensure child components have access
  • Let Apollo figure out all of the components that need to fetch data and run
    getDataFromTree
  • Just use vanilla Apollo query / mutation methods in your components, defining the queries with graphql-tag template literal

Fetching data with SWR

A few months ago Zeit released an example for fetching data from an API and caching it in your CDN with the

stale-while-revalidate
HTTP header. Essentially what it does is instruct the cache to serve a cached version of a resource, but to re-validate that resource simultaneously. The trick is to then have a strategy for updating your page if the resource changes.

Zeit has packaged up a lot of this into a nice SWR package which can handle both client and server side data fetching using this strategy.

It generally leads to ~realtime results, but also delivering mostly static resources to the end-viewer which means pages / apis load very very fast. As an example of how we can use swr from the above todos list we would refactor slightly:

// pages/todos.js
import swr from "swr";
import fetch from "isomorphic-unfetch";
const fetcher = async function (...args) {
const res = await fetch(...args);
return res.json();
};
const Todos = ({ initialData, url }) => {
const { data } = useSWR(url, fetcher, { initialData });
return (
<div>
{data.map((todo) => (
<div key={todo.id}>{todo.item}</div>
))}
</div>
);
};
Todos.getInitialProps = async () => {
const url = `/api/todos`;
const initialData = await fetcher(url);
return { initialData, url };
};

This example is how you would use SWR in combination with server side rendering. You basically fetch the data on the server and pass it through to

swr
which will handle the cache settings and re-fetching if the page focus changes.

Fetching data at build time

There is a new RFC for how Next.js could fetch data at build time. This closes one major gap between Next.js and Gatsby, where if you just want to run your queries — eg against your CMS at build time can then generate a fully static application.

We'll update this post if the RFC makes progress!