---
name: rate_limiter_by_key
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func RateLimiterByKey(rl *RateLimiter, keyFunc func(r *http.Request) string) Middleware"
description: "Middleware HTTP configurable que aplica rate limiting con un extractor de clave custom. Permite limitar por API key, user ID, header arbitrario, etc. Si keyFunc devuelve cadena vacia el request pasa sin limit."
tags: [rate_limit, http, middleware, custom, key, server, infra]
uses_functions: [rate_limiter_check_go_infra, rate_limit_headers_go_infra, http_error_response_go_infra]
uses_types: [RateLimiter_go_infra, Middleware_go_infra, HTTPError_go_infra]
returns: [Middleware_go_infra]
returns_optional: false
error_type: "error_go_core"
imports: [net/http]
params:
  - name: rl
    desc: "puntero al RateLimiter compartido entre todos los requests"
  - name: keyFunc
    desc: "funcion que extrae la clave del request (API key, user ID, etc.). Cadena vacia salta el limit"
output: "Middleware que aplica rate limit segun keyFunc, responde 429 con HTTPError JSON al exceder"
tested: true
tests: ["aplica limit por la clave devuelta por keyFunc", "key vacia salta el limit", "responde 429 con body JSON al exceder", "headers X-RateLimit-* siempre presentes en respuesta"]
test_file_path: "functions/infra/rate_limiter_by_key_test.go"
file_path: "functions/infra/rate_limiter_by_key.go"
---

## Ejemplo

```go
// Limit por API key
rl := RateLimiterNew(100, 200)
mw := RateLimiterByKey(rl, func(r *http.Request) string {
    return r.Header.Get("X-API-Key")
})

// Limit por user ID extraido del JWT (suponiendo middleware previo que lo setea)
mwUser := RateLimiterByKey(rl, func(r *http.Request) string {
    return r.Context().Value("user_id").(string)
})
```

## Notas

Funcion impura — el middleware muta el estado del limiter. Reutiliza `RateLimiterCheck`, `RateLimitHeaders` y `HTTPErrorResponse` del registry. Si el keyFunc devuelve "" se interpreta como "sin clave identificable" y se deja pasar el request: util para endpoints publicos donde solo se quiere limitar requests autenticados. La respuesta 429 sigue el formato JSON estandar `{"status":429,"code":"rate_limited","message":"too many requests"}`.
