Go > Web Development > REST APIs > Creating RESTful endpoints

Creating a Simple REST API in Go

This example demonstrates how to create a basic REST API in Go using the `net/http` package and the `encoding/json` package for handling JSON data. It includes defining routes, handling requests, and sending responses.

Project Setup

First, create a new Go project directory. Inside the directory, initialize a new Go module using `go mod init `. Then, create a main.go file to hold the code.

mkdir go-rest-api
cd go-rest-api
go mod init example.com/go-rest-api
touch main.go

Basic REST API Implementation

This code defines the basic structure of a REST API with functionalities to get all articles, get a single article by ID, create a new article, and delete an article. It utilizes the `gorilla/mux` router for request handling. To make the code executable you have to add `"fmt"` and `"io/ioutil"` to the import section.

package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gorilla/mux"
)

type Article struct {
	Id      string `json:"Id"`
	Title   string `json:"Title"`	
	Desc    string `json:"desc"`
	Content string `json:"content"`
}

var Articles []Article

func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to the HomePage!")
	fmt.Println("Endpoint Hit: HomePage")
}

func returnAllArticles(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Endpoint Hit: returnAllArticles")
	json.NewEncoder(w).Encode(Articles)
}

func returnSingleArticle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	key := vars["id"]

	for _, article := range Articles {
		if article.Id == key {
			json.NewEncoder(w).Encode(article)
		}
	}
}

func createNewArticle(w http.ResponseWriter, r *http.Request) {
	// get the body of our POST request
	// unmarshal this into a new Article struct
	// append this to our Articles array.
	reqBody, _ := ioutil.ReadAll(r.Body)
	var article Article
	json.Unmarshal(reqBody, &article)
	// update our global Articles array to include
	Articles = append(Articles, article)

	json.NewEncoder(w).Encode(article)
}

func deleteArticle(w http.ResponseWriter, r *http.Request) {
	// once again, lets parse the path parameters
	vars := mux.Vars(r)
	// we will need to extract the `id` of the article we
	// wish to delete
	id := vars["id"]

	// loop through all our articles
	// if the article.Id equals the id we pass in through the path
	// delete the article
	for index, article := range Articles {
		if article.Id == id {
			Articles = append(Articles[:index], Articles[index+1:]...)
		}
	}

}

func handleRequests() {
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", homePage)
	myRouter.HandleFunc("/articles", returnAllArticles)
	myRouter.HandleFunc("/article", createNewArticle).Methods("POST")
	myRouter.HandleFunc("/article/{id}", deleteArticle).Methods("DELETE")
	myRouter.HandleFunc("/article/{id}", returnSingleArticle)
	log.Fatal(http.ListenAndServe(":10000", myRouter))
}

func main() {
	Articles = []Article{
		Article{Id: "1", Title: "Hello", Desc: "Article Description", Content: "Article Content"},
		Article{Id: "2", Title: "Hello 2", Desc: "Article Description", Content: "Article Content"},
	}
	handleRequests()
}

Detailed Explanation

  • Article Struct: Defines the structure of an article with fields for ID, Title, Description, and Content. The `json:"..."` tags are used for JSON encoding and decoding.
  • Routes:
    • `/`: Serves a welcome message.
    • `/articles`: Returns all articles in JSON format.
    • `/article/{id}`: Returns a single article by its ID.
    • `/article`: Creates a new article.
    • `/article/{id}`: Deletes a specific article.
  • Request Handlers:
    • homePage: Handles the root endpoint.
    • returnAllArticles: Encodes the `Articles` slice into JSON and sends it as a response.
    • returnSingleArticle: Retrieves an article based on the ID from the request and returns it as JSON.
    • createNewArticle: Parses the request body to create a new article and adds it to the `Articles` slice.
    • deleteArticle: Deletes an article based on the ID from the request.

Dependencies

This example utilizes the `gorilla/mux` package for routing. Install it using the following command:

go get github.com/gorilla/mux

Running the Application

Run the application using `go run main.go`. The API will be accessible at `http://localhost:10000`.

go run main.go

Real-Life Use Case

This structure can be used for building content management systems, e-commerce platforms, or any application requiring CRUD operations on data via an API.

Best Practices

  • Error Handling: Implement robust error handling to provide meaningful feedback to the client.
  • Input Validation: Validate incoming data to prevent security vulnerabilities and ensure data integrity.
  • Authentication/Authorization: Secure the API endpoints with appropriate authentication and authorization mechanisms.
  • Logging: Implement logging to track API usage and debug issues.
  • Testing: Write unit and integration tests to ensure the API functions correctly.

Interview Tip

Be prepared to discuss the difference between REST and other API architectures, common HTTP methods, and best practices for designing RESTful APIs. Also, understand how the code interacts with the HTTP request and response objects.

When to use them

REST APIs are ideal for scenarios where you need to provide a standardized interface for accessing and manipulating data over the internet. They are widely used for web and mobile application backends.

Memory footprint

The memory footprint of this example is relatively small. It depends on the size of the `Articles` slice and the number of concurrent requests. Using efficient data structures and optimizing request handling can help minimize memory usage.

Alternatives

  • GraphQL: An alternative to REST that allows clients to request specific data.
  • gRPC: A high-performance, open-source universal RPC framework.

Pros

  • Simplicity: Easy to understand and implement.
  • Scalability: Can be scaled horizontally to handle increased traffic.
  • Flexibility: Supports various data formats (e.g., JSON, XML).

Cons

  • Over-fetching/Under-fetching: Clients may receive more or less data than they need.
  • Multiple Round Trips: May require multiple requests to fetch related data.

FAQ

  • How do I handle different HTTP methods?

    Use the `Methods` function in `gorilla/mux` to specify the allowed HTTP methods for each route. For example, `myRouter.HandleFunc("/article", createNewArticle).Methods("POST")`.
  • How do I handle query parameters?

    Use the `URL.Query()` method on the `Request` object to access query parameters. For example, `r.URL.Query().Get("paramName")`.
  • How can I add middleware to my API?

    You can create custom middleware functions that wrap your request handlers. Middleware can be used for logging, authentication, and other cross-cutting concerns.