Documentation Index Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
The mobile package provides robust API versioning middleware that supports version detection from headers, query parameters, and URL paths, with built-in deprecation warnings and graceful migration paths.
Quick Start
import " github.com/go-mizu/mizu/mobile "
app := mizu . New ()
// Add version middleware
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 2 , 0 }, { 1 , 0 }},
Deprecated : [] mobile . Version {{ 1 , 0 }},
Default : mobile . Version { Major : 2 },
}))
app . Get ( "/api/users" , func ( c * mizu . Ctx ) error {
version := mobile . VersionFromCtx ( c )
if version . AtLeast ( 2 , 0 ) {
return c . JSON ( 200 , v2Response )
}
return c . JSON ( 200 , v1Response )
})
The Version Type
type Version struct {
Major int
Minor int
}
Creating Versions
// Major version only
v1 := mobile . Version { Major : 1 } // v1
v2 := mobile . Version { Major : 2 } // v2
// Major.Minor versions
v1_5 := mobile . Version { Major : 1 , Minor : 5 } // v1.5
v2_1 := mobile . Version { Major : 2 , Minor : 1 } // v2.1
Version Methods
version := mobile . Version { Major : 2 , Minor : 1 }
// String representation
version . String () // "v2.1"
// Check if zero/unset
version . IsZero () // false
// Compare versions
version . Compare ( mobile . Version { Major : 2 , Minor : 0 }) // 1 (greater)
version . Compare ( mobile . Version { Major : 2 , Minor : 1 }) // 0 (equal)
version . Compare ( mobile . Version { Major : 3 , Minor : 0 }) // -1 (less)
// Convenience comparisons
version . AtLeast ( 2 , 0 ) // true
version . AtLeast ( 2 , 1 ) // true
version . AtLeast ( 2 , 2 ) // false
version . Before ( 3 , 0 ) // true
version . Before ( 2 , 1 ) // false
Parsing Versions
// Parse from string
v , err := mobile . ParseVersion ( "v2.1" ) // Version{2, 1}
v , err := mobile . ParseVersion ( "v2" ) // Version{2, 0}
v , err := mobile . ParseVersion ( "2.1" ) // Version{2, 1}
v , err := mobile . ParseVersion ( "2" ) // Version{2, 0}
Version Middleware
Configuration Options
type VersionOptions struct {
// Header is the version header name
// Default: "X-API-Version"
Header string
// QueryParam is an alternative query parameter for version
// Default: "" (disabled)
QueryParam string
// PathPrefix enables extraction from URL path prefix (e.g., /v1/...)
// Default: false
PathPrefix bool
// Default is the default version when none specified
// Default: Version{Major: 1}
Default Version
// Supported lists all supported versions
// Empty means no validation
Supported [] Version
// Deprecated lists deprecated versions (still work but warn)
Deprecated [] Version
// OnUnsupported handles unsupported version requests
OnUnsupported func ( c * mizu . Ctx , v Version ) error
// EchoVersion includes X-API-Version in response
// Default: true
EchoVersion bool
}
Basic Setup
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 3 , 0 }, { 2 , 0 }, { 1 , 0 }},
Deprecated : [] mobile . Version {{ 1 , 0 }},
Default : mobile . Version { Major : 3 },
}))
Version Detection Sources
The middleware checks version from multiple sources:
1. Header (default)
curl -H "X-API-Version: v2" http://localhost:3000/api/users
2. Query Parameter
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
QueryParam : "version" ,
}))
curl "http://localhost:3000/api/users?version=v2"
3. URL Path Prefix
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
PathPrefix : true ,
}))
curl http://localhost:3000/v2/api/users
Priority order: Header > Query > Path > Default
Supported Versions
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {
{ 3 , 0 }, // v3 - latest
{ 2 , 0 }, // v2 - still supported
{ 1 , 0 }, // v1 - deprecated but works
},
}))
Unsupported versions return 400 Bad Request:
{
"code" : "invalid_request" ,
"message" : "Unsupported API version: v4" ,
"details" : {
"requested" : "v4" ,
"supported" : [ "v3" , "v2" , "v1" ]
}
}
Deprecated Versions
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 3 , 0 }, { 2 , 0 }, { 1 , 0 }},
Deprecated : [] mobile . Version {{ 1 , 0 }},
}))
Deprecated versions work but add response header:
Custom Unsupported Handler
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 2 , 0 }},
OnUnsupported : func ( c * mizu . Ctx , v mobile . Version ) error {
return c . JSON ( 400 , map [ string ] any {
"error" : "Version not supported" ,
"requested" : v . String (),
"available" : [] string { "v2" },
"docs" : "https://api.example.com/docs/migration" ,
})
},
}))
Using Version Context
Access in Handlers
func handler ( c * mizu . Ctx ) error {
version := mobile . VersionFromCtx ( c )
// Returns zero Version if middleware not applied
if version . IsZero () {
version = mobile . Version { Major : 1 } // Default
}
return c . JSON ( 200 , map [ string ] any {
"api_version" : version . String (),
})
}
Version-Aware Logic
func getUsers ( c * mizu . Ctx ) error {
version := mobile . VersionFromCtx ( c )
users := fetchUsers ()
// v3: New response format
if version . AtLeast ( 3 , 0 ) {
return c . JSON ( 200 , V3Response {
Data : users ,
Meta : getMeta (),
Links : getLinks (),
})
}
// v2: Added pagination
if version . AtLeast ( 2 , 0 ) {
return c . JSON ( 200 , V2Response {
Data : users ,
Total : len ( users ),
Page : 1 ,
PerPage : 20 ,
})
}
// v1: Simple array
return c . JSON ( 200 , users )
}
Switch-Based Routing
func handler ( c * mizu . Ctx ) error {
version := mobile . VersionFromCtx ( c )
switch {
case version . AtLeast ( 3 , 0 ):
return handleV3 ( c )
case version . AtLeast ( 2 , 0 ):
return handleV2 ( c )
default :
return handleV1 ( c )
}
}
Field-Level Versioning
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username,omitempty"` // v2+
Avatar string `json:"avatar,omitempty"` // v3+
}
func getUser ( c * mizu . Ctx ) error {
version := mobile . VersionFromCtx ( c )
user := fetchUser ( c . Param ( "id" ))
// Create versioned response
response := User {
ID : user . ID ,
Name : user . Name ,
Email : user . Email ,
}
if version . AtLeast ( 2 , 0 ) {
response . Username = user . Username
}
if version . AtLeast ( 3 , 0 ) {
response . Avatar = user . Avatar
}
return c . JSON ( 200 , response )
}
Migration Patterns
Adding New Fields (Non-Breaking)
// v1 response
type UserV1 struct {
ID int `json:"id"`
Name string `json:"name"`
}
// v2 adds email (backward compatible)
type UserV2 struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"` // New field
}
func getUser ( c * mizu . Ctx ) error {
v := mobile . VersionFromCtx ( c )
user := fetchUser ()
if v . AtLeast ( 2 , 0 ) {
return c . JSON ( 200 , UserV2 {
ID : user . ID ,
Name : user . Name ,
Email : user . Email ,
})
}
return c . JSON ( 200 , UserV1 {
ID : user . ID ,
Name : user . Name ,
})
}
Renaming Fields (Breaking)
// v1: uses "fullname"
type UserV1 struct {
ID int `json:"id"`
Fullname string `json:"fullname"`
}
// v2: renamed to "name"
type UserV2 struct {
ID int `json:"id"`
Name string `json:"name"`
}
func getUser ( c * mizu . Ctx ) error {
v := mobile . VersionFromCtx ( c )
user := fetchUser ()
if v . AtLeast ( 2 , 0 ) {
return c . JSON ( 200 , UserV2 { ID : user . ID , Name : user . Name })
}
// v1: Use old field name
return c . JSON ( 200 , UserV1 { ID : user . ID , Fullname : user . Name })
}
Changing Data Types (Breaking)
// v1: role is a string
type UserV1 struct {
ID int `json:"id"`
Role string `json:"role"` // "admin", "user"
}
// v2: role is an object
type UserV2 struct {
ID int `json:"id"`
Role RoleInfo `json:"role"`
}
type RoleInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Permissions [] string `json:"permissions"`
}
func getUser ( c * mizu . Ctx ) error {
v := mobile . VersionFromCtx ( c )
user := fetchUser ()
if v . AtLeast ( 2 , 0 ) {
return c . JSON ( 200 , UserV2 {
ID : user . ID ,
Role : getRoleInfo ( user . RoleID ),
})
}
return c . JSON ( 200 , UserV1 {
ID : user . ID ,
Role : user . RoleName ,
})
}
Deprecation Workflow
// Step 1: Mark as deprecated
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 3 , 0 }, { 2 , 0 }, { 1 , 0 }},
Deprecated : [] mobile . Version {{ 1 , 0 }},
}))
// Step 2: Log usage of deprecated versions
func deprecationLogger () mizu . Middleware {
return func ( next mizu . Handler ) mizu . Handler {
return func ( c * mizu . Ctx ) error {
v := mobile . VersionFromCtx ( c )
if v . Before ( 2 , 0 ) {
log . Warn ( "Deprecated API version used" ,
"version" , v . String (),
"path" , c . Request (). URL . Path ,
"device" , mobile . DeviceFromCtx ( c ). DeviceID ,
)
}
return next ( c )
}
}
}
// Step 3: Remove support (after migration period)
app . Use ( mobile . VersionMiddleware ( mobile . VersionOptions {
Supported : [] mobile . Version {{ 3 , 0 }, { 2 , 0 }}, // v1 removed
}))
Client Implementation
iOS (Swift)
class APIClient {
let apiVersion = "v2"
func request ( _ endpoint : String ) -> URLRequest {
var request = URLRequest ( url : URL ( string : baseURL + endpoint) ! )
request. setValue (apiVersion, forHTTPHeaderField : "X-API-Version" )
return request
}
func handleResponse ( _ response : HTTPURLResponse) {
if response. value ( forHTTPHeaderField : "X-API-Deprecated" ) == "true" {
// Warn user about deprecated API
NotificationCenter. default . post (
name : . apiDeprecated ,
object : nil
)
}
}
}
Android (Kotlin)
class VersionInterceptor ( private val apiVersion: String = "v2" ) : Interceptor {
override fun intercept (chain: Interceptor .Chain): Response {
val request = chain. request (). newBuilder ()
. header ( "X-API-Version" , apiVersion)
. build ()
val response = chain. proceed (request)
if (response. header ( "X-API-Deprecated" ) == "true" ) {
// Handle deprecation warning
EventBus. post ( ApiDeprecatedEvent ())
}
return response
}
}
Flutter (Dart)
class ApiClient {
static const apiVersion = 'v2' ;
Future < Response > request ( String endpoint) async {
final response = await http. get (
Uri . parse ( ' $ baseUrl $ endpoint ' ),
headers : { 'X-API-Version' : apiVersion},
);
if (response.headers[ 'x-api-deprecated' ] == 'true' ) {
// Show deprecation warning
showDeprecationWarning ();
}
return response;
}
}
Best Practices
Version Naming
Use semantic versioning: v1, v1.1, v2
Major versions for breaking changes
Minor versions for backward-compatible additions
Deprecation Timeline
Announce : Notify developers of upcoming deprecation
Warn : Add to deprecated list (X-API-Deprecated header)
Monitor : Track usage of deprecated versions
Remove : Remove from supported list
Documentation
Document all supported versions
Maintain migration guides
Provide changelogs per version
Next Steps
Structured Errors Return consistent error responses
Pagination Page and cursor-based pagination
Offline Sync Delta synchronization
API Reference Complete API documentation