Skip to main content
The Metadata API provides utilities for managing request metadata (headers) across RPC boundaries in both HTTP and gRPC transports.

Types

Metadata

Represents request headers internally.
type Metadata map[string][]string
Metadata translates back and forth from transport headers (HTTP headers or gRPC metadata).

Functions

New

Creates Metadata from key-value maps.
func New(mds ...map[string][]string) Metadata
mds
...map[string][]string
Variable number of key-value maps to merge into Metadata
metadata
Metadata
New Metadata instance with merged values
Example:
md := metadata.New(
    map[string][]string{
        "authorization": {"Bearer token123"},
        "x-request-id":  {"req-001"},
    },
)

NewServerContext

Creates a new context with server metadata attached.
func NewServerContext(ctx context.Context, md Metadata) context.Context
ctx
context.Context
Parent context
md
Metadata
Metadata to attach
context
context.Context
Context with server metadata

FromServerContext

Returns the server metadata from context.
func FromServerContext(ctx context.Context) (Metadata, bool)
ctx
context.Context
Context to retrieve metadata from
metadata
Metadata
Retrieved metadata
ok
bool
Whether metadata was found in the context
Example:
if md, ok := metadata.FromServerContext(ctx); ok {
    token := md.Get("authorization")
    fmt.Printf("Token: %s\n", token)
}

NewClientContext

Creates a new context with client metadata attached.
func NewClientContext(ctx context.Context, md Metadata) context.Context
ctx
context.Context
Parent context
md
Metadata
Metadata to attach
context
context.Context
Context with client metadata

FromClientContext

Returns the client metadata from context.
func FromClientContext(ctx context.Context) (Metadata, bool)
ctx
context.Context
Context to retrieve metadata from
metadata
Metadata
Retrieved metadata
ok
bool
Whether metadata was found in the context

AppendToClientContext

Returns a new context with provided key-value pairs merged into existing metadata.
func AppendToClientContext(ctx context.Context, kv ...string) context.Context
ctx
context.Context
Context with existing metadata
kv
...string
Key-value pairs to add (must be even number of arguments)
context
context.Context
Context with appended metadata
Example:
ctx = metadata.AppendToClientContext(ctx,
    "x-trace-id", "trace-123",
    "x-user-id", "user-456",
)

MergeToClientContext

Merges new metadata into the client context.
func MergeToClientContext(ctx context.Context, cmd Metadata) context.Context
ctx
context.Context
Context with existing metadata
cmd
Metadata
Metadata to merge
context
context.Context
Context with merged metadata
Example:
md := metadata.New(map[string][]string{
    "authorization": {"Bearer token"},
    "content-type":  {"application/json"},
})
ctx = metadata.MergeToClientContext(ctx, md)

Metadata Methods

Add

Adds a key-value pair to the metadata.
func (m Metadata) Add(key, value string)
key
string
Header key (case-insensitive, stored as lowercase)
value
string
Header value to add
Example:
md := metadata.New()
md.Add("x-custom-header", "value1")
md.Add("x-custom-header", "value2")
// Results in: x-custom-header: ["value1", "value2"]

Get

Returns the first value associated with the key.
func (m Metadata) Get(key string) string
key
string
Header key to retrieve (case-insensitive)
value
string
First value associated with the key, or empty string if not found

Set

Stores a key-value pair, replacing existing values.
func (m Metadata) Set(key string, value string)
key
string
Header key (case-insensitive)
value
string
Header value to set
Example:
md.Set("authorization", "Bearer new-token")

Values

Returns all values associated with the key.
func (m Metadata) Values(key string) []string
key
string
Header key to retrieve (case-insensitive)
values
[]string
All values associated with the key

Range

Iterates over all elements in the metadata.
func (m Metadata) Range(f func(k string, v []string) bool)
f
func(k string, v []string) bool
Function called for each key-value pair. Return false to stop iteration.
Example:
md.Range(func(key string, values []string) bool {
    fmt.Printf("%s: %v\n", key, values)
    return true // continue iteration
})

Clone

Returns a deep copy of the metadata.
func (m Metadata) Clone() Metadata
metadata
Metadata
Cloned metadata instance

Usage Examples

Server-side: Reading Request Headers

package main

import (
    "context"
    "fmt"
    "github.com/go-kratos/kratos/v2/metadata"
)

func handleRequest(ctx context.Context) {
    if md, ok := metadata.FromServerContext(ctx); ok {
        // Get single header value
        token := md.Get("authorization")
        userAgent := md.Get("user-agent")
        
        // Get all values for a key
        cookies := md.Values("cookie")
        
        fmt.Printf("Token: %s\n", token)
        fmt.Printf("User-Agent: %s\n", userAgent)
        fmt.Printf("Cookies: %v\n", cookies)
        
        // Iterate all headers
        md.Range(func(key string, values []string) bool {
            fmt.Printf("%s: %v\n", key, values)
            return true
        })
    }
}

Client-side: Adding Request Headers

func makeRequest(ctx context.Context) {
    // Method 1: Using AppendToClientContext
    ctx = metadata.AppendToClientContext(ctx,
        "authorization", "Bearer token123",
        "x-request-id", "req-001",
        "x-user-id", "user-456",
    )
    
    // Method 2: Creating and merging metadata
    md := metadata.New(map[string][]string{
        "content-type": {"application/json"},
        "accept":       {"application/json"},
    })
    ctx = metadata.MergeToClientContext(ctx, md)
    
    // Make gRPC or HTTP request with ctx
    client.SomeMethod(ctx, req)
}

Middleware: Forwarding Headers

import (
    "github.com/go-kratos/kratos/v2/middleware"
    "github.com/go-kratos/kratos/v2/metadata"
)

func ForwardHeaders() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            // Extract headers from incoming request
            if md, ok := metadata.FromServerContext(ctx); ok {
                // Forward specific headers to downstream services
                ctx = metadata.AppendToClientContext(ctx,
                    "x-request-id", md.Get("x-request-id"),
                    "x-trace-id", md.Get("x-trace-id"),
                    "authorization", md.Get("authorization"),
                )
            }
            
            return handler(ctx, req)
        }
    }
}

Authentication Middleware

func Authentication() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            md, ok := metadata.FromServerContext(ctx)
            if !ok {
                return nil, errors.Unauthorized("UNAUTHORIZED", "missing metadata")
            }
            
            token := md.Get("authorization")
            if token == "" {
                return nil, errors.Unauthorized("UNAUTHORIZED", "missing auth token")
            }
            
            // Validate token
            user, err := validateToken(token)
            if err != nil {
                return nil, errors.Unauthorized("UNAUTHORIZED", "invalid token")
            }
            
            // Add user info to context
            ctx = metadata.AppendToClientContext(ctx,
                "x-user-id", user.ID,
                "x-user-role", user.Role,
            )
            
            return handler(ctx, req)
        }
    }
}

Request Tracing

import (
    "github.com/google/uuid"
    "github.com/go-kratos/kratos/v2/metadata"
)

func TraceMiddleware() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            md, _ := metadata.FromServerContext(ctx)
            
            // Get or generate trace ID
            traceID := md.Get("x-trace-id")
            if traceID == "" {
                traceID = uuid.New().String()
            }
            
            // Get or generate request ID
            requestID := md.Get("x-request-id")
            if requestID == "" {
                requestID = uuid.New().String()
            }
            
            // Forward to downstream services
            ctx = metadata.AppendToClientContext(ctx,
                "x-trace-id", traceID,
                "x-request-id", requestID,
            )
            
            return handler(ctx, req)
        }
    }
}

Custom Metadata Operations

func customMetadataOps() {
    // Create metadata
    md := metadata.New()
    
    // Add headers
    md.Set("content-type", "application/json")
    md.Set("authorization", "Bearer token")
    md.Add("accept-language", "en-US")
    md.Add("accept-language", "en")
    
    // Get values
    contentType := md.Get("content-type")
    languages := md.Values("accept-language")
    
    // Clone for modification
    mdCopy := md.Clone()
    mdCopy.Set("x-custom", "value")
    
    // Iterate
    md.Range(func(key string, values []string) bool {
        fmt.Printf("%s: %v\n", key, values)
        return true
    })
}

Build docs developers (and LLMs) love