DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. Build a RESTful API in Go and MongoDB

Build a RESTful API in Go and MongoDB

Learn the steps for building your own RESTful API using Go and MongoDB, and a demonstration of how to navigate and use it, in this tutorial.

Mohamed Labouardy user avatar by
Mohamed Labouardy
·
Dec. 15, 17 · Tutorial
Like (18)
Save
Tweet
Share
23.57K Views

Join the DZone community and get the full member experience.

Join For Free

In this post, I will illustrate how you can build your own RESTful API in Go and MongoDB. All the code used in this demo can be found on my GitHub.

1 — API Specification

The REST API service will expose endpoints to manage a store of movies. The operations that our endpoints will allow are:

2 — Fetching Dependencies

Before we begin, we need to get the packages we need to set up the API:

go get github.com/BurntSushi/toml gopkg.in/mgo.v2 github.com/gorilla/mux
  • toml: Parse the configuration file (MongoDB server & credentials)

  • mux: Request router and dispatcher for matching incoming requests to their respective handler

  • mgo: MongoDB driver

3 — API Structure

Once the dependencies are installed, we create a file called "app.go," with the following content:

package main

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

    "github.com/gorilla/mux"
)

func AllMoviesEndPoint(w http.ResponseWriter, r * http.Request) {
    fmt.Fprintln(w, "not implemented yet !")
}

func FindMovieEndpoint(w http.ResponseWriter, r * http.Request) {
    fmt.Fprintln(w, "not implemented yet !")
}

func CreateMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    fmt.Fprintln(w, "not implemented yet !")
}

func UpdateMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    fmt.Fprintln(w, "not implemented yet !")
}

func DeleteMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    fmt.Fprintln(w, "not implemented yet !")
}

func main() {
    r: = mux.NewRouter()
    r.HandleFunc("/movies", AllMoviesEndPoint).Methods("GET")
    r.HandleFunc("/movies", CreateMovieEndPoint).Methods("POST")
    r.HandleFunc("/movies", UpdateMovieEndPoint).Methods("PUT")
    r.HandleFunc("/movies", DeleteMovieEndPoint).Methods("DELETE")
    r.HandleFunc("/movies/{id}", FindMovieEndpoint).Methods("GET")
    if err: = http.ListenAndServe(":3000", r);err != nil {
        log.Fatal(err)
    }
}


The code above creates a controller for each endpoint, then expose an HTTP server on port 3000.

Note: We are using GET, POST, PUT, and DELETEwhere appropriate. We are also defining parameters that can be passed in.

To run the server in local, type the following command:

go run app.go

If you point your browser to http://localhost:3000/movies, you should see:

4 — Model

Now that we have a minimal application, it’s time to create a basic Movie model. In Go, we use the struct keyword to create a model:

type Movie struct {
    ID bson.ObjectId `bson:"_id" json:"id"`
    Name string `bson:"name" json:"name"`
    CoverImage string `bson:"cover_image" json:"cover_image"`
    Description string `bson:"description" json:"description"`
}

Next, we will create the Data Access Object to manage database operations.

5 — Data Access Object

5.1 — Establish Connection

package dao

import (
"log"

"github.com/mlabouardy/movies-restapi/models"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)

type MoviesDAO struct {
Server   string
Database string
}

var db *mgo.Database

const (
COLLECTION = "movies"
)

func (m *MoviesDAO) Connect() {
session, err := mgo.Dial(m.Server)
if err != nil {
log.Fatal(err)
}
db = session.DB(m.Database)
}

The connect() method as its name implies, establish a connection to MongoDB database.

5.2 — Database Queries

The implementation is relatively straightforward and just includes issuing right method using db.C(COLLECTION) object and returning the results. These methods can be implemented as follows:

func(m * MoviesDAO) FindAll()([] Movie, error) {
    var movies[] Movie
    err: = db.C(COLLECTION).Find(bson.M {}).All( & movies)
    return movies, err
}

func(m * MoviesDAO) FindById(id string)(Movie, error) {
    var movie Movie
    err: = db.C(COLLECTION).FindId(bson.ObjectIdHex(id)).One( & movie)
    return movie, err
}

func(m * MoviesDAO) Insert(movie Movie) error {
    err: = db.C(COLLECTION).Insert( & movie)
    return err
}

func(m * MoviesDAO) Delete(movie Movie) error {
    err: = db.C(COLLECTION).Remove( & movie)
    return err
}

func(m * MoviesDAO) Update(movie Movie) error {
    err: = db.C(COLLECTION).UpdateId(movie.ID, & movie)
    return err
}

6 — Set Up API Endpoints

6.1 — Create a Movie

Update the CreateMovieEndpoint method as follows:

func CreateMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    defer r.Body.Close()
    var movie Movie
    if err: = json.NewDecoder(r.Body).Decode( & movie);
    err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    movie.ID = bson.NewObjectId()
    if err: = dao.Insert(movie);
    err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondWithJson(w, http.StatusCreated, movie)
}

It decodes the request body into a movie object, assigns it an ID, and uses the DAOInsert method to create a movie in the database.

Let’s test it out:

  • With Postman:

  • With cURL:

curl -sSX POST -d ‘{“name”:”dunkirk”,”cover_image”:”https://image.tmdb.org/t/p/w640/cUqEgoP6kj8ykfNjJx3Tl5zHCcN.jpg", “description”:”world war 2 movie”}’ http://localhost:3000/movies | jq ‘.’

6.2 — List of Movies

The code below is self-explanatory:

func AllMoviesEndPoint(w http.ResponseWriter, r * http.Request) {
    movies, err: = dao.FindAll()
    if err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondWithJson(w, http.StatusOK, movies)
}

It uses the FindAll method of DAO to fetch a list of movies from the database.

Let’s test it out:

  • With Postman:

  • With cURL:

curl -sSX GET http://localhost:3000/movies | jq ‘.’

6.3 — Find a Movie

We will use the mux library to get the parameters that the users passed in with the request:

func FindMovieEndpoint(w http.ResponseWriter, r * http.Request) {
    params: = mux.Vars(r)
    movie,
    err: = dao.FindById(params["id"])
    if err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid Movie ID")
        return
    }
    respondWithJson(w, http.StatusOK, movie)
}


Let’s test it out:

  • With Postman:

  • With cURL:

curl -sSX GET http://localhost:3000/movies/599570faf0429b4494cfa5d4 | jq ‘.’

6.4 — Update an Existing Movie

Update the UpdateMovieEndPoint method as follows:

func UpdateMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    defer r.Body.Close()
    var movie Movie
    if err: = json.NewDecoder(r.Body).Decode( & movie);
    err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    if err: = dao.Update(movie);
    err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondWithJson(w, http.StatusOK, map[string] string {
        "result": "success"
    })
}

Let’s test it out:

  • With Postman:

  • With cURL:

curl -sSX PUT -d ‘{“name”:”dunkirk”,”cover_image”:”https://image.tmdb.org/t/p/w640/cUqEgoP6kj8ykfNjJx3Tl5zHCcN.jpg", “description”:”world war 2 movie”}’ http://localhost:3000/movies | jq ‘.’

6.5 — Delete an Existing Movie

Update the DeleteMovieEndPoint method as follows:

func DeleteMovieEndPoint(w http.ResponseWriter, r * http.Request) {
    defer r.Body.Close()
    var movie Movie
    if err: = json.NewDecoder(r.Body).Decode( & movie);
    err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid request payload")
        return
    }
    if err: = dao.Delete(movie);
    err != nil {
        respondWithError(w, http.StatusInternalServerError, err.Error())
        return
    }
    respondWithJson(w, http.StatusOK, map[string] string {
        "result": "success"
    })
}

Let’s test it out:

  • With Postman:

  • With cURL:

curl -sSX DELETE -d ‘{“name”:”dunkirk”,”cover_image”:”https://image.tmdb.org/t/p/w640/cUqEgoP6kj8ykfNjJx3Tl5zHCcN.jpg", “description”:”world war 2 movie”}’ http://localhost:3000/movies | jq ‘.’

Taking this further? On my upcoming posts, I will show you how to:

  • Write Unit Tests in Go for each Endpoint
  • Build a UI in Angular 4
  • Setup a CI/CD with CircleCI
  • Deploy the stack on AWS, and much more…

So stay tuned!

API Continuous Integration/Deployment MongoDB REST Web Protocols unit test Database Data access object

Published at DZone with permission of Mohamed Labouardy, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Solving the Kubernetes Security Puzzle
  • What Is API-First?
  • Tracking Software Architecture Decisions
  • How To Build a Spring Boot GraalVM Image

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: