Giter Site home page Giter Site logo

versioning's Introduction

API Versioning (Go)

build status report card godocs donate on PayPal

Semver versioning for your APIs. It implements all the suggestions written at api-guidelines and more.

The version comparison is done by the go-version package. It supports matching over patterns like ">= 1.0, < 3" and e.t.c.

Getting started

The only requirement is the Go Programming Language.

$ go get github.com/kataras/versioning

Features

  • Per route version matching, an http.Handler with "switch" cases via versioning.Map for version => handler
  • Per group versioned routes and deprecation API
  • Version matching like ">= 1.0, < 2.0" or just "2.0.1" and e.t.c.
  • Version not found handler (can be customized by simply adding the versioning.NotFound: customNotMatchVersionHandler on the Map)
  • Version is retrieved from the "Accept" and "Accept-Version" headers (can be customized through request's context key)
  • Respond with "X-API-Version" header, if version found.
  • Deprecation options with customizable "X-API-Warn", "X-API-Deprecation-Date", "X-API-Deprecation-Info" headers via Deprecated wrapper.

Compare Versions

// If reports whether the "version" is a valid match to the "is".
// The "is" can be a version constraint like ">= 1, < 3".
If(version string, is string) bool
// Match reports whether the current version matches the "expectedVersion".
Match(r *http.Request, expectedVersion string) bool

Example

router.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
    if versioning.Match(r, ">= 2.2.3") {
        // [logic for >= 2.2.3 version of your handler goes here]
        return
    }
})

Determining The Current Version

Current request version is retrieved by versioning.GetVersion(r *http.Request).

By default the GetVersion will try to read from:

  • Accept header, i.e Accept: "application/json; version=1.0"
  • Accept-Version header, i.e Accept-Version: "1.0"
func handler(w http.ResponseWriter, r *http.Request){
    currentVersion := versioning.GetVersion(r)
}

You can also set a custom version to a handler trough a middleware by setting a request context's value. For example:

import (
    "context"
    "net/http"

    "github.com/kataras/versioning"
)

func urlParamVersion(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        version := r.URL.Query().Get("v") // ?v=2.3.5
        if version == "" {
            // set a default version, e.g. 1.0
            version = "1.0"
        }
        r = r.WithContext(versioning.WithVersion(r.Context(), version))
        next.ServeHTTP(w, r)
    })
}

Map Versions to Handlers

The versioning.NewMatcher(versioning.Map) http.Handler creates a single handler which decides what handler need to be executed based on the requested version.

// middleware for all versions.
func myMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        // [...]
        next.ServeHTTP(w, r)
    })
}

func myCustomVersionNotFound(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(404)
    fmt.Fprintf(w, "%s version not found", versioning.GetVersion(r))
}

router := http.NewServeMux()
router.Handle("/", myMiddleware(versioning.NewMatcher(versioning.Map{
    // v1Handler is a handler of yuors that will be executed only on version 1.
    "1.0":               v1Handler, 
    ">= 2, < 3":         v2Handler,
    versioning.NotFound: http.HandlerFunc(myCustomNotVersionFound),
})))

Deprecation

Using the versioning.Deprecated(handler http.Handler, options versioning.DeprecationOptions) http.Handler function you can mark a specific handler version as deprecated.

v1Handler = versioning.Deprecated(v1Handler, versioning.DeprecationOptions{
    // if empty defaults to: "WARNING! You are using a deprecated version of this API."
    WarnMessage string
    DeprecationDate time.Time
    DeprecationInfo string
})

router.Handle("/", versioning.NewMatcher(versioning.Map{
    "1.0": v1Handler,
    // [...]
}))

This will make the handler to send these headers to the client:

  • "X-API-Warn": options.WarnMessage
  • "X-API-Deprecation-Date": options.DeprecationDate
  • "X-API-Deprecation-Info": options.DeprecationInfo

versioning.DefaultDeprecationOptions can be passed instead if you don't care about Date and Info.

Grouping Routes By Version

Grouping routes by version is possible as well.

Using the versioning.NewGroup(version string) *versioning.Group function you can create a group to register your versioned routes. The versioning.RegisterGroups(r *http.ServeMux, versionNotFoundHandler http.Handler, groups ...*versioning.Group) must be called in the end in order to register the routes to a specific StdMux.

router := http.NewServeMux()

// version 1.
usersAPIV1 := versioning.NewGroup(">= 1, < 2")
usersAPIV1.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
        return
    }

    w.Write([]byte("v1 resource: /api/users handler"))
})
usersAPIV1.HandleFunc("/api/users/new", func(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
        return
    }

    w.Write([]byte("v1 resource: /api/users/new post handler"))
})

// version 2.
usersAPIV2 := versioning.NewGroup(">= 2, < 3")
usersAPIV2.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
        return
    }

    w.Write([]byte("v2 resource: /api/users handler"))
})
usersAPIV2.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
        return
    }

    w.Write([]byte("v2 resource: /api/users post handler"))
})

versioning.RegisterGroups(router, versioning.NotFoundHandler, usersAPIV1, usersAPIV2)

A middleware can be registered, using the methods we learnt above, i.e by using the versioning.Match in order to detect what code/handler you want to be executed when "x" or no version is requested.

Deprecation for Group

Just call the Group#Deprecated(versioning.DeprecationOptions) on the group you want to notify your API consumers that this specific version is deprecated.

userAPIV1 := versioning.NewGroup("1.0").Deprecated(versioning.DefaultDeprecationOptions)

For a more detailed technical documentation you can head over to our godocs. And for executable code you can always visit the _examples repository's subdirectory.

License

kataras/versioning is free and open-source software licensed under the MIT License.

versioning's People

Contributors

anjannath avatar dependabot[bot] avatar kataras avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

anjannath lcpinto

versioning's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.