Documentation Index Fetch the complete documentation index at: https://mintlify.com/go-kratos/kratos/llms.txt
Use this file to discover all available pages before exploring further.
Kratos provides a metadata system for passing key-value pairs between services. Metadata is used for cross-cutting concerns like tracing, authentication, and request context.
type Metadata map [ string ][] string
Metadata is a map of string keys to string slice values, similar to HTTP headers.
metadata/metadata.go:16-26
import " github.com/go-kratos/kratos/v2/metadata "
// Create new metadata
md := metadata . New ( map [ string ][] string {
"x-request-id" : { "12345" },
"x-user-id" : { "user-123" },
})
// Create from multiple maps
md := metadata . New (
map [ string ][] string { "key1" : { "val1" }},
map [ string ][] string { "key2" : { "val2" }},
)
Add
Add a value to a key (creates or appends):
metadata/metadata.go:29-36
md := metadata . New ()
md . Add ( "x-request-id" , "12345" )
md . Add ( "x-tags" , "tag1" )
md . Add ( "x-tags" , "tag2" ) // Appends to existing key
// Result: {"x-request-id": ["12345"], "x-tags": ["tag1", "tag2"]}
Get
Get the first value for a key:
metadata/metadata.go:39-45
value := md . Get ( "x-request-id" ) // Returns "12345"
empty := md . Get ( "nonexistent" ) // Returns ""
Set
Set a single value (replaces existing):
metadata/metadata.go:48-53
md . Set ( "x-request-id" , "new-id" )
// Replaces all values for the key
Values
Get all values for a key:
metadata/metadata.go:65-67
values := md . Values ( "x-tags" ) // Returns []string{"tag1", "tag2"}
Range
Iterate over all metadata:
metadata/metadata.go:56-62
md . Range ( func ( key string , values [] string ) bool {
log . Infof ( " %s : %v " , key , values )
return true // Continue iteration
})
Clone
Create a deep copy:
metadata/metadata.go:70-76
original := metadata . New ( map [ string ][] string {
"key" : { "value" },
})
cloned := original . Clone ()
cloned . Set ( "key" , "modified" ) // Doesn't affect original
Server Context
Extract metadata from incoming requests:
metadata/metadata.go:81-83
import " github.com/go-kratos/kratos/v2/metadata "
func MyHandler ( ctx context . Context , req * pb . Request ) ( * pb . Response , error ) {
// Extract metadata from context
if md , ok := metadata . FromServerContext ( ctx ); ok {
requestID := md . Get ( "x-request-id" )
userID := md . Get ( "x-user-id" )
log . Infof ( "Request: %s , User: %s " , requestID , userID )
}
return & pb . Response {}, nil
}
metadata/metadata.go:81-83
import " github.com/go-kratos/kratos/v2/metadata "
md := metadata . New ( map [ string ][] string {
"x-service" : { "my-service" },
})
ctx = metadata . NewServerContext ( ctx , md )
Client Context
Add metadata to outgoing requests:
metadata/metadata.go:94-96
import " github.com/go-kratos/kratos/v2/metadata "
func CallService ( ctx context . Context ) error {
// Create metadata
md := metadata . New ( map [ string ][] string {
"x-request-id" : { generateRequestID ()},
"x-user-id" : { getUserID ()},
})
// Add to context
ctx = metadata . NewClientContext ( ctx , md )
// Make request - metadata automatically sent
reply , err := client . GetUser ( ctx , & pb . GetUserRequest {
Id : 123 ,
})
return err
}
Append to Context
Add metadata to existing context:
metadata/metadata.go:105-116
import " github.com/go-kratos/kratos/v2/metadata "
// Append key-value pairs
ctx = metadata . AppendToClientContext ( ctx ,
"x-request-id" , "12345" ,
"x-user-id" , "user-123" ,
)
Merge new metadata into context:
metadata/metadata.go:119-126
newMD := metadata . New ( map [ string ][] string {
"x-trace-id" : { "trace-123" },
})
ctx = metadata . MergeToClientContext ( ctx , newMD )
HTTP Integration
Metadata automatically maps to HTTP headers:
Server Side
import (
" github.com/go-kratos/kratos/v2/transport/http "
" github.com/go-kratos/kratos/v2/middleware/metadata "
)
// Add metadata middleware
httpSrv := http . NewServer (
http . Middleware (
metadata . Server (), // Extracts headers into metadata
),
)
// Access in handler
func HandleRequest ( ctx http . Context ) error {
if md , ok := metadata . FromServerContext ( ctx ); ok {
userAgent := md . Get ( "user-agent" )
authorization := md . Get ( "authorization" )
}
return nil
}
Client Side
import (
" github.com/go-kratos/kratos/v2/transport/http "
" github.com/go-kratos/kratos/v2/middleware/metadata "
)
client , _ := http . NewClient (
ctx ,
http . WithMiddleware (
metadata . Client (), // Injects metadata as headers
),
)
// Add metadata
ctx = metadata . AppendToClientContext ( ctx ,
"x-api-key" , "secret-key" ,
)
// Make request - metadata sent as headers
var reply Response
client . Invoke ( ctx , "GET" , "/api/resource" , nil , & reply )
gRPC Integration
Metadata maps to gRPC metadata:
Server Side
import (
" github.com/go-kratos/kratos/v2/transport/grpc "
" github.com/go-kratos/kratos/v2/middleware/metadata "
)
grpcSrv := grpc . NewServer (
grpc . Middleware (
metadata . Server (), // Extracts gRPC metadata
),
)
func ( s * Service ) GetUser ( ctx context . Context , req * pb . GetUserRequest ) ( * pb . User , error ) {
if md , ok := metadata . FromServerContext ( ctx ); ok {
traceID := md . Get ( "x-trace-id" )
}
return & pb . User {}, nil
}
Client Side
import (
" github.com/go-kratos/kratos/v2/transport/grpc "
" github.com/go-kratos/kratos/v2/middleware/metadata "
)
conn , _ := grpc . DialInsecure (
ctx ,
grpc . WithMiddleware (
metadata . Client (), // Injects as gRPC metadata
),
)
client := pb . NewUserServiceClient ( conn )
ctx = metadata . AppendToClientContext ( ctx ,
"x-trace-id" , "trace-123" ,
)
reply , err := client . GetUser ( ctx , & pb . GetUserRequest { Id : 1 })
Common Use Cases
Request ID Propagation
import " github.com/google/uuid "
func RequestIDMiddleware () middleware . Middleware {
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface {}) ( interface {}, error ) {
md , _ := metadata . FromServerContext ( ctx )
requestID := md . Get ( "x-request-id" )
if requestID == "" {
requestID = uuid . New (). String ()
}
// Add to outgoing requests
ctx = metadata . AppendToClientContext ( ctx ,
"x-request-id" , requestID ,
)
return handler ( ctx , req )
}
}
}
User Authentication
func AuthMiddleware () middleware . Middleware {
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface {}) ( interface {}, error ) {
md , _ := metadata . FromServerContext ( ctx )
token := md . Get ( "authorization" )
userID , err := validateToken ( token )
if err != nil {
return nil , errors . Unauthorized ( "INVALID_TOKEN" , "invalid token" )
}
// Propagate user ID
ctx = metadata . AppendToClientContext ( ctx ,
"x-user-id" , userID ,
)
return handler ( ctx , req )
}
}
}
Trace Context Propagation
func TracingMiddleware () middleware . Middleware {
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface {}) ( interface {}, error ) {
md , _ := metadata . FromServerContext ( ctx )
traceID := md . Get ( "x-trace-id" )
spanID := md . Get ( "x-span-id" )
if traceID == "" {
traceID = generateTraceID ()
}
// Propagate to downstream services
ctx = metadata . AppendToClientContext ( ctx ,
"x-trace-id" , traceID ,
"x-span-id" , generateSpanID (),
"x-parent-span-id" , spanID ,
)
return handler ( ctx , req )
}
}
}
Best Practices
Always use the metadata middleware for automatic header/metadata conversion.
Metadata keys are automatically lowercased. Use lowercase keys consistently.
Use prefixes like x- for custom metadata keys to avoid conflicts.
Metadata is meant for small pieces of data. Don’t store large payloads.
Only propagate metadata that’s needed downstream. Don’t blindly forward everything.
Always validate metadata values before using them, especially for security-sensitive data.
Complete Example
package main
import (
" context "
" github.com/go-kratos/kratos/v2/middleware "
" github.com/go-kratos/kratos/v2/middleware/metadata "
" github.com/go-kratos/kratos/v2/transport/http "
" github.com/go-kratos/kratos/v2/transport/grpc "
" github.com/google/uuid "
)
// Middleware to ensure request ID
func RequestID () middleware . Middleware {
return func ( handler middleware . Handler ) middleware . Handler {
return func ( ctx context . Context , req interface {}) ( interface {}, error ) {
md , _ := metadata . FromServerContext ( ctx )
requestID := md . Get ( "x-request-id" )
if requestID == "" {
requestID = uuid . New (). String ()
md . Set ( "x-request-id" , requestID )
}
// Propagate to downstream
ctx = metadata . NewClientContext ( ctx , md )
return handler ( ctx , req )
}
}
}
func main () {
// HTTP Server
httpSrv := http . NewServer (
http . Middleware (
metadata . Server (), // Extract HTTP headers
RequestID (), // Ensure request ID
),
)
// gRPC Server
grpcSrv := grpc . NewServer (
grpc . Middleware (
metadata . Server (), // Extract gRPC metadata
RequestID (), // Ensure request ID
),
)
// HTTP Client
httpClient , _ := http . NewClient (
context . Background (),
http . WithMiddleware (
metadata . Client (), // Inject as HTTP headers
),
)
// gRPC Client
grpcConn , _ := grpc . DialInsecure (
context . Background (),
grpc . WithMiddleware (
metadata . Client (), // Inject as gRPC metadata
),
)
}
HTTP Transport HTTP header handling
gRPC Transport gRPC metadata handling
Middleware Metadata middleware
Transport Transport headers