Table of contents
If you're new to Go (often referred to as Golang) and want to learn how to build a RESTful API, you're in the right place! This guide will walk you through the process step-by-step, providing code examples to help you understand how everything fits together.
What is a REST API?
A REST (Representational State Transfer) API is a way for applications to communicate over HTTP using standard methods like GET, POST, PUT, and DELETE. RESTful APIs are stateless and can be used across various platforms and languages, making them a popular choice for web services.
Why Use Go for REST APIs?
Go is a statically typed, compiled language known for its simplicity and performance. It's excellent for building web services due to its efficient concurrency model and robust standard library, which includes powerful packages for handling HTTP requests.
Prerequisites
Before we begin, make sure you have:
Go installed on your system. You can download it from the official website.simp
A basic understanding of Go syntax and programming concepts.
A code editor or IDE you're comfortable with (e.g., VSCode, GoLand).
Setting Up Your Go Project
First, create a new directory for your project and navigate into it:
mkdir simple-rest-api
cd simple-rest-api
Initialize a new Go module:
go mod init github.com/yourusername/simple-rest-api
Replace github.com/yourusername/simple-rest-api
with your module path.
Building the REST API
We'll build a simple API to manage a list of books. The API will support the following operations:
Get a list of all books (GET)
Get a single book by ID (GET)
Add a new book (POST)
Update an existing book (PUT)
Delete a book (DELETE)
Creating a Simple Server
Let's start by creating a basic HTTP server.
Create a file named main.go
and add the following code:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", homePage)
fmt.Println("Server starting on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func homePage(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Welcome to the Simple REST API in Go!")
}
Run the server:
go run main.go
Visit http://localhost:8080
in your browser, and you should see "Welcome to the Simple REST API in Go!"
Defining a Data Model
Let's define a Book
struct to represent our data model.
Add the following code to main.go
:
type Book struct {
ID string `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
}
var books []Book
Initialize some sample data:
func init() {
books = []Book{
{ID: "1", Title: "1984", Author: "George Orwell"},
{ID: "2", Title: "To Kill a Mockingbird", Author: "Harper Lee"},
{ID: "3", Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"},
}
}
Implementing API Endpoints
Get All Books
Add the following handler function:
func getBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(books)
}
Don't forget to import the encoding/json
package at the top:
import (
"encoding/json"
// other imports
)
Update the main
function to handle this route:
func main() {
http.HandleFunc("/", homePage)
http.HandleFunc("/books", getBooks)
// rest of the code
}
Now, when you navigate to http://localhost:8080/books
, you should see the list of books in JSON format.
Get a Single Book
Add the handler function:
func getBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
id := r.URL.Query().Get("id")
for _, book := range books {
if book.ID == id {
json.NewEncoder(w).Encode(book)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
Update main
to handle this route:
func main() {
http.HandleFunc("/", homePage)
http.HandleFunc("/books", getBooks)
http.HandleFunc("/book", getBook)
// rest of the code
}
Now you can get a single book by ID: http://localhost:8080/book?id=1
Add a New Book
Add the handler function:
func createBook(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
var newBook Book
err := json.NewDecoder(r.Body).Decode(&newBook)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
books = append(books, newBook)
json.NewEncoder(w).Encode(newBook)
}
Update main
:
func main() {
http.HandleFunc("/", homePage)
http.HandleFunc("/books", getBooks)
http.HandleFunc("/book", getBook)
http.HandleFunc("/book/create", createBook)
// rest of the code
}
Now you can add a new book by sending a POST request to http://localhost:8080/book/create
with a JSON body.
Update a Book
Add the handler function:
func updateBook(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
id := r.URL.Query().Get("id")
var updatedBook Book
err := json.NewDecoder(r.Body).Decode(&updatedBook)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
for i, book := range books {
if book.ID == id {
books[i] = updatedBook
json.NewEncoder(w).Encode(updatedBook)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
Update main
:
func main() {
http.HandleFunc("/", homePage)
http.HandleFunc("/books", getBooks)
http.HandleFunc("/book", getBook)
http.HandleFunc("/book/create", createBook)
http.HandleFunc("/book/update", updateBook)
// rest of the code
}
You can update a book by sending a PUT request to http://localhost:8080/book/update?id=1
.
Delete a Book
Add the handler function:
func deleteBook(w http.ResponseWriter, r *http.Request) {
if r.Method != "DELETE" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
id := r.URL.Query().Get("id")
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
fmt.Fprintf(w, "Book with ID %s deleted", id)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
Update main
:
func main() {
http.HandleFunc("/", homePage)
http.HandleFunc("/books", getBooks)
http.HandleFunc("/book", getBook)
http.HandleFunc("/book/create", createBook)
http.HandleFunc("/book/update", updateBook)
http.HandleFunc("/book/delete", deleteBook)
// rest of the code
}
Now you can delete a book by sending a DELETE request to http://localhost:8080/book/delete?id=1
.
Testing the API
You can test the API endpoints using a tool like Postman or curl.
Example with curl
Get all books
curl http://localhost:8080/books
Get a single book
curl http://localhost:8080/book?id=1
Create a new book
curl -X POST -H "Content-Type: application/json" -d '{"id":"4","title":"Brave New World","author":"Aldous Huxley"}' http://localhost:8080/book/create
Update a book
curl -X PUT -H "Content-Type: application/json" -d '{"id":"4","title":"Brave New World Revisited","author":"Aldous Huxley"}' http://localhost:8080/book/update?id=4
Delete a book
curl -X DELETE http://localhost:8080/book/delete?id=4
Conclusion
Congratulations! You've built a simple REST API in Go. This basic API can be expanded and improved in many ways, such as adding a database, using a router package for cleaner route handling, and implementing proper error handling and logging.
Next Steps
Use a Router Package: Packages like
gorilla/mux
orchi
can make route handling cleaner and more efficient.Connect to a Database: Instead of using an in-memory slice, connect your API to a database like PostgreSQL or MongoDB.
Implement Middleware: Add middleware for logging, authentication, and request validation.
By understanding these basics, you're well on your way to building robust and efficient web services in Go. Happy coding!