What is API Versioning?

What is API Versioning?

Hey Everyone, How are you doing, Today I am back with one of the most important topics called API Versioning.

Let's discuss the different techniques we have to version an API with implementation in Golang.

What is API Versioning:

API versioning is a crucial aspect of API design and development, ensuring that changes in your API do not disrupt existing clients.

This guide explores four common methods for API versioning

  1. URI Versioning.
  2. Header Versioning.
  3. Query Parameter Versioning.
  4. Media Type Versioning.

URI Versioning:

URI versioning involves embedding the version number directly in the URI path. This method is straightforward to understand for clients.

Version 1:
GET /api/v1/products        
Version 2:
GET /api/v2/products        

Let's check the implementation for this:

package main

import (
	"fmt"
	"log"
	"net/http"
)

// Handler for version 1 of the API
func getV1Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v1", "data":"This is v1 data"}`)
}

// Handler for version 2 of the API
func getV2Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v2", "data":"This is v2 data"}`)
}

// Main handler to route to the correct version
func mainHandler(w http.ResponseWriter, r *http.Request) {
	switch {
	case r.URL.Path == "/api/v1/data":
		getV1Data(w, r)
	case r.URL.Path == "/api/v2/data":
		getV2Data(w, r)
	default:
		http.NotFound(w, r)
	}
}

func main() {
	http.HandleFunc("/", mainHandler)
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}        

Test it with the following curls

# Request to version 1 of the API
curl http://localhost:8080/api/v1/data

# Request to version 2 of the API
curl http://localhost:8080/api/v2/data        

Header Versioning:

Header versioning involves specifying the API version in the HTTP headers. This method keeps the URI clean and focuses on headers for version control.

Version 1:
GET /api/products
Headers: 
  API-Version: v1        
Version 2:
GET /api/products
Headers: 
  API-Version: v2        

Let's check the implementation for this:

package main

import (
	"fmt"
	"log"
	"net/http"
)

// Handler for version 1 of the API
func getV1Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v1", "data":"This is v1 data"}`)
}

// Handler for version 2 of the API
func getV2Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v2", "data":"This is v2 data"}`)
}

// Main handler to route to the correct version based on headers
func mainHandler(w http.ResponseWriter, r *http.Request) {
	apiVersion := r.Header.Get("API-Version")
	switch apiVersion {
	case "v1":
		getV1Data(w, r)
	case "v2":
		getV2Data(w, r)
	default:
		http.Error(w, "Unsupported API version", http.StatusBadRequest)
	}
}

func main() {
	http.HandleFunc("/api/data", mainHandler)
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}        

Test it with the following curls

# Request to version 1 of the API
curl -H "API-Version: v1" http://localhost:8080/api/data

# Request to version 2 of the API
curl -H "API-Version: v2" http://localhost:8080/api/data

# Request with an unsupported version
curl -H "API-Version: v3" http://localhost:8080/api/data        

Query Parameter Versioning:

In query parameter versioning, the version number is included as a query parameter in the request URL. This method is flexible and easy to implement.

Version 1:
GET /api/products?version=v1        
Version 2:
GET /api/products?version=v2        

Let's check the implementation for this:

package main

import (
	"fmt"
	"log"
	"net/http"
)

// Handler for version 1 of the API
func getV1Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v1", "data":"This is v1 data"}`)
}

// Handler for version 2 of the API
func getV2Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintln(w, `{"version":"v2", "data":"This is v2 data"}`)
}

// Main handler to route to the correct version based on query parameter
func mainHandler(w http.ResponseWriter, r *http.Request) {
	queryParams := r.URL.Query()
	apiVersion := queryParams.Get("version")

	switch apiVersion {
	case "v1":
		getV1Data(w, r)
	case "v2":
		getV2Data(w, r)
	default:
		http.Error(w, "Unsupported API version", http.StatusBadRequest)
	}
}

func main() {
	http.HandleFunc("/api/data", mainHandler)
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}        

Test it with the following curls:

# Request to version 1 of the API
curl http://localhost:8080/api/data?version=v1

# Request to version 2 of the API
curl http://localhost:8080/api/data?version=v2

# Request with an unsupported version
curl http://localhost:8080/api/data?version=v3        

Media Type Versioning:

Media-type versioning, also known as content negotiation, uses the Accept header to specify the version. This method is flexible and integrates well with REST principles.

Version 1:
GET /api/products
Headers: 
  Accept: application/vnd.example.v1+json        
Version 2:
GET /api/products
Headers: 
  Accept: application/vnd.example.v2+json        

Let's check the implementation for this:

package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
)

// Handler for version 1 of the API
func getV1Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/vnd.example.v1+json")
	fmt.Fprintln(w, `{"version":"v1", "data":"This is v1 data"}`)
}

// Handler for version 2 of the API
func getV2Data(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/vnd.example.v2+json")
	fmt.Fprintln(w, `{"version":"v2", "data":"This is v2 data"}`)
}

// Main handler to route to the correct version based on Accept header
func mainHandler(w http.ResponseWriter, r *http.Request) {
	acceptHeader := r.Header.Get("Accept")
	if strings.Contains(acceptHeader, "application/vnd.example.v1+json") {
		getV1Data(w, r)
	} else if strings.Contains(acceptHeader, "application/vnd.example.v2+json") {
		getV2Data(w, r)
	} else {
		http.Error(w, "Unsupported API version", http.StatusUnsupportedMediaType)
	}
}

func main() {
	http.HandleFunc("/api/data", mainHandler)
	log.Println("Starting server on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
        

Test it with the following curls:

# Request version 1 of the API
curl -H "Accept: application/vnd.example.v1+json" http://localhost:8080/api/data

# Request version 2 of the API
curl -H "Accept: application/vnd.example.v2+json" http://localhost:8080/api/data

# Request with an unsupported version
curl -H "Accept: application/vnd.example.v3+json" http://localhost:8080/api/data        

These are the common API Versioning Techniques.

That's it for Today, see you again with yet another Good topic until then bye bye

Durga Chikkala signing off...


To view or add a comment, sign in

More articles by Durga Chikkala

  • 🚀 Mastering Efficient UI Rendering: The Power of Virtual DOM.

    Hey everyone, How are you doing? It's been a very long time connecting with you all. I been writing React code from…

  • Branching Out: A Deep Dive into Tree Data Structure Part-1

    A Tree is a hierarchical data structure that contains nodes and connections to other nodes. each node will hold the…

  • Let's Deep dive into Recursion

    Hey everyone Welcome back to the tech world created by Durga chikkala, Today we will discuss recursion from zero to…

  • AES(Advanced Encryption Standard)

    Recently I have been building one of my personal projects and I want the data to be stored in a very secure way I am…

  • Learn GCP with Me (Part - 2)

    Hey Everyone welcome back to the most exciting series Learn GCP with me Part 2 if you haven't read my Part 1 please go…

  • Learn GCP with Me (Part - 1)

    Hey Everyone Welcome Back, It's been many days since we started a new series. Today I was thinking about what series we…

    2 Comments
  • Let's Explore Bloom Filter.....

    Hey everyone I am back with a new and exciting topic called Bloom Filter what is Bloom Filter: A Bloom filter is a…

  • Did you know these function types in Go?

    Hey everyone welcome back to yet another new article, I Hope you guys are doing well. Okay, let's not waste our time;…

  • Maximizing Efficiency: Unveiling the Power of Thread Pools

    Hey everyone, Welcome back Today we are back with a new and very useful topic called Thread Pools or Worker Pools. Have…

  • Learn Next.js with Me(Part-1)

    Hey Everyone, Welcome back I am Durga Chikkala a software engineer by profession and entrepreneur by passion…

Explore content categories