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, andapi/
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.