Nirmalya Ghosh I'm a computer science engineer specializing in web design and development with an eye for detail. I have over three years of experience with React and also fiddle with Ruby on Rails and Elixir.

Optimizing static pages in your Next.js apps with Prisma

4 min read 1236

Optimizing Static Pages In Next.js Apps With Prisma

I’ve been building applications using Next.js for quite some time, and I’m consistently amazed by the developer experience it provides. It’s very easy to build a highly performant server-side-rendered (SSR) application using Next.js.

In version 9.3, Next introduced some amazing features, including:

In this post, we’ll mostly discuss next-gen SSG support and why integrating your Next.js app with Prisma makes this feature even more powerful. All the code snippets present in this article are available on GitHub.

Why use Prisma with Next.js?

Prisma is an open-source database toolkit. It helps us in querying our database by providing a type-safe API. Prisma Client is an auto-generated query builder that provides type-safe access to our database.

Integrating Prisma with Next.js

Integrating Prisma Client with Next.js is very easy. We need to install the dependency first:

yarn add @prisma/client

Next, we’ll need to initialize Prisma. Run the following command from the project root:

npx prisma init

This will generate the following files:

Initializing Prisma In Our App
Initializing Prisma in our app.
  1. schema.prisma – the Prisma schema with our database connection and the Prisma Client generator
  2. .env – a dotenv file for defining environment variables (used for our database connection)

We’ll be using SQLite in our application, so let’s modify our .env file:

// prisma/.env

DATABASE_URL="file:./dev.db"

Let’s also modify our schema file to add two models:

  1. Post
    1. id
    2. title
    3. content
    4. published
    5. author
    6. authorId
  2. User
    1. id
    2. email
    3. name
    4. posts
// prisma/schema.prisma

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

Next, we need to migrate our database. To do that, we’ll run the following command:

npx prisma migrate save --experimental && npx prisma migrate up --experimental

Now, our Prisma directory will look like the following:

Prisma Directory Structure
Our directory structure after running migrations.

Next, we need to generate our Prisma client. We can do so with the following command:

npx prisma generate

Please note that we need to run the prisma generate command after we change our Prisma schema to update the generated Prisma Client code. Also, prisma generate is automatically invoked when we install the @prisma/client npm module.

Now we can use Prisma in our code like the following:

// pages/get-static-props/index.js

import { PrismaClient } from "@prisma/client";
import { Stack, Box, Link } from "@chakra-ui/core";
import _Link from "next/link";

const IndexPage = ({ posts }) => {
  return (
    <Stack spacing={4}>
      {posts.map((post) => {
        return (
          <Box key={post.id}>
            <_Link
              href="/get-static-props/[postId]"
              as={`/get-static-props/${post.id}`}
            >
              <Link>{post.title}</Link>
            </_Link>
          </Box>
        );
      })}
    </Stack>
  );
};

export async function getStaticProps() {
  const prisma = new PrismaClient();
  const posts = await prisma.post.findMany();

  return {
    props: {
      posts,
    },
  };
}

export default IndexPage;

We can view our data through the Prisma Studio using the following command:

npx prisma studio --experimental

If we now visit http://localhost:5555/, we should be able to view the Studio.

Prisma Studio
Previewing Prisma Studio.

From here, we can manage the data through this interface.

What is static site generation?

When building web applications, we can choose to make our application generate statically or render it on the server side.

Before the release of version 9.3, any applications built with Next.js could opt into automatic static optimization. Any page that doesn’t have a blocking request using getInitialProps would be optimized automatically, and the page would be rendered as static HTML.

However, it wasn’t possible to generate static pages for dynamic routes. With getStaticProps and getServerSideProps, it’s now possible to do SSG using Next.js.

getStaticProps

If we export an async function called getStaticProps, Next.js will fetch the data for this page during build time and store it as JSON inside the page. This is useful when we want to render static pages while fetching data from an API.

For example, if we define a getStaticProps async function inside a page, it can fetch the data during build time and send that data to the component as props.

// pages/get-static-props/index.js

export async function getStaticProps() {
  const prisma = new PrismaClient();
  const posts = await prisma.post.findMany({
    include: { author: true },
  });
  return {
    props: { // This will be sent to the component as props
      posts, 
    },
  };
}

In the component, we can access the posts data as a prop:

// pages/get-static-props/index.js

const GetStaticPropsIndexPage = ({ posts }) => {
  return (
    <Stack spacing={4}>
      {posts.map((post) => {
        return (
          <Box key={post.id}>
            <Heading mb={4} size="md">
              {post.title}
            </Heading>
          </Box>
        );
      })}
    </Stack>
  );
};

The above will render the content as expected:

Fetching Data Using getStaticProps
Fetching data using getStaticProps.

getStaticProps will generate the following JSON during build time:

Our Generated JSON
JSON generated at build time.

Now, if we visit http://localhost:3000/get-static-props/1, we’ll see the following page:

Our First Post Page
Our first post page.

To view this page, we’ll have to add another async function called getStaticPaths:

// pages/get-static-props/[postId].js

export async function getStaticProps(ctx) {
  const prisma = new PrismaClient();
  const post = await prisma.post.findOne({
    where: {
      id: parseInt(ctx.params.postId),
    },
  });
  return {
    props: {
      post,
    },
  };
}
export async function getStaticPaths() {
  return {
    paths: [{ params: { postId: "1" } }],
    fallback: false,
  };
}

Next.js will statically generate /1 at build time using the page component in pages/get-static-props/[postId].js. If we now visit http://localhost:3000/get-static-props/1 and refresh the page, we’re able to view the page. However, if we do the same for http://localhost:3000/get-static-props/2, we’d get a 404.

To solve this issue, we need to add the postId 2 as well to the paths array:

paths: [{ params: { postId: "1" } }, { params: { postId: "2" } }],

Now, the http://localhost:3000/get-static-props/2 page will load fine.

getServerSideProps

If we export an async function called getServerSideProps in a page, Next.js will pre-render the page on each request using the data returned by getServerSideProps. getServerSideProps gets called every time we load the page, but the code is only executed on the server, unlike getInitialProps.

// pages/get-server-side-props/index.js

export async function getServerSideProps() {
  const prisma = new PrismaClient();
  const posts = await prisma.post.findMany();
  return {
    props: {
      posts,
    },
  };
}

We can also fetch individual posts at http://localhost:3000/get-server-side-props/1:

// pages/get-server-side-props/index.js

export async function getServerSideProps(ctx) {
  const prisma = new PrismaClient();
  const post = await prisma.post.findOne({
    where: {
      id: parseInt(ctx.params.postId),
    },
  });
  return {
    props: {
      post,
    },
  };
}

Conclusion

In this post, we learned about how to make our Next.js applications faster using advanced features like getStaticProps, getServerSideProps, and Prisma. Until now, we’ve used getInitialProps, which would make the page pre-render. However, any subsequent page redirects will fetch on the client side to get the new data.

With getServerSideProps, every time the data will be fetched on the server, even on page redirects. With getStaticProps, the data is fetched at build time; it won’t be called on the client side. As a result, the SSG page becomes very fast.

LogRocket: Full visibility into production Next.js apps

Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your Next.js apps — .

Nirmalya Ghosh I'm a computer science engineer specializing in web design and development with an eye for detail. I have over three years of experience with React and also fiddle with Ruby on Rails and Elixir.

Leave a Reply