Golang CRUD REST API with MongoDB

This tutorial will guide you through creating a CRUD (Create, Read, Update, Delete) API using Golang and MongoDB. We'll test all the REST APIs using Postman.

Prerequisites

  1. Go installed on your machine (latest version).
  2. MongoDB installed and running.
  3. Postman for testing APIs.

Step 1: Set Up Your Go Project

  1. Create a new directory for your project and navigate into it:

    mkdir go-crud-api-mongodb
    cd go-crud-api-mongodb
    
  2. Initialize a new Go module:

    go mod init go-crud-api-mongodb
    

Step 2: Install Required Packages

Install the necessary packages for handling MongoDB and HTTP requests:

go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/mongo/options
go get github.com/gorilla/mux

Step 3: Set Up the MongoDB Database

  1. Start MongoDB and create a new database and collection:

    use go_crud_api;
    
    db.createCollection("users");
    

Step 4: Create the Main Application File

Create a new file main.go and set up the basic structure:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/mux"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// User represents the model for our resource
type User struct {
	ID        primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
	Name      string             `json:"name,omitempty" bson:"name,omitempty"`
	Email     string             `json:"email,omitempty" bson:"email,omitempty"`
	CreatedAt time.Time          `json:"created_at,omitempty" bson:"created_at,omitempty"`
}

var client *mongo.Client

func main() {
	// Initialize MongoDB client
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
	client, _ = mongo.Connect(ctx, clientOptions)

	router := mux.NewRouter()
	router.HandleFunc("/users", getUsers).Methods("GET")
	router.HandleFunc("/user/{id}", getUser).Methods("GET")
	router.HandleFunc("/user", createUser).Methods("POST")
	router.HandleFunc("/user/{id}", updateUser).Methods("PUT")
	router.HandleFunc("/user/{id}", deleteUser).Methods("DELETE")

	log.Fatal(http.ListenAndServe(":8000", router))
}

// getUsers fetches all users from the database
func getUsers(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var users []User
	collection := client.Database("go_crud_api").Collection("users")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	cursor, err := collection.Find(ctx, bson.M{})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer cursor.Close(ctx)
	for cursor.Next(ctx) {
		var user User
		cursor.Decode(&user)
		users = append(users, user)
	}
	json.NewEncoder(w).Encode(users)
}

// getUser fetches a single user by ID from the database
func getUser(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)
	id, _ := primitive.ObjectIDFromHex(params["id"])
	var user User
	collection := client.Database("go_crud_api").Collection("users")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	err := collection.FindOne(ctx, bson.M{"_id": id}).Decode(&user)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(user)
}

// createUser creates a new user in the database
func createUser(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var user User
	_ = json.NewDecoder(r.Body).Decode(&user)
	user.ID = primitive.NewObjectID()
	user.CreatedAt = time.Now()
	collection := client.Database("go_crud_api").Collection("users")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	_, err := collection.InsertOne(ctx, user)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	json.NewEncoder(w).Encode(user)
}

// updateUser updates an existing user in the database
func updateUser(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)
	id, _ := primitive.ObjectIDFromHex(params["id"])
	var user User
	_ = json.NewDecoder(r.Body).Decode(&user)
	collection := client.Database("go_crud_api").Collection("users")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	update := bson.M{
		"$set": user,
	}
	_, err := collection.UpdateOne(ctx, bson.M{"_id": id}, update)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	user.ID = id
	json.NewEncoder(w).Encode(user)
}

// deleteUser deletes a user from the database
func deleteUser(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	params := mux.Vars(r)
	id, _ := primitive.ObjectIDFromHex(params["id"])
	collection := client.Database("go_crud_api").Collection("users")
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	_, err := collection.DeleteOne(ctx, bson.M{"_id": id})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

Explanation of the Code and REST APIs

  1. Package and Imports

    • The main package is used, and necessary packages are imported.
    • go.mongodb.org/mongo-driver/mongo is used for MongoDB interactions.
    • github.com/gorilla/mux is used for routing.
  2. User Struct

    • Represents the User model with BSON tags for MongoDB and JSON tags for marshalling/unmarshalling.
  3. Global Client Variable

    • client is a global variable to hold the MongoDB client.
  4. Main Function

    • Initializes the MongoDB client.
    • Creates a new router using mux.NewRouter().
    • Defines the REST endpoints and associates them with handler functions.
    • Starts the HTTP server on port 8000.
  5. getUsers Function

    • Retrieves all users from the users collection.
    • Encodes the result as JSON and writes it to the response.
  6. getUser Function

    • Retrieves a single user by ID.
    • Returns a 404 error if the user is not found.
  7. createUser Function

    • Decodes the JSON request body to a User struct.
    • Inserts the new user into the users collection.
    • Returns the created user as JSON.
  8. updateUser Function

    • Decodes the JSON request body to a User struct.
    • Updates the user with the specified ID in the users collection.
    • Returns the updated user as JSON.
  9. deleteUser Function

    • Deletes the user with the specified ID from the users collection.
    • Returns a 204 No Content status on successful deletion.

Testing with Postman

  1. Get All Users

    • Method: GET
    • URL: http://localhost:8000/users
    • Response: JSON array of users.
  2. Get a Single User

    • Method: GET
    • URL: http://localhost:8000/user/{id}
    • Response: JSON object of the user.
  3. Create a New User

    • Method: POST
    • URL: http://localhost:8000/user
    • Body:
      {
        "name": "John Doe",
        "email": "john@example.com"
      }
      
    • Response: JSON object of the created user.
  4. Update an Existing User

    • Method: PUT
    • URL: http://localhost:8000/user/{id}
    • Body:
      {
        "name": "Jane Doe",
        "email": "jane@example.com"
      }
      
    • Response: JSON object of the updated user.
  5. Delete a User

    • Method: DELETE
    • URL: http://localhost:8000/user/{id}
    • Response: 204 No Content status.

Comments