Golang

How to Build a REST API in Golang (Step-by-Step Guide for Beginners)

We will generate the REST API  for blog application in golang.

1. Prerequisites

  • Go installed (go version)
  • Git installed
  • PostgreSQL installed (optional for database use)
  • Basic understanding of HTTP & JSON 

2. Create Project Structure

mkdir go-api-example
cd go-api-example
go mod init github.com/yourname/go-api-example

  • go mod init is used to initialize a new Go module in your project directory.
  • github.com/yourname/go-api-example is the module path, typically a repository path (like GitHub). It's used as the import path for your code and any sub-packages

What Gets Created:

A new file named go.mod is created in the root of your project directory . It will contain something like:

module github.com/yourname/go-api-example

go 1.21 // (or whatever your current Go version is)

This go.mod file:

  • Declares the module path.
  • Specifies the Go version your project is using.
  • Will later list all dependencies (external packages) your code uses.

Why Use Modules?

Go modules help you:

  • Manage dependencies (versions of external packages).
  • Avoid issues with GOPATH and legacy $GOPATH/src structure.
  • Easily fetch and upgrade dependencies using go get, go mod tidy, etc.

  you can also run the command

go mod tidy

Which:

  • Scans your code.
  • Downloads any missing dependencies.
  • Removes unused ones.
  • Updates your go.mod and creates a go.sum file (used to verify the integrity of downloaded modules).

i.e Cleans up and ensures dependencies are downloaded and correctly listed.

3. Install Required Packages

go get github.com/gorilla/mux
go get github.com/joho/godotenv
go get github.com/lib/pq

go get github.com/gorilla/mux

This installs the Gorilla Mux package, which is a powerful HTTP router and URL matcher for Go.

Why Use It:

  • It allows you to define routes with parameters like /users/{id}.
  • Supports:
    • Route variables (/user/{id})
    • Middleware chaining
    • Hostname/Method matching
    • Schemes (http/https)

Example:

r := mux.NewRouter()
r.HandleFunc("/users/{id}", getUserHandler).Methods("GET")
http.ListenAndServe(":8000", r)

 

go get github.com/joho/godotenv

This installs the godotenv package, used for loading environment variables from a .env file.

Why Use It:

  • You can keep sensitive config like API keys, DB credentials, etc., out of your code.
  • Easy to switch environments (development, production, etc.)

Example:

err := godotenv.Load()
dbHost := os.Getenv("DB_HOST")

 

go get github.com/lib/pq

This installs the PostgreSQL driver for Go's database/sql package.

 Why Use It:

  • Enables your Go application to connect to and query a PostgreSQL database.
  • You can use it with standard SQL operations like SELECT, INSERT, etc.

Connect and interact with PostgreSQL DB

Example:

import (
    "database/sql"
    _ "github.com/lib/pq"
)

connStr := "user=username dbname=mydb sslmode=disable"
db, err := sql.Open("postgres", connStr)

4. Project Structure Example

go-api-example/

  main.go
  .env
  go.mod
  go.sum
  handlers/
     article.go
  models/
     article.go
  db/
     connection.go

5. Create .env

DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=yourpassword
DB_NAME=goapi

6. Create Database Connection (db/connection.go)

package db

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/lib/pq"
	"github.com/joho/godotenv"
)

var DB *sql.DB

func Init() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
	}

	connStr := fmt.Sprintf(
		"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
		os.Getenv("DB_HOST"), os.Getenv("DB_PORT"),
		os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"),
		os.Getenv("DB_NAME"))

	DB, err = sql.Open("postgres", connStr)
	if err != nil {
		log.Fatal(err)
	}

	if err = DB.Ping(); err != nil {
		log.Fatal(err)
	}

	fmt.Println("? Connected to the database")
}

7. Create Model (models/article.go)

package models

type Article struct {
	ID      int    `json:"id"`
	Title   string `json:"title"`
	Content string `json:"content"`
}

8. Create Handlers (handlers/article.go)

package handlers

import (
	"database/sql"
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/yourname/go-api-example/db"
	"github.com/yourname/go-api-example/models"
)

func GetArticles(w http.ResponseWriter, r *http.Request) {
	rows, err := db.DB.Query("SELECT id, title, content FROM articles")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var articles []models.Article
	for rows.Next() {
		var a models.Article
		err := rows.Scan(&a.ID, &a.Title, &a.Content)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		articles = append(articles, a)
	}

	json.NewEncoder(w).Encode(articles)
}

func GetArticle(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	var a models.Article
	err := db.DB.QueryRow("SELECT id, title, content FROM articles WHERE id=$1", id).
		Scan(&a.ID, &a.Title, &a.Content)
	if err == sql.ErrNoRows {
		http.NotFound(w, r)
		return
	} else if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	json.NewEncoder(w).Encode(a)
}

func CreateArticle(w http.ResponseWriter, r *http.Request) {
	var a models.Article
	_ = json.NewDecoder(r.Body).Decode(&a)

	err := db.DB.QueryRow("INSERT INTO articles(title, content) VALUES($1, $2) RETURNING id",
		a.Title, a.Content).Scan(&a.ID)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	json.NewEncoder(w).Encode(a)
}

func UpdateArticle(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	var a models.Article
	_ = json.NewDecoder(r.Body).Decode(&a)

	_, err := db.DB.Exec("UPDATE articles SET title=$1, content=$2 WHERE id=$3",
		a.Title, a.Content, id)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	a.ID, _ = strconv.Atoi(id)
	json.NewEncoder(w).Encode(a)
}

func DeleteArticle(w http.ResponseWriter, r *http.Request) {
	params := mux.Vars(r)
	id := params["id"]

	_, err := db.DB.Exec("DELETE FROM articles WHERE id=$1", id)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

9. Main File (main.go)

package main

import (
	"log"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/yourname/go-api-example/db"
	"github.com/yourname/go-api-example/handlers"
)

func main() {
	db.Init()

	r := mux.NewRouter()

	r.HandleFunc("/articles", handlers.GetArticles).Methods("GET")
	r.HandleFunc("/articles/{id}", handlers.GetArticle).Methods("GET")
	r.HandleFunc("/articles", handlers.CreateArticle).Methods("POST")
	r.HandleFunc("/articles/{id}", handlers.UpdateArticle).Methods("PUT")
	r.HandleFunc("/articles/{id}", handlers.DeleteArticle).Methods("DELETE")

	log.Println("Server started at http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", r))
}

10. SQL to Create Table

CREATE TABLE articles (
    id SERIAL PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL
);

11. Run the App

go run main.go

Open browser or use tools like Postman, curl, or Thunder Client to test your API.

Example API Endpoints

  • GET /articles ? List all
  • GET /articles/1 ? Get one
  • POST /articles ? Create
  • PUT /articles/1 ? Update
  • DELETE /articles/1 ? Delete

 

 

 

 

 


About author

author image

Amrit panta

Fullstack developer, content creator



Scroll to Top