{"id":19188925,"url":"https://github.com/kelvins/goapitutorial","last_synced_at":"2025-07-26T12:10:39.165Z","repository":{"id":84557963,"uuid":"92756521","full_name":"kelvins/GoApiTutorial","owner":"kelvins","description":" :books: Building and Testing a REST API in GoLang using Gorilla Mux and MySQL","archived":false,"fork":false,"pushed_at":"2019-03-28T09:09:13.000Z","size":20,"stargazers_count":104,"open_issues_count":2,"forks_count":25,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-08T02:47:41.209Z","etag":null,"topics":["api","golang","rest-api"],"latest_commit_sha":null,"homepage":"https://goo.gl/8qe9Du","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kelvins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-29T16:24:14.000Z","updated_at":"2025-03-20T18:24:48.000Z","dependencies_parsed_at":"2023-03-11T14:30:55.217Z","dependency_job_id":null,"html_url":"https://github.com/kelvins/GoApiTutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kelvins/GoApiTutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelvins%2FGoApiTutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelvins%2FGoApiTutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelvins%2FGoApiTutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelvins%2FGoApiTutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kelvins","download_url":"https://codeload.github.com/kelvins/GoApiTutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kelvins%2FGoApiTutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267163765,"owners_count":24045710,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-26T02:00:08.937Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","golang","rest-api"],"created_at":"2024-11-09T11:26:43.561Z","updated_at":"2025-07-26T12:10:39.121Z","avatar_url":"https://github.com/kelvins.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Building and Testing a REST API in GoLang using Gorilla Mux and MySQL\n\n[![Build Status](https://travis-ci.org/kelvins/GoApiTutorial.svg?branch=master)](https://travis-ci.org/kelvins/GoApiTutorial)\n[![Coverage Status](https://coveralls.io/repos/github/kelvins/GoApiTutorial/badge.svg?branch=master)](https://coveralls.io/github/kelvins/GoApiTutorial?branch=master)\n\nLink to the Medium story: https://goo.gl/8qe9Du\n\n\nIn this tutorial we will learn how to build and test a simple REST API in Go using [Gorilla Mux](https://github.com/gorilla/mux) router and the [MySQL](https://www.mysql.com/) database. We will also create the application following the [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) (TDD) methodology.\n\n### Goals\n\n* Become familiar with the TDD methodology.\n\n* Become familiar with the Gorilla Mux package.\n\n* Learn how to use MySQL in Go.\n\n### Prerequisites\n\n* You must have a working Go and MySQL environments.\n\n* Basic familiarity with Go and MySQL.\n\n### About the Application\n\nThe application is a simple REST API server that will provide endpoints to allow accessing and manipulating ‘users’.\n\n### API Specification\n\n* Create a new user in response to a valid POST request at /user,\n\n* Update a user in response to a valid PUT request at /user/{id},\n\n* Delete a user in response to a valid DELETE request at /user/{id},\n\n* Fetch a user in response to a valid GET request at /user/{id}, and\n\n* Fetch a list of users in response to a valid GET request at /users.\n\nThe {id} will determine which user the request will work with.\n\n### Creating the Database\n\nAs our application is simple, we will create only one table called users with the following fields:\n\n* id : is the primary key.\n\n* name : is the name of the user.\n\n* age : is the age of the user.\n\nLet’s use the following statement to create the database and the table.\n\n    CREATE DATABASE rest_api_example;\n    USE rest_api_example;\n    CREATE TABLE users (\n        id INT AUTO_INCREMENT PRIMARY KEY,\n        name VARCHAR(50) NOT NULL,\n        age INT NOT NULL\n    );\n\nIt is a very simple table but it’s ok for this example.\n\n### Getting Dependencies\n\nBefore we start writing our application, we need to get some dependencies that we will use. We need to get the two following packages:\n\n* mux : The Gorilla Mux router.\n\n* mysql : The MySQL driver.\n\nYou can easily use go get to get it:\n\n    go get github.com/gorilla/mux\n    go get github.com/go-sql-driver/mysql\n\n### Getting Started\n\nFirst of all, let’s create a file called app.go and add an App structure to hold our application. This structure provides references to the router and the database that we will use on our application. To make it testable let’s also create two methods to initialize and run the application:\n\n    // app.go\n    \n    package main\n    \n    import (\n        \"database/sql\"\n\n        \"github.com/gorilla/mux\"\n        _ \"github.com/go-sql-driver/mysql\"\n    )\n    \n    type App struct {\n        Router *mux.Router\n        DB     *sql.DB\n    }\n    \n    func (a *App) Initialize(user, password, dbname string) { }\n    \n    func (a *App) Run(addr string) { }\n\nThe Initialize method is responsible for create a database connection and wire up the routes, and the Run method will simply start the application.\n\nNote that we have to import both mux and mysql packages here.\n\nNow, let’s create the main.go file which will contain the entry point for the application:\n\n    // main.go\n    \n    package main\n    \n    func main() {\n        a := App{} \n        // You need to set your Username and Password here\n        a.Initialize(\"DB_USERNAME\", \"DB_PASSWORD\", \"rest_api_example\")\n    \n        a.Run(\":8080\")\n    }\n\nNote that on this step you need to set the username and password.\n\nNow, let’s create a file called model.go which is used to define our user structure and provide some useful functions to deal with database operations.\n\n    // model.go\n\n    package main\n\n    import (\n        \"database/sql\"\n        \"errors\"\n    )\n\n    type user struct {\n        ID    int    `json:\"id\"`\n        Name  string `json:\"name\"`\n        Age   int    `json:\"age\"`\n    }\n\n    func (u *user) getUser(db *sql.DB) error {\n        return errors.New(\"Not implemented\")\n    }\n\n    func (u *user) updateUser(db *sql.DB) error {\n        return errors.New(\"Not implemented\")\n    }\n\n    func (u *user) deleteUser(db *sql.DB) error {\n        return errors.New(\"Not implemented\")\n    }\n\n    func (u *user) createUser(db *sql.DB) error {\n        return errors.New(\"Not implemented\")\n    }\n\n    func getUsers(db *sql.DB, start, count int) ([]user, error) {\n        return nil, errors.New(\"Not implemented\")\n    }\n\nAt this point we should have a file structure like that:\n\n    ┌── app.go\n    ├── main.go\n    └── model.go\n\nNow it’s time to write some tests for our API.\n\n### Writing Tests\n\nAs we are following the test-driven development (TDD) methodology, we need to write the test even before we write the functions itself.\n\nAs we will run the tests using a database, we need to make sure the database is set up before running the tests and cleaned up after the tests. So let’s create the main_test.go file. In the main_test.go file let’s create the TestMain function which is executed before all tests and will do these stuff for us.\n\n    // main_test.go\n\n    package main\n\n    import (\n        \"os\"\n        \"log\"\n        \"testing\"\n    )\n\n    var a App\n\n    func TestMain(m *testing.M) {\n        a = App{}\n        a.Initialize(\"DB_USERNAME\", \"DB_PASSWORD\", \"rest_api_example\")\n\n        ensureTableExists()\n\n        code := m.Run()\n\n        clearTable()\n\n        os.Exit(code)\n    }\n\n    func ensureTableExists() {\n        if _, err := a.DB.Exec(tableCreationQuery); err != nil {\n            log.Fatal(err)\n        }\n    }\n\n    func clearTable() {\n        a.DB.Exec(\"DELETE FROM users\")\n        a.DB.Exec(\"ALTER TABLE users AUTO_INCREMENT = 1\")\n    }\n\n    const tableCreationQuery = `\n    CREATE TABLE IF NOT EXISTS users\n    (\n        id INT AUTO_INCREMENT PRIMARY KEY,\n        name VARCHAR(50) NOT NULL,\n        age INT NOT NULL\n    )`\n\nNote that the global variable a represents the application that we want to test.\n\nWe use the ensureTableExists function that the table we need for testing is available. The tableCreationQuery is a constant which is a query used to create the database table.\n\nAfter run the tests we need to call the clearTable function to clean the database up.\n\nIn order to run the tests we need to implement the Initialize function in the app.go file, to create a database connection and initialize the router. Now the Initialize function should look like this:\n\n    // app.go\n    \n    func (a *App) Initialize(user, password, dbname string) {\n        connectionString := fmt.Sprintf(\"%s:%s@/%s\", user, password, dbname)\n\n        var err error\n        a.DB, err = sql.Open(\"mysql\", connectionString)\n        if err != nil {\n            log.Fatal(err)\n        }\n\n        a.Router = mux.NewRouter()\n    }\n\nAt this point even if we don’t have any tests we should be able to run go test without finding any runtime errors. Let’s try it out:\n\n    go test -v\n\nExecuting this command should result something like this:\n\n    testing: warning: no tests to run\n    PASS\n    ok      _/home/user/app 0.051s\n\n### Writing API Tests\n\nLet’s start testing the response of the /users endpoint with an empty table.\n\n    // main_test.go\n\n    func TestEmptyTable(t *testing.T) {\n        clearTable()\n\n        req, _ := http.NewRequest(\"GET\", \"/users\", nil)\n        response := executeRequest(req)\n\n        checkResponseCode(t, http.StatusOK, response.Code)\n\n        if body := response.Body.String(); body != \"[]\" {\n            t.Errorf(\"Expected an empty array. Got %s\", body)\n        }\n    }\n\nThis test will delete all records in the users table and send a GET request to the /users endpoint.\n\nWe use the executeRequest function to execute the request, and checkResponseCode function to test that the HTTP response code is what we expect, and finally, we check the body of the response and check if it is what we expect.\n\nSo, let’s implement the executeRequest and checkResponseCode functions.\n\n    // main_test.go\n    \n    func executeRequest(req *http.Request) *httptest.ResponseRecorder {\n        rr := httptest.NewRecorder()\n        a.Router.ServeHTTP(rr, req)\n    \n        return rr\n    }\n\n    func checkResponseCode(t *testing.T, expected, actual int) {\n        if expected != actual {\n            t.Errorf(\"Expected response code %d. Got %d\\n\", expected, actual)\n        }\n    }\n\nMake sure you have imported the \"net/http\" and \"net/http/httptest\" packages and run the tests again. If everything goes well you should get something like this:\n\n    === RUN   TestEmptyTable\n    --- FAIL: TestEmptyTable (0.02s)\n        main_test.go:71: Expected response code 200. Got 404\n        main_test.go:58: Expected an empty array. Got 404 page not found\n    FAIL\n    exit status 1\n    FAIL    _/home/user/app 0.055s\n\nAs expected, the tests will fail because we don’t have implemented anything yet, so let’s continue implementing other tests before we implement the functions for the application itself.\n\nLet’s implement a test that tries to fetch a nonexistent user.\n\n    // main_test.go\n    \n    func TestGetNonExistentUser(t *testing.T) {\n        clearTable()\n\n        req, _ := http.NewRequest(\"GET\", \"/user/45\", nil)\n        response := executeRequest(req)\n\n        checkResponseCode(t, http.StatusNotFound, response.Code)\n\n        var m map[string]string\n        json.Unmarshal(response.Body.Bytes(), \u0026m)\n        if m[\"error\"] != \"User not found\" {\n            t.Errorf(\"Expected the 'error' key of the response to be set to 'User not found'. Got '%s'\", m[\"error\"])\n        }\n    }\n\nThis test basically tests two things: the status code which should be 404 and if the response contains the expected error message.\n\nNote that in this step we need to import the \"encoding/json\" package to use the json.Unmarshal function.\n\nNow, let’s implement a test to create a user.\n\n    // main_test.go\n\n    func TestCreateUser(t *testing.T) {\n        clearTable()\n\n        payload := []byte(`{\"name\":\"test user\",\"age\":30}`)\n\n        req, _ := http.NewRequest(\"POST\", \"/user\", bytes.NewBuffer(payload))\n        response := executeRequest(req)\n\n        checkResponseCode(t, http.StatusCreated, response.Code)\n\n        var m map[string]interface{}\n        json.Unmarshal(response.Body.Bytes(), \u0026m)\n\n        if m[\"name\"] != \"test user\" {\n            t.Errorf(\"Expected user name to be 'test user'. Got '%v'\", m[\"name\"])\n        }\n\n        if m[\"age\"] != 30.0 {\n            t.Errorf(\"Expected user age to be '30'. Got '%v'\", m[\"age\"])\n        }\n\n        // the id is compared to 1.0 because JSON unmarshaling converts numbers to\n        // floats, when the target is a map[string]interface{}\n        if m[\"id\"] != 1.0 {\n            t.Errorf(\"Expected user ID to be '1'. Got '%v'\", m[\"id\"])\n        }\n    }\n\nIn this test, we manually add a new user to the database and, by accessing the correspondent endpoint, we check if the status code is 201 (the resource was created) and if the JSON response contains the correct information that was added.\n\nNote that in this step we need to import the \"bytes\" package to use the bytes.NewBuffer function.\n\nNow, let’s implement a test to fetch an existing user.\n\n    // main_test.go\n    \n    func TestGetUser(t *testing.T) {\n        clearTable()\n        addUsers(1)\n\n        req, _ := http.NewRequest(\"GET\", \"/user/1\", nil)\n        response := executeRequest(req)\n\n        checkResponseCode(t, http.StatusOK, response.Code)\n    }\n\nThis test basically add a new user to the database and check if the correct endpoint results in an HTTP response with status code 200 (success).\n\nIn this test above we use the addUsers function which is used to add a new user to the database for the tests. So, let’s implement this function:\n\n    // main_test.go\n    \n    func addUsers(count int) {\n        if count \u003c 1 {\n            count = 1\n        }\n\n        for i := 0; i \u003c count; i++ {\n            statement := fmt.Sprintf(\"INSERT INTO users(name, age) VALUES('%s', %d)\", (\"User \" + strconv.Itoa(i+1)), ((i+1) * 10))\n            a.DB.Exec(statement)\n        }\n    }\n\nNote that in this step we need to import the \"strconv\" package to use the strconv.Itoa function to convert an integer to a string.\n\nNow, let’s test the update option:\n\n    // main_test.go\n\n    func TestUpdateUser(t *testing.T) {\n        clearTable()\n        addUsers(1)\n\n        req, _ := http.NewRequest(\"GET\", \"/user/1\", nil)\n        response := executeRequest(req)\n        var originalUser map[string]interface{}\n        json.Unmarshal(response.Body.Bytes(), \u0026originalUser)\n\n        payload := []byte(`{\"name\":\"test user - updated name\",\"age\":21}`)\n\n        req, _ = http.NewRequest(\"PUT\", \"/user/1\", bytes.NewBuffer(payload))\n        response = executeRequest(req)\n\n        checkResponseCode(t, http.StatusOK, response.Code)\n\n        var m map[string]interface{}\n        json.Unmarshal(response.Body.Bytes(), \u0026m)\n\n        if m[\"id\"] != originalUser[\"id\"] {\n            t.Errorf(\"Expected the id to remain the same (%v). Got %v\", originalUser[\"id\"], m[\"id\"])\n        }\n\n        if m[\"name\"] == originalUser[\"name\"] {\n            t.Errorf(\"Expected the name to change from '%v' to '%v'. Got '%v'\", originalUser[\"name\"], m[\"name\"], m[\"name\"])\n        }\n\n        if m[\"age\"] == originalUser[\"age\"] {\n            t.Errorf(\"Expected the age to change from '%v' to '%v'. Got '%v'\", originalUser[\"age\"], m[\"age\"], m[\"age\"])\n        }\n    }\n\nIn the above test, we basically add a new user to the database and then we use the correct endpoint to update it.\n\nIt tests if the status code is 200 indicating success and if the JSON response contains the updated details about the user.\n\nAnd the last test, for now, will try to delete a user.\n\n    // main_test.go\n    \n    func TestDeleteUser(t *testing.T) {\n        clearTable()\n        addUsers(1)\n\n        req, _ := http.NewRequest(\"GET\", \"/user/1\", nil)\n        response := executeRequest(req)\n        checkResponseCode(t, http.StatusOK, response.Code)\n\n        req, _ = http.NewRequest(\"DELETE\", \"/user/1\", nil)\n        response = executeRequest(req)\n\n        checkResponseCode(t, http.StatusOK, response.Code)\n\n        req, _ = http.NewRequest(\"GET\", \"/user/1\", nil)\n        response = executeRequest(req)\n        checkResponseCode(t, http.StatusNotFound, response.Code)\n    }\n\nIn this test we basically create a new user and test if it exists in the database, then we user the correct endpoint to delete the user and checks if it was properly deleted.\n\nAt this point we should be able to run go test -v in your project directory.\n\nAll tests should fail but it’s ok because we did not implement the application functions yet. So let’s implement it to make these tests pass.\n\n### Creating the Application Functionalities\n\nLet’s begin implementing the methods in the model.go file. These methods are responsible for executing the database statements and it can be implemented as follows:\n\n    // model.go\n    \n    func (u *user) getUser(db *sql.DB) error {\n        statement := fmt.Sprintf(\"SELECT name, age FROM users WHERE id=%d\", u.ID)\n        return db.QueryRow(statement).Scan(\u0026u.Name, \u0026u.Age)\n    }\n\n    func (u *user) updateUser(db *sql.DB) error {\n        statement := fmt.Sprintf(\"UPDATE users SET name='%s', age=%d WHERE id=%d\", u.Name, u.Age, u.ID)\n        _, err := db.Exec(statement)\n        return err\n    }\n\n    func (u *user) deleteUser(db *sql.DB) error {\n        statement := fmt.Sprintf(\"DELETE FROM users WHERE id=%d\", u.ID)\n        _, err := db.Exec(statement)\n        return err\n    }\n\n    func (u *user) createUser(db *sql.DB) error {\n        statement := fmt.Sprintf(\"INSERT INTO users(name, age) VALUES('%s', %d)\", u.Name, u.Age)\n        _, err := db.Exec(statement)\n\n        if err != nil {\n            return err\n        }\n\n        err = db.QueryRow(\"SELECT LAST_INSERT_ID()\").Scan(\u0026u.ID)\n\n        if err != nil {\n            return err\n        }\n\n        return nil\n    }\n\n    func getUsers(db *sql.DB, start, count int) ([]user, error) {\n        statement := fmt.Sprintf(\"SELECT id, name, age FROM users LIMIT %d OFFSET %d\", count, start)\n        rows, err := db.Query(statement)\n\n        if err != nil {\n            return nil, err\n        }\n\n        defer rows.Close()\n\n        users := []user{}\n\n        for rows.Next() {\n            var u user\n            if err := rows.Scan(\u0026u.ID, \u0026u.Name, \u0026u.Age); err != nil {\n                return nil, err\n            }\n            users = append(users, u)\n        }\n\n        return users, nil\n    }\n\nThe getUsers function fetches records from the users table and limits the number of records based on the count value passed by parameter. The start parameter determines how many records are skipped at the beginning.\n\nAt this point, we need to remove the errors package and import the fmt package.\n\nThe model is done, now we need to implement the App functions, including the **routes** and **route handlers**.\n\nLet’s start creating the getUser function to fetch a single user.\n\n    // app.go\n    \n    func (a *App) getUser(w http.ResponseWriter, r *http.Request) {\n        vars := mux.Vars(r)\n        id, err := strconv.Atoi(vars[\"id\"])\n        if err != nil {\n            respondWithError(w, http.StatusBadRequest, \"Invalid user ID\")\n            return\n        }\n\n        u := user{ID: id}\n        if err := u.getUser(a.DB); err != nil {\n            switch err {\n            case sql.ErrNoRows:\n                respondWithError(w, http.StatusNotFound, \"User not found\")\n            default:\n                respondWithError(w, http.StatusInternalServerError, err.Error())\n            }\n            return\n        }\n\n        respondWithJSON(w, http.StatusOK, u)\n    }\n\nThis handler basically retrieves the id of the user from the requested URL and uses the getUser function, from the model, to fetch the user details.\n\nIf the user is not found it will respond with the status code 404. This function uses the respondWithError and respondWithJSON functions to process errors and normal responses. These functions are implemented as follows:\n\n    // app.go\n    \n    func respondWithError(w http.ResponseWriter, code int, message string) {\n        respondWithJSON(w, code, map[string]string{\"error\": message})\n    }\n\n    func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {\n        response, _ := json.Marshal(payload)\n\n        w.Header().Set(\"Content-Type\", \"application/json\")\n        w.WriteHeader(code)\n        w.Write(response)\n    }\n\nThe rest of the handlers can be implemented in a similar manner:\n\n    // app.go\n    \n    func (a *App) getUsers(w http.ResponseWriter, r *http.Request) {\n        count, _ := strconv.Atoi(r.FormValue(\"count\"))\n        start, _ := strconv.Atoi(r.FormValue(\"start\"))\n\n        if count \u003e 10 || count \u003c 1 {\n            count = 10\n        }\n        if start \u003c 0 {\n            start = 0\n        }\n\n        users, err := getUsers(a.DB, start, count)\n        if err != nil {\n            respondWithError(w, http.StatusInternalServerError, err.Error())\n            return\n        }\n\n        respondWithJSON(w, http.StatusOK, users)\n    }\n\nThis handler uses the count and start parameters from the querystring to fetch count number of users, starting at position start in the database. By default, start is set to 0 and count is set to 10. If these parameters aren’t provided, this handler will respond with the first 10 users.\n\nLet’s implement the handler to create a user.\n\n    // app.go\n    \n    func (a *App) createUser(w http.ResponseWriter, r *http.Request) {\n        var u user\n        decoder := json.NewDecoder(r.Body)\n        if err := decoder.Decode(\u0026u); err != nil {\n            respondWithError(w, http.StatusBadRequest, \"Invalid request payload\")\n            return\n        }\n        defer r.Body.Close()\n\n        if err := u.createUser(a.DB); err != nil {\n            respondWithError(w, http.StatusInternalServerError, err.Error())\n            return\n        }\n\n        respondWithJSON(w, http.StatusCreated, u)\n    }\n\nThis handler assumes that the request body is a JSON object containing the details of the user to be created. It extracts that object into a user and then uses the createUser function.\n\nThe handler to update a user:\n\n    // app.go\n    \n    func (a *App) updateUser(w http.ResponseWriter, r *http.Request) {\n        vars := mux.Vars(r)\n        id, err := strconv.Atoi(vars[\"id\"])\n        if err != nil {\n            respondWithError(w, http.StatusBadRequest, \"Invalid user ID\")\n            return\n        }\n\n        var u user\n        decoder := json.NewDecoder(r.Body)\n        if err := decoder.Decode(\u0026u); err != nil {\n            respondWithError(w, http.StatusBadRequest, \"Invalid resquest payload\")\n            return\n        }\n        defer r.Body.Close()\n        u.ID = id\n\n        if err := u.updateUser(a.DB); err != nil {\n            respondWithError(w, http.StatusInternalServerError, err.Error())\n            return\n        }\n\n        respondWithJSON(w, http.StatusOK, u)\n    }\n\nThis handler extracts the user details from the request body and the id from the URL, and uses the id and the body to update the user.\n\nAnd the last handler that we will implement is used to delete a user.\n\n    // app.go\n    \n    func (a *App) deleteUser(w http.ResponseWriter, r *http.Request) {\n        vars := mux.Vars(r)\n        id, err := strconv.Atoi(vars[\"id\"])\n        if err != nil {\n            respondWithError(w, http.StatusBadRequest, \"Invalid User ID\")\n            return\n        }\n\n        u := user{ID: id}\n        if err := u.deleteUser(a.DB); err != nil {\n            respondWithError(w, http.StatusInternalServerError, err.Error())\n            return\n        }\n\n        respondWithJSON(w, http.StatusOK, map[string]string{\"result\": \"success\"})\n    }\n\nThis handler extracts the id from the URL and uses it to delete the corresponding user.\n\nNow that we have all handlers implemented we must define the routes which will use them.\n\n    // app.go\n    \n    func (a *App) initializeRoutes() {\n        a.Router.HandleFunc(\"/users\", a.getUsers).Methods(\"GET\")\n        a.Router.HandleFunc(\"/user\", a.createUser).Methods(\"POST\")\n        a.Router.HandleFunc(\"/user/{id:[0-9]+}\", a.getUser).Methods(\"GET\")\n        a.Router.HandleFunc(\"/user/{id:[0-9]+}\", a.updateUser).Methods(\"PUT\")\n        a.Router.HandleFunc(\"/user/{id:[0-9]+}\", a.deleteUser).Methods(\"DELETE\")\n    }\n\nThe routes are defined based on the API specification defined earlier. The {id:[0-9]+} part of the path indicates that Gorilla Mux should treat process a URL only if the id is a number. For all matching requests, Gorilla Mux then stores the the actual numeric value in the id variable.\n\nNow we just need to implement the Run function and call initializeRoutes from the Initialize method.\n\n    // app.go\n    \n    func (a *App) Initialize(user, password, dbname string) {\n        connectionString := fmt.Sprintf(\"%s:%s@/%s\", user, password, dbname)\n\n        var err error\n        a.DB, err = sql.Open(\"mysql\", connectionString)\n        if err != nil {\n            log.Fatal(err)\n        }\n\n        a.Router = mux.NewRouter()\n        a.initializeRoutes()\n    }\n\n    func (a *App) Run(addr string) {\n        log.Fatal(http.ListenAndServe(addr, a.Router))\n    }\n\nRemember to import all packages needed.\n\n    // app.go\n    \n    import (\n        \"database/sql\"\n        \"encoding/json\"\n        \"fmt\"\n        \"log\"\n        \"net/http\"\n        \"strconv\"\n\n        _ \"github.com/go-sql-driver/mysql\"\n        \"github.com/gorilla/mux\"\n    )\n\nThe final version of the app.go file should look like this: [https://github.com/kelvins/GoApiTutorial/blob/master/app.go](https://github.com/kelvins/GoApiTutorial/blob/master/app.go)\n\nNow if we run the tests again:\n\n    go test -v\n\nWe should get the following results:\n\n    === RUN   TestEmptyTable\n    --- PASS: TestEmptyTable (0.02s)\n    === RUN   TestGetNonExistentUser\n    --- PASS: TestGetNonExistentUser (0.02s)\n    === RUN   TestCreateUser\n    --- PASS: TestCreateUser (0.01s)\n    === RUN   TestGetUser\n    --- PASS: TestGetUser (0.01s)\n    === RUN   TestUpdateUser\n    --- PASS: TestUpdateUser (0.01s)\n    === RUN   TestDeleteUser\n    --- PASS: TestDeleteUser (0.01s)\n    PASS\n    ok   github.com/kelvins/goapi 0.124s\n\nThe complete code can be found on **Github** at the following link: [https://github.com/kelvins/GoApiTutorial](https://github.com/kelvins/GoApiTutorial)\n\n### Travis CI and Coveralls\n\nIf you are familiar with [Travis CI](https://travis-ci.org/) and [Coveralls](https://coveralls.io/) you can use the following settings for the build environment on the .travis.yml file:\n\n    language: go\n\n    go:\n      - 1.6\n      - 1.8\n      - tip\n\n    services:\n      - mysql\n\n    before_install:\n      - mysql -e 'CREATE DATABASE IF NOT EXISTS rest_api_example;'\n\n    install:\n      - go get golang.org/x/tools/cmd/cover\n      - go get github.com/mattn/goveralls\n      - go get github.com/gorilla/mux\n      - go get github.com/go-sql-driver/mysql\n\n    script:\n      - go test -covermode=count -coverprofile=coverage.out\n      - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci\n\n**Note** that for the **Travis CI** run the tests properly using the **MySQL** database you need to set the **username** as root and leave the **password** empty.\n\nIf you are not familiar with it, I suggest starting reading the **Getting Started **section of both [Travis CI](https://docs.travis-ci.com/user/getting-started/) and [Coveralls](https://coveralls.zendesk.com/hc/en-us). These tools are well documented and quite easy to understand and use.\n\nIf you want to manually test the API by manually sending requests I suggest to use the **Insomnia** application. It is a cross-platform REST API client that is very easy to use. It can be found here: [https://github.com/getinsomnia/insomnia](https://github.com/getinsomnia/insomnia)\n\n### References\n\nAlmost all this tutorial was created (and some codes copied) based on the tutorial written by Kulshekhar Kabra which can be found at the following link:\n[**Building and Testing a REST API in Go with Gorilla Mux and PostgreSQL**\n*Learn how to build simple and well-tested REST APIs backed by PostgreSQL in Go, using Gorilla Mux - a highly stable and…*semaphoreci.com](https://semaphoreci.com/community/tutorials/building-and-testing-a-rest-api-in-go-with-gorilla-mux-and-postgresql)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkelvins%2Fgoapitutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkelvins%2Fgoapitutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkelvins%2Fgoapitutorial/lists"}