DEV Community

Cover image for Build a Fullstack App With Strapi and Next.js
Gerald
Gerald

Posted on • Updated on

Build a Fullstack App With Strapi and Next.js

Let's build a fullstack blog using Strapi and Next.js.
This article is based on Digital Ocean's Tech Talk by Chris Sev.
To get the ball rolling, let's setup our Strapi backend by running the following command in the terminal

npx create-strapi-app blog-strapi --quickstart
Enter fullscreen mode Exit fullscreen mode

This command creates a Strapi project named blog-strapi and automatically opens the admin panel in our default browser.
Let's go ahead and enter credentials and click LET'S START. We should see the Strapi dashboard as shown below

Alt Text

Let's create our collection. On the dashboard, click on CREATE YOUR YOUR FIRST CONTENT-TYPE. Let's have Post as display name and click Continue. Select Text as the collection type and name the field title and leave the Short text type selected. Click on Add another field and select Text collection type and name it slug. Let's add one more field by clicking Add another field, select Rich text and name it content and click finish and save. Our collection now looks as below
Alt Text

To create posts, go to the left panel under Collection types, click on Posts and hit the Add New Posts button. Let's add the title as My Wins This Week, slug as my-wins-this-week, content as Won the golf tournament and click save and publish. Head over to the browser at http://localhost:1337/posts. We should be getting a Forbidden error because the API is secured by default. To change that, let's get back to the Strapi dashboard and click on settings. Under USERS AND PERMISSIONS PLUGIN, click on Roles then public and select find, findone, create and click save. Let's get back to our browser and refresh http://localhost:1337/posts. We should see our post as shown below
Alt Text
Our post looks good so far but we have no idea who the author is. To be able to add an author, we need to add another field of type relation to the Post collection type. On the Strapi dashboard, click on Content-Type Builder, select Post, click on Add another field and select Relation. On the File dropdown select User(from-users-permiss...) and select a User has many Posts relation. Let's rename users_permissions_user field name to simply user and click finish and save.
Let's go ahead and create a user. Under collection types, select Users and click on Add New Users. Input user credentials and toggle Confirmed button to set it to ON. Under Posts(0), let's assign My Wins This Week post to this user(in my case daka) because why not? Click save and refresh the browser at http://localhost:1337/posts. You should see the user object included in our response as shown below
Alt Text
Go ahead and create a couple of more posts and assign them users so that we have enough data to work with on the frontend.

Let's create our Next.js frontend by running the following command in the terminal

npx create-next-app blog-next
Enter fullscreen mode Exit fullscreen mode

The above command creates a new Next.js app. Once the setup is done, run the following commands

cd blog-next
npm run dev
Enter fullscreen mode Exit fullscreen mode

This will start our Next.js app server and the app can be accessed at http://localhost:3000/.
Let's open up our app in a Text Editor(I'm using VS Code), go to index.js and replace everything in the return block with <div>We are here!</div> and save. Head over to the browser at http://localhost:3000 and we should see the output as shown below
Alt Text
Let's go above the Home component and get our API data using getStaticProps function as shown below.

export async function getStaticProps() {
  //get posts from API
  const res = await fetch('http://localhost:1337/posts');
  const posts = await res.json();

  return {
    props: { posts }
  };
}
Enter fullscreen mode Exit fullscreen mode

Now, in our Home component we can receive the posts as props and loop over them as shown below

export default function Home({ posts }) {
  return (
    <div className={styles.container}>
      {/* Loop over posts and show them */}
      {posts &&
        posts.map(post => (
          <div key={post.id} className={styles.post}>
            <h1>{post.title}</h1>
            <p>{post.content}</p>
            <em>By {post.user.username}</em>
          </div>
        ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Let's also add a bit of styling. In the styles directory, go to Home.module.css and replace all that is in there with the CSS code below.

.container {
  display: grid;
  grid-template-columns: auto auto auto;
  margin: 5%;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.post {
  padding-bottom: 10%;
}

Enter fullscreen mode Exit fullscreen mode

When we save this, we should see the posts as shown below(of course you could do better styling).
Alt Text
Now let's handle routing by creating a file called [slug].js in the pages directory. This is a way that Next.js dynamically generates a bunch of files. There are two main things we need to do in [slug].js:

  1. Tell Next.js how many pages there are and
  2. For each individual page; get the data for that page.

We can achieve these with the help of getStaticPaths and getStaticProps functions respectively as shown in the snippet below

//Tell Next.js how many pages there are
export async function getStaticPaths() {
  const res = await fetch('http://localhost:1337/posts');
  const posts = await res.json();

  const paths = posts.map(post => ({
    params: { slug: post.slug }
  }));

  return {
    paths,
    fallback: false
  };
}

//For each individual page; get the data for that page
export async function getStaticProps({ params }) {
  const { slug } = params;
  const res = await fetch(`http://localhost:1337/posts?slug=${slug}`);
  const data = await res.json();
  const post = data[0];
  return {
    props: { post }
  };
}
Enter fullscreen mode Exit fullscreen mode

fallback in getStaticPaths is set to false because we have a small number of paths to pre-render - so they are all statically generated during build time. In getStaticProps, const post is assigned data[0] because the response we get is an array and we are only interested in the first element.

We can now add a Post component in [slug].js and pass the props as shown below

export default function Post({ post }) {
  return <div>{post.title}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Now when we route to http://localhost:3000/my-wins-this-week, we get a single post as shown below:
Alt Text
Awesome right?

So, to be able to generate our post files, we need to add the following script to our package.json

  "export": "next build && next export"
Enter fullscreen mode Exit fullscreen mode

Let's stop the server and run it

npm run export
Enter fullscreen mode Exit fullscreen mode

This generates HTML files from all the posts we have in Strapi.
These files are in the out folder and are minified and ready to be hosted on a static server.
Now we will add provision to navigate between the posts page and the single post page using the Next.js Link. Edit the return block of the Home component as shown below

<div className={styles.container}>
      {/* Loop over posts and show them */}
      {posts &&
        posts.map(post => (
          <Link href={`/${post.slug}`} key={post.id} className={styles.post}>
            <a>
              <h1>{post.title}</h1>
              <p>{post.content}</p>
              <em>By {post.user.username}</em>
            </a>
          </Link>
        ))}
    </div>
Enter fullscreen mode Exit fullscreen mode

and the return block for the Post component in[slug].js as follows

    <div>
      <Link href="/">
        <a>Home</a>
      </Link>
      <h1>{post.title}</h1>
    </div>
Enter fullscreen mode Exit fullscreen mode

Be sure to import Link from "next/link" in both cases.
Now, when we start the server, we should be able to navigate through our site.

The last thing we want to do is creating a post. For that we need an API exploration tool, in my case I'm using Postman. Let's create a POST request with the following body:

{
    "title": "The New Normal",
    "slug": "the-new-normal",
    "content": "The new normal is here",
    "user": {
        "id": 1
    }
}
Enter fullscreen mode Exit fullscreen mode

When we hit send, below is the post response we are getting
Alt Text
When we head over to our Strapi dashboard, we can see our new post added to the list of posts and when we click on it, we see the information passed as shown below.
Alt Text

That's it, our basic fullstack app is done.

Happy coding!

Top comments (3)

Collapse
 
leviathan91 profile image
Philipp Goemann • Edited

Hi Gerald,
I am currently watching Chris Talk as well and am loving it.

But I am having a hard time understanding what is happening around minute 31.

At this point, all the data being displayed to the clientside has been rendered at built time by using getStaticProps.

He then adds a new post, and this post is being displayed on the client-side - without rebuilding the app?

How does this work? Have I misunderstood what getStaticProps does?

Collapse
 
chris__sev profile image
Chris Sev

Amazing work!

Collapse
 
geraldmaboshe profile image
Gerald

Thanks, Chris. You are my inspiration