How to Combine Next.js and Go

In modern web development, developers are primarily tasked with creating applications that not only load quickly but also provide a smooth user experience. The combination of Next.js and Go offers a powerful solution for this task.

Next.js, with its capabilities of static generation and server-side rendering, allows for the creation of high-performance interfaces that are easily optimized for search engines. Go, in turn, provides a reliable backend capable of handling numerous requests simultaneously.

In this article, we will explore how integrating these two technologies can help you build dynamic websites.

Creating a Static Website in Next.js

First, let’s create a new project using the create-next-app command.

Open a terminal and run the following command:

npx create-next-app my-static-site
cd my-static-site

This command will create a new directory called my-static-site where the project will be located. Next, move into this directory.

After initializing the project, you will see the following structure:

my-static-site/
├── node_modules/
├── public/
├── styles/
   └── Home.module.css
├── pages/
   ├── api/
   ├── _app.js
   ├── index.js
└── package.json

  • public/: This is where you store static files like images or fonts.
  • styles/: The folder for the CSS styles of your application.
  • pages/: This is the heart of the application, where each JavaScript file corresponds to a route. For example, index.js is the root route, and api/ is for API routes.

Now, let’s implement static generation using the getStaticProps and getStaticPaths methods.

Static Generation Methods

  • getStaticProps: This allows you to fetch data at build time, meaning the pages will be pre-generated with data. This can improve both SEO and load speed.

Example usage of getStaticProps:

// pages/index.js

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}

const Home = ({ data }) => {
  return (
    <div>
      <h1>Static Site on Next.js</h1>
      <ul>
        {data.map((item) => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default Home

Here, we make an API request and pass the data into the component as props.

  • getStaticPaths: This is used for dynamic page generation based on route parameters.

Example usage of getStaticPaths:

// pages/posts/[id].js

export async function getStaticPaths() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));

  return { paths, fallback: false };
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();

  return {
    props: {
      post,
    },
  };
}

const Post = ({ post }) => {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
};

export default Post;

In this example, pages for each post are generated based on API data. getStaticPaths creates an array of paths, and getStaticProps fetches data for a specific post.

Dynamic Data Loading via API Routes

Next, we will create an API route that returns data, which can be integrated into the site.

Create the file pages/api/posts.js:

// pages/api/posts.js

export default async function handler(req, res) {
  const response = await fetch('https://api.example.com/posts');
  const posts = await response.json();

  res.status(200).json(posts);
}

Now, you have an API that returns a list of posts, which you can use in the application.

To load data asynchronously, you can use fetch in useEffect within a component:

import { useEffect, useState } from 'react';

const DynamicContent = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch('/api/posts');
      const data = await res.json();
      setPosts(data);
    };

    fetchData();
  }, []);

  return (
    <div>
      <h2>Dynamic Posts</h2>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default DynamicContent;

Integrating with Go: Backend Setup

Add Gin to your dependencies:

go get -u github.com/gin-gonic/gin

Now, let’s create the API. Create the file main.go and add the following code:

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type Post struct {
    ID    string `json:"id"`
    Title string `json:"title"`
    Body  string `json:"body"`
}

var posts = []Post{
    {ID: "1", Title: "Post 1", Body: "This is the body of post 1."},
    {ID: "2", Title: "Post 2", Body: "This is the body of post 2."},
}

func main() {
    r := gin.Default()

    r.GET("/api/posts", getPosts)
    r.GET("/api/posts/:id", getPostByID)

    r.Run(":8080") // Start server on port 8080
}

func getPosts(c *gin.Context) {
    c.JSON(http.StatusOK, posts)
}

func getPostByID(c *gin.Context) {
    id := c.Param("id")
    for _, post := range posts {
        if post.ID == id {
            c.JSON(http.StatusOK, post)
            return
        }
    }
    c.JSON(http.StatusNotFound, gin.H{"message": "post not found"})
}

We created two routes: one to get all posts and another to get a post by its ID. If a post is not found, we return a 404 status and an error message.

Connecting the API with Next.js

Let’s fetch the list of posts in the Next.js app. Open the pages/index.js file of your Next.js application and add the following code:

import { useEffect, useState } from 'react';

const Home = () => {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchPosts = async () => {
      try {
        const res = await fetch('http://localhost:8080/api/posts');
        if (!res.ok) throw new Error('Error fetching data');
        const data = await res.json();
        setPosts(data);
      } catch (error) {
        console.error('Error:', error);
      }
    };

    fetchPosts();
  }, []);

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default Home;

Setting Up CORS for Go

To allow Next.js to communicate with the Go API, we need to set up CORS. This can be done using built-in middleware in Gin.

Add the following line in main.go:

r.Use(cors.Default())

You’ll need to install the CORS package:

go get -u github.com/gin-contrib/cors

Now, the API can handle requests from the Next.js application.