DEV Community

Cover image for Creating a very simple blog with Next.js and Strapi
Diego Segura
Diego Segura

Posted on

Creating a very simple blog with Next.js and Strapi

Contents

  • Setup Strapi locally and deploy it to heroku
  • Data fetching strategies with Next.js
  • Create dynamic routes
  • Basic GraphQl
  • Static SSR
  • Deploy frontend to vercel

What you need first

  1. Github account
    Create a github account and generate a new ssh key. Then add it to your github account and you are ready.

  2. Code editor

  3. Heroku account

Create an account if you don't have one

  1. Install heroku CLI

  2. nodejs

  3. postgresql

I I installed it for Arch but you can google how to install it for your env

Rendering strategy

Check this out.

On this article i will focus on Static SSR strategy for this blog. I like it because it will fit in 90% use cases and i think its really performant.

Install Strapi with CLI

1. Login to Heroku from your CLI

Next, you need to login to Heroku from your computer.

heroku login

Follow the instructions and return to your command line.

2. Create a new Strapi project

yarn create strapi-app strapi-blog

When prompted, answer, on this order:

  • custom
  • postgres
  • my-blog-db

3. Init a Git repository and commit your project

Init the Git repository

cd my-project
git init

Add heroku remote

heroku git:remote -a blog-strapi-app

Your local development environment is now set-up and configured to work with Heroku. You have a new Strapi project and a new Heroku app ready to be configured to work with a database and with each other.

4. Heroku Database set-up

heroku addons:create heroku-postgresql:hobby-dev

5. Database credentials

The add-on automatically exposes the database credentials into a single environment variable accessible by your app. To retrieve it, type:

heroku config

This should print something like this: DATABASE_URL: postgres://ebitxebvixeeqd:dc59b16dedb3a1eef84d4999sb4baf@ec2-50-37-231-192.compute-2.amazonaws.com: 5432/d516fp1u21ph7b.

(This url is read like so: postgres:// USERNAME : PASSWORD @ HOST : PORT : DATABASE_NAME)

6. Set environment variables

Strapi expects a variable for each database connection configuration (host, username, etc.). So, from the url above, you have to set several environment variables in the Heroku config:

heroku config:set DATABASE_USERNAME=ebitxebvixeeqd
heroku config:set DATABASE_PASSWORD=dc59b16dedb3a1eef84d4999a0be041bd419c474cd4a0973efc7c9339afb4baf
heroku config:set DATABASE_HOST=ec2-50-37-231-192.compute-2.amazonaws.com
heroku config:set DATABASE_PORT=5432
heroku config:set DATABASE_NAME=d516fp1u21ph7b

Replace these above values with your actual values.

7. Update your database config file.

Replace the contents of config/database.js with the following:

module.exports = ({ env }) => ({
  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'postgres',
        host: env('DATABASE_HOST', '127.0.0.1'),
        port: env.int('DATABASE_PORT', 27017),
        database: env('DATABASE_NAME', 'my-blog-db'),
        username: env('DATABASE_USERNAME', ''),
        password: env('DATABASE_PASSWORD', ''),
      },
      options: {
        ssl: false,
      },
    },
  },
});

8. Deploy.

git add --all
git commit -m "update config"
git push heroku master

The deployment may take a few minutes. At the end, logs will display the url of your project (e.g. https://mighty-taiga-80884.herokuapp.com). You can also open your project using the command line:

heroku open

If you see the Strapi Welcome page, you have correctly set-up, configured and deployed your Strapi project on Heroku. You will now need to set-up your

Frontend

1. Create our Next.js app

We move to the parent folder as we dont want to mix strapi and frontend files and we create our app

cd ..
yarn create next-app

2. Routing

  • We create a file pages/article/[id].js which will be our article item page:
import Head from "next/head";

export default function ArticlePage() {
  return (
    <div>
      <Head>
        <title>This is an article title</title>
      </Head>
      <main>
        This is an article body
      </main>

    </div>
  );
}
  • We empty the contents of pages/index.js as its our home page and it will contiain our ArticleList in the future.
import styles from "../styles/Home.module.scss";

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Our great blog</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        The article list
      </main>
    </div>
  );
}
  • We create a 404 custom page by adding a pages/404.js with a PageNotFound component.
export default function PageNotFound() {
    return <div>oh oh...</div>
}

3. Create some structure

  • Create a modules folder at the root of our Next.js project
  • Create a modules/core module with two subfolders components and services.
  • Inside modules/core/components create a Header.js and a Footer.js files like:
import Link from "next/link";

export default function Header() {
  return (
    <header>
      <Link href="/">
        <a>Our awesome blog</a>
      </Link>
    </header>
  );
}
export default function Footer() {
  return <footer>® Awesome friends company</footer>;
}
  • Consume your components in app.js
import "../styles/globals.scss";
import Header from "../modules/core/components/Header";
import Footer from "../modules/core/components/Footer";

function MyApp({ Component, pageProps }) {
  return (
    <div>
      <Header></Header>
      <div className="route">
        <Component {...pageProps} />
      </div>
      <Footer></Footer>
    </div>
  );
}

export default MyApp;

  • Remove the folder routes/api as we are not using microservices for our example, it will be fully static.

  • Install SASS (optional)
    We are using sass because we want to be able of reusing variables for colors.

yarn add sass
  • Change all .css files to .scss (omit if you did not add sass)

  • Create a reset.scss file next to global.scss with your favourite css reset. (use .css if you didn't add sass)

  • Empty the contents of global.scss and import reset.scss like:

@import 'reset'
  • Create a file variables.scss next to global.scss and define your colors, breakpoints or whatever variables you need to share here. For our case, i picked a random palette from here

variables.scss

$charcoal: #264653;
$persianGreen: #2A9D8F;
$orangeYellowCrayola: #E9C46A;
$sandyBrown: #F4A261;
$burntSienna: #E76F51;
  • Empty the contents of Home.module.scss to leave it like
.container {
  display: block;
}

.main {
  display: block;
}

You can style it from here in the future.

Data fetching

Launch your strapi in local. For this, go to the folder where you installed strapi and type:

yarn develop
  • Log into your admin account in http://localhost:1337/admin

create an article content type and add the following fields:

  • canonical (will be our permalink or pathname)
  • description (long text)
  • title (short text)
  • body (rich text)

save everything and open the strapi folder in your text editor.

  • Create a schema.graphql.js inside api/article/config with the following:
const { sanitizeEntity } = require('strapi-utils');

module.exports = {
  query: `
    articleByCanonical(canonical: ID): Article
  `,
  resolver: {
    Query: {
      articleByCanonical: {
        resolverOf: 'Article.findOne',
        async resolver(_, { canonical }) {
          const entity = await strapi.services.article.findOne({ canonical });
          return sanitizeEntity(entity, { model: strapi.models.article });
        },
      },
    },
  },
};

And save everything.

  • Now, on your frontend project add the apollo client dependency (remember to cd frontend dir to install the dependency in the frontend package.json)
yarn add @apollo/client
  • Create a .env.local file with:
STRAPI_HOST="http://localhost:1337"
  • Create a service inside core/services/apolloClient.service.js where we will export our apollo client instance.
import { ApolloClient, InMemoryCache } from '@apollo/client'

export default new ApolloClient({
    uri: `${process.env.STRAPI_HOST}/graphql`,
    cache: new InMemoryCache()
})
  • Create a new module article with components and services folders as we did with core.

Create ArticleList and Article components inside article/components/ArticleList.js and article/components/Article.js with the following:

export default function Article({ title, body }) {
  return <div>
    <h1>{title}</h1>
    <div>{body}</div>
  </div>;
}

And

import Link from "next/link";

export default function ArticleList({ articles }) {
  return articles.length ? (
    <ol>
      {articles.map((article) => (
        <li key={article.id}>
          <Link href={`article/${article.canonical}`}>
            <h2>{article.title}</h2>
          </Link>
          <p>{article.description}...</p>
        </li>
      ))}
    </ol>
  ) : (
    <div>There are no articles</div>
  );
}
  • Create the articles service in article/services/article.service.js
import { gql } from "@apollo/client";
import apolloClient from "../../core/services/apolloClient.service";

export async function getAll() {
  return apolloClient
    .query({
      query: gql`
        {
          articles {
            canonical
            description
            id
            created_at
            title
          }
        }
      `,
    })
    .then((result) => result.data.articles);
}

export async function getByCanonical(canonical) {
  return apolloClient
    .query({
      query: gql`
        {
          articleByCanonical(canonical: "${canonical}") {
            title
            body
          }
        }
      `,
    })
    .then((result) => {
      console.log(result.data)
      return result.data.articleByCanonical;
    });
}
  • Finally, we add the fetching logic inside our routes and we consume these components:

pages/index.js

import Head from "next/head";
import ArticleList from "../modules/article/components/ArticleList";
import { getAll } from "../modules/article/services/article.service";

export default function HomePage({ articles }) {
  return (
    <div className={styles.container}>
      <Head>
        <title>Our great blog</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <ArticleList articles={articles}></ArticleList>
      </main>
    </div>
  );
}

export async function getStaticProps(context) {
  return {
    props: { articles: await getAll() },
  };
}

And change pages/article/[id].js

import marked from "marked";

import Article from "../../modules/article/components/Article";
import {
  getAll,
  getByCanonical,
} from "../../modules/article/services/article.service";
import Head from "next/head";

export default function ArticlePage({ article }) {
  return (
    <div>
      <Head>
        <title>{article.title}</title>
      </Head>
      <main>
        <Article {...article}></Article>
      </main>

    </div>
  );
}

export async function getStaticPaths() {
  const articles = await getAll();

  const paths = articles.map((article) => ({
    params: { id: article.canonical },
  }));

  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const article = await getByCanonical(params.id);

  return { props: { article } };
}

Add styles

  • You can create any file .module.scss next to your component and your css classes will be mapped to an object you can import like
// foo.module.scss
.container {
  display: block;
}

// foo.js
import styles from './foo.module.scss'

function Foo() {
  return <div className={styles.container}>My Foo styled component</div>
}

Deploy

  • For strapi deployment just go to your strapi project in a terminal and commit and push everything:
git add --all
git commit -m "my changes"
git push heroku master
  • For the frontend: frontend path and push your changes.
  • In package.json change the build command for next build && next export. This will generate a static build into /out folder. That's all we need to serve our site. To test it locally just run npx serve out/
  • Then create a github repo and add your remote
  • Then go to vercel.com and import your github project
  • Remember to add your STRAPI_HOST env variable in vercel to point to your heroku database
  • Follow the wizard and your app will be deployed

Updates

  • Create a webhook in vercel that will provide you an url
  • Log into your production strapi environment and in settings > webhooks create a new webhook with the provided url.
  • Now every time you update your production database your blog will be rebuilt and redeployed.

Demo and Code

Latest comments (0)