Building a Go Server for Mobile Backend Development

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setting Up Go
  4. Creating the Server
  5. Handling HTTP Requests
  6. Connecting to a Database
  7. Authentication and Authorization
  8. Testing the Server
  9. Conclusion


Introduction

In this tutorial, we will learn how to build a Go server for mobile backend development. We will cover the basics of setting up Go, creating an HTTP server, handling requests, connecting to a database, implementing authentication and authorization, and testing the server. By the end of this tutorial, you will have a functioning Go server that can be used as a backend for mobile applications.

Prerequisites

Before starting this tutorial, you should have a basic understanding of Go programming language syntax and concepts. Familiarity with concepts like functions, variables, loops, and conditional statements will be beneficial. Additionally, you should have Go installed on your machine. If you don’t have Go installed, please refer to the official Go documentation for installation instructions.

Setting Up Go

First, let’s ensure that Go is properly installed on your machine. Open your command line or terminal and run the following command:

go version

If Go is installed correctly, you should see the version number printed on the screen. If not, please refer to the official Go installation documentation to install Go.

Creating the Server

  1. Create a new directory for your Go project:

     mkdir go-server
     cd go-server
    
  2. Initialize a new Go module:

     go mod init github.com/your-username/go-server
    
  3. Create a new file named main.go and open it in a text editor.

  4. In the main.go file, import the necessary packages:

     package main
        
     import (
         "fmt"
         "log"
         "net/http"
     )
    
  5. Define the main function and create a basic HTTP server:

     func main() {
         http.HandleFunc("/", handleRequest)
         log.Fatal(http.ListenAndServe(":8080", nil))
     }
    
  6. Implement the handleRequest function to handle incoming HTTP requests:

     func handleRequest(w http.ResponseWriter, r *http.Request) {
         fmt.Fprintf(w, "Hello, World!")
     }
    
  7. Save the main.go file.

  8. Run the server using the following command:

     go run main.go
    
  9. Open your web browser and visit http://localhost:8080. You should see the message “Hello, World!” displayed on the page.

    Congratulations! You have successfully created a basic Go server.

Handling HTTP Requests

Now, let’s enhance our server to handle different types of HTTP requests and route them to appropriate functions.

  1. In the main.go file, let’s define a new function to handle a POST request:

     func handlePostRequest(w http.ResponseWriter, r *http.Request) {
         // Handle POST request logic here
     }
    
  2. Modify the handleRequest function to differentiate between GET and POST requests:

     func handleRequest(w http.ResponseWriter, r *http.Request) {
         if r.Method == "GET" {
             fmt.Fprintf(w, "Hello, World!")
         } else if r.Method == "POST" {
             handlePostRequest(w, r)
         }
     }
    
  3. Save the main.go file.

    Now, when a GET request is made to the server, the message “Hello, World!” will be displayed. If a POST request is made, it will be handled by the handlePostRequest function.

Connecting to a Database

To make our server more useful, let’s connect it to a database to store and retrieve data.

  1. Install the required database driver package. In this example, we will use the popular SQLite database:

     go get github.com/mattn/go-sqlite3
    
  2. Import the necessary packages in the main.go file:

     import (
         // ...
         "database/sql"
         _ "github.com/mattn/go-sqlite3"
     )
    
  3. Define a global variable to hold the database connection:

     var db *sql.DB
    
  4. In the main function, open the database connection:

     func main() {
         // ...
         var err error
         db, err = sql.Open("sqlite3", "./data.db")
         if err != nil {
             log.Fatal(err)
         }
         defer db.Close()
     }
    
  5. Create a new table in the database for storing data:

     func createTable() {
         createTableSQL := `
             CREATE TABLE IF NOT EXISTS data (
                 id INTEGER PRIMARY KEY AUTOINCREMENT,
                 name TEXT NOT NULL,
                 age INTEGER NOT NULL
             );
         `
         _, err := db.Exec(createTableSQL)
         if err != nil {
             log.Fatal(err)
         }
     }
    
  6. Call the createTable function at the beginning of the main function:

     func main() {
         // ...
         createTable()
     }
    
  7. Modify the handlePostRequest function to insert data into the database:

     func handlePostRequest(w http.ResponseWriter, r *http.Request) {
         // Parse request body
         err := r.ParseForm()
         if err != nil {
             http.Error(w, "Bad Request", http.StatusBadRequest)
             return
         }
        
         // Extract name and age from form data
         name := r.Form.Get("name")
         age := r.Form.Get("age")
        
         // Insert data into the database
         insertSQL := `
             INSERT INTO data (name, age)
             VALUES (?, ?)
         `
         _, err = db.Exec(insertSQL, name, age)
         if err != nil {
             log.Fatal(err)
         }
        
         fmt.Fprintf(w, "Data inserted successfully!")
     }
    

    Now, when a POST request is made to the server with form data containing “name” and “age” fields, the data will be inserted into the database.

Authentication and Authorization

To secure our server, let’s implement basic authentication and authorization using JSON Web Tokens (JWT).

  1. Install the required JWT package:

     go get github.com/dgrijalva/jwt-go
    
  2. Import the necessary packages in the main.go file:

     import (
         // ...
         "github.com/dgrijalva/jwt-go"
     )
    
  3. Define a secret key for signing and validating JWTs:

     var secret = []byte("your-secret-key")
    
  4. Implement a function to generate JWTs:

     func generateToken() (string, error) {
         token := jwt.New(jwt.SigningMethodHS256)
         claims := token.Claims.(jwt.MapClaims)
         claims["authorized"] = true
         // Add other claims as needed
        
         return token.SignedString(secret)
     }
    
  5. Modify the handleRequest function to check for a valid JWT:

     func handleRequest(w http.ResponseWriter, r *http.Request) {
         // Parse JWT from request header
         tokenString := r.Header.Get("Authorization")
         if tokenString == "" {
             http.Error(w, "Unauthorized", http.StatusUnauthorized)
             return
         }
        
         // Validate JWT
         token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
             if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                 return nil, fmt.Errorf("Invalid signing method")
             }
             return secret, nil
         })
         if err != nil || !token.Valid {
             http.Error(w, "Unauthorized", http.StatusUnauthorized)
             return
         }
        
         // Continue handling the request
         if r.Method == "GET" {
             fmt.Fprintf(w, "Hello, World!")
         } else if r.Method == "POST" {
             handlePostRequest(w, r)
         }
     }
    
  6. Modify the main function to generate and print a JWT:

     func main() {
         // ...
         token, err := generateToken()
         if err != nil {
             log.Fatal(err)
         }
         fmt.Println("JWT:", token)
     }
    

    Now, before making any request to the server, you will need to include a valid JWT in the “Authorization” header. Otherwise, the server will return an “Unauthorized” status.

Testing the Server

To ensure the reliability and correctness of our server, it’s important to write tests that cover different scenarios.

  1. Create a new file named main_test.go and open it in a text editor.

  2. At the beginning of the file, import the necessary packages:

     package main_test
        
     import (
         "net/http"
         "net/http/httptest"
         "strings"
         "testing"
     )
    
  3. Implement a test function for the handleRequest function:

     func TestHandleRequest(t *testing.T) {
         requestBody := strings.NewReader("")
         req := httptest.NewRequest("GET", "http://localhost:8080", requestBody)
         w := httptest.NewRecorder()
        
         handleRequest(w, req)
        
         resp := w.Result()
         if resp.StatusCode != http.StatusOK {
             t.Errorf("Expected status 200, got %d", resp.StatusCode)
         }
        
         body := w.Body.String()
         if body != "Hello, World!" {
             t.Errorf("Expected 'Hello, World!', got '%s'", body)
         }
     }
    
  4. Save the main_test.go file.

  5. Run the tests using the following command:

     go test
    

    If all tests pass, you can be confident that your server is working as expected.

Conclusion

In this tutorial, we have learned how to build a Go server for mobile backend development. We covered the basics of setting up Go, creating an HTTP server, handling requests, connecting to a database, implementing authentication and authorization, and testing the server. You can now use this knowledge to build robust backend servers for your mobile applications.

Remember to always follow best practices, such as validating user input, handling errors gracefully, and securing sensitive data. Additionally, continue exploring Go’s vast ecosystem and experiment with different packages to expand your server’s capabilities.

Happy coding with Go!