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):
Strapi as a Flexible Backend System:
Integration with Apollo Client and Mantine:
Contact Form and Email Handling with Nodemailer:
Cost-Effective and Efficient Hosting Setup:
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.
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.
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.
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.