Table of Contents
- Introduction
- Prerequisites
- Setting Up Go
- Creating the Server
- Handling HTTP Requests
- Connecting to a Database
- Authentication and Authorization
- Testing the Server
-
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
-
Create a new directory for your Go project:
mkdir go-server cd go-server
-
Initialize a new Go module:
go mod init github.com/your-username/go-server
-
Create a new file named
main.go
and open it in a text editor. -
In the
main.go
file, import the necessary packages:package main import ( "fmt" "log" "net/http" )
-
Define the main function and create a basic HTTP server:
func main() { http.HandleFunc("/", handleRequest) log.Fatal(http.ListenAndServe(":8080", nil)) }
-
Implement the
handleRequest
function to handle incoming HTTP requests:func handleRequest(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, World!") }
-
Save the
main.go
file. -
Run the server using the following command:
go run main.go
-
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.
-
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 }
-
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) } }
-
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.
-
Install the required database driver package. In this example, we will use the popular SQLite database:
go get github.com/mattn/go-sqlite3
-
Import the necessary packages in the
main.go
file:import ( // ... "database/sql" _ "github.com/mattn/go-sqlite3" )
-
Define a global variable to hold the database connection:
var db *sql.DB
-
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() }
-
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) } }
-
Call the
createTable
function at the beginning of themain
function:func main() { // ... createTable() }
-
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).
-
Install the required JWT package:
go get github.com/dgrijalva/jwt-go
-
Import the necessary packages in the
main.go
file:import ( // ... "github.com/dgrijalva/jwt-go" )
-
Define a secret key for signing and validating JWTs:
var secret = []byte("your-secret-key")
-
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) }
-
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) } }
-
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.
-
Create a new file named
main_test.go
and open it in a text editor. -
At the beginning of the file, import the necessary packages:
package main_test import ( "net/http" "net/http/httptest" "strings" "testing" )
-
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) } }
-
Save the
main_test.go
file. -
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!