How to use NextJS with Golang
Reading Time • 5 min read
How to use NextJS with Golang
How to use NextJS with Golang

If you want a scalable, high-performance backend with a fast-loading frontend, this guide is for you.

Let's introduce our technologies:

  • Go is a backend language which has been tremendously popular since being launched by Google 10 years ago. It's simple to learn, has powerful concurrency primitives, and can deploy anywhere as a single binary.
  • NextJS is a full-stack framework based on React, it gives you the benefits of react without the performance costs, since it lets you easily convert webpages to HTML and launch them around the world.

Thousands of major companies like Netflix and Notion use Go and NextJS in their stacks, and by the end of this guide you'll be able to as well.

Setup

Installing NodeJS and Go

You'll have to download and install two different tools before we can get started. They're available for all of the major operating systems.

Download | Node.js
Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine.
Downloads - The Go Programming Language

Creating the project structure

Next, you'll have to create directories in the following structure:

We'll set up NextJS in the frontend - run their installation command to create an app there:

# create the NextJS base app in the frontend directory
user@computer:my-project/services$ npx create-next-app@latest
Need to install the following packages:
  create-next-app@latest
Ok to proceed? (y) 
✔ What is your project named? … frontend
✔ Would you like to use TypeScript with this project? … (No) / Yes
✔ Would you like to use ESLint with this project? … No / (Yes)
Creating a new Next.js app in /home/user/my-project/services/frontend.

# remove the .git directory and the pages/api directory from our frontend - we won't be using them.
user@computer:my-project/services$ rm -rf frontend/.git frontend/pages/api

Next, create a simple Go HTTP server at services/backend/main.go

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		resp := []byte(`{"status": "ok"}`)
		rw.Header().Set("Content-Type", "application/json")
		rw.Header().Set("Content-Length", fmt.Sprint(len(resp)))
		rw.Write(resp)
	})

	log.Println("Server is available at http://localhost:8000")
	log.Fatal(http.ListenAndServe(":8000", handler))
}

Finally, we should be able to start both services (at different ports) by running two commands in two different terminals:

user@computer:my-project/services/frontend$ npm run dev
> [email protected] dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
event - compiled client and server successfully in 1718 ms (161 modules)
user@computer:my-project/services/backend$ go run main.go
Server is available at http://localhost:8000

We can verify everything is set up correctly by visiting the two printed URLs from each step.

Finally (and optionally,) let's commit and push our changes to GitHub:

user@computer:my-project$ git init
Initialized empty Git repository in /home/user/my-project/.git/

user@computer:my-project$ git remote add origin [email protected]:My-Github-User/my-project

user@computer:my-project$ git add services

user@computer:my-project$ git commit -m 'First commit'

user@computer:my-project$ git push origin master

At this point, my repository looked like this.

Connecting your frontend with your backend

NextJS uses React.js to generate HTML. You can sort of think of it as a template that runs both on the frontend and the backend.

This means that we can fetch data from our backend, insert it into our frontend, and then send HTML all at once.

Edit the file at services/frontend/pages/index.js and make its contents be the following;

import Head from 'next/head'
import styles from '../styles/Home.module.css'

export async function getServerSideProps() {
  const res = await fetch("http://localhost:8000").then(x => x.json());
  return {
    props: {
      status: res.status, //should be "ok"
    }
  }
}

export default function Home({status}) {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <div>Status is: {status}</div>
      </main>
    </div>
  )
}

If you refresh your frontend, you'll notice that the {"status": "ok"} we are sending from our backend gets pulled in via the getServerSideProps call, and automatically templated into {status}:

Let's add another route in our backend at services/backend/main.go :

package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		var resp []byte
		if req.URL.Path == "/status" {
			resp = []byte(`{"status": "ok"}`)
		} else if req.URL.Path == "/username" {
			resp = []byte(`{"username": "colin"}`)
		} else {
			rw.WriteHeader(http.StatusNotFound)
			return
		}
		
		rw.Header().Set("Content-Type", "application/json")
		rw.Header().Set("Content-Length", fmt.Sprint(len(resp)))
		rw.Write(resp)
	})

	log.Println("Server is available at http://localhost:8000")
	log.Fatal(http.ListenAndServe(":8000", handler))
}

Re-start the backend (going to the terminal running the backend, pressing control+c, and re-running go run main.go)

Finally, modify our frontend at services/frontend/pages/index.js to reference our second route:

export async function getServerSideProps() {
  const {status} = await fetch("http://localhost:8000/status").then(x => x.json());
  const {username} = await fetch("http://localhost:8000/username").then(x => x.json());
  return {
    props: {
      status: status,
      username: username,
    }
  }
}

export default function Home({status, username}) {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>
        <div>Status is: {status}, your username is: {username}</div>
      </main>
    </div>
  )
}

Finally, commit these changes as well:

user@computer:my-project$ git add services

user@computer:my-project$ git commit -m 'Add routes'

user@computer:my-project$ git push origin master

Next steps

Last Updated • Dec 12, 2022