React
Strapi

NextJS + Strapi Portfolio

Next.js and Strapi: A Seamless Combination

Welcome to this article where we'll explore the powerful combination of Next.js and Strapi. Together, these technologies provide a seamless and efficient content management experience for web development.

Next.js with Server Side Generation (SSG):

  • Built upon React, Next.js offers a wide range of features.
  • SSG fetches data from the GraphQL Strapi endpoint during the server build.
  • Fast and optimized rendering for a smooth user experience.

Strapi as a Flexible Backend System:

  • Incorporates GraphQL and supports various databases like Postgres, MySQL, and SQLite.
  • Simplifies data fetching and handles schema aspects.

Integration with Apollo Client and Mantine:

  • Next.js seamlessly communicates with the backend through Apollo client and GraphQL endpoint.
  • Mantine component library enhances visual appeal and user interface.

Contact Form and Email Handling with Nodemailer:

  • Simplifies sending emails and ensures reliable communication with users.

Cost-Effective and Efficient Hosting Setup:

  • Strapi hosted on an AWS free-tier Ubuntu server as a Docker image in the Elastic Container Registry (ECR).
  • Next.js hosted on Vercel's free-tier plan with impressive performance.
  • Database hosted on Railway's exceptional free-tier offering.

This cost-effective and efficient hosting setup allows us to maintain a high-quality website while keeping costs at a minimum. In the following sections, we'll delve into the details and benefits of this setup, helping you make informed choices for your own projects.

How Next Works?

Next.js, a framework built upon React, offers a powerful feature set that enables interaction with data and allows for procedures to be executed before rendering pages and components. This functionality is achieved through two server-side functions: getServerSideProps and getStaticProps. These functions are specifically designed to work within components, or in the case of Next.js, pages, that reside inside the "pages" folder of your project.

Let's take a look at an example that demonstrates a simple fetch operation using Server-Side Rendering (SSR):

import React from 'react';

function MyPage({ data }) {
  // Use the fetched data to render your page
  return (
    <div>
      {/* Render your components */}
    </div>
  );
}

export async function getServerSideProps() {
  // Fetch data from an external API
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();

  // Pass the fetched data as props to your page component
  return {
    props: {
      data,
    },
  };
}

export default MyPage;

By utilizing the getServerSideProps function, you can fetch data from an external API and pass it as props to your page component. This approach offers two significant benefits. Firstly, you can ensure that sensitive data remains on the server-side, preventing any leakage to the client-side. Secondly, your responses can be faster since the data is fetched and processed during the server-side rendering process.

Now, let's explore the folder structure that we have set up for our project:

  • Components: This folder contains all the reusable elements that compose our pages. These components are modular and can be utilized across different pages.

  • GraphQL: Within this folder, we store the GraphQL queries and the GraphQL client configuration. This allows us to easily import the client configuration within our page functions, making it straightforward to perform GraphQL queries.

  • Pages: The "pages" folder represents the most complex part of the project's folder structure. In Next.js, routing is handled by the folder structure itself. For example, an URL like localhost:3000/projects points to the pages/projects/index.js file. Additionally, we can use a slug parameter to access individual project pages. By defining a slug using brackets, such as pages/projects/[slug].js, we can read the slug value through the getServerSideProps context and fetch the corresponding project.

For the component library, we have opted for Mantine out of curiosity. While using Tailwind CSS might result in more maintainable code, we wanted to try out something new. Working with Mantine has been a pleasant experience, and you can explore its documentation here.

In summary, Next.js is a simple yet powerful framework that can yield incredible results. Its ability to interact with data and execute procedures before rendering pages and components, coupled with the convenient folder structure, provides a seamless development experience.

Data Fetching with Apollo and GraphQL using getServerSideProps

Next.js seamlessly integrates with Apollo Client, enabling efficient communication between the frontend and the backend using GraphQL queries. By utilizing the getServerSideProps function in Next.js, we can fetch data from the GraphQL endpoint and pass it as props to our page component.

To demonstrate this, let's take a look at an example:

import React from 'react';
import { apolloClient } from '@/graphql/apolloClient'
import { GET_ALL_PROJECTS } from '@/graphql/queries'

function Projects({ projects }) {
  // Use the fetched data to render your page
  return (
        <Container>
          <Grid>
            {projects.map((project) => 
              <Grid.Col key={project.attributes.urlSlug} md={12} lg={4}>
                <ProjectCard 
                  urlSlug={project.attributes.urlSlug} 
                  title={project.attributes.title}
                  description={project.attributes.description} 
                  headerImage={strapi + project.attributes.cover.data?.attributes.url}/>
              </Grid.Col>
            )}
          </Grid>
        </Container>
  );
}

export async function getServerSideProps() {
  const { data } = await apolloClient.query({
    query: GET_ALL_PROJECTS
  })

  // Pass the fetched data as props to your page component
  return {
    props: {
      projects: data.projects.data,
    },
  };
}

export default Projects;

In this example, we import the necessary dependencies: React, our query, and our Apollo Client instance client. Inside the getServerSideProps function, we use apolloClient.query to perform the GraphQL query and retrieve the data.

Once the data is fetched, it is passed as props to the Projects component, where you can utilize it to render your page.

By leveraging Apollo Client and GraphQL with getServerSideProps, you can seamlessly fetch and utilize data from your backend within Next.js, enabling dynamic and data-driven rendering for your pages.

How Strapi Works

I'll not go deep into how Strapi works behind the scenes, I want to focus on how it works for our use case and how to use it.

When you create a new Strapi project, you're presented with a login page. This first login will be your admin login. Then, you're presented with the Strapi Dashboard. The logic behind is pretty simple: We create entities and relationships just like any other back-end framework, but instead using code we create it through the panel. The idea is that when you are creating the entities, Strapi is handling the schema behind the scenes. If you create a new content-type, it stores the table information with all the fields and relationships you selected for it, in the /api/ folder. You can check out how it works by exploring it, but basically is a JSON representing the schema and wrappers to create routes, controllers and services. It's pretty neat I must say.