Kong Hooks
Introduction
Kong Hooks provide a powerful mechanism to customize the behavior of the Kong API Gateway at various stages of the request/response lifecycle. Hooks allow developers to inject custom logic at specific points (or "phases") during the processing of an API request, without having to create a full-fledged plugin. They are essentially event listeners that get triggered at predefined moments, enabling you to modify requests or responses, implement custom logic, or integrate with external systems.
If you've ever needed to make small adjustments to how Kong handles requests or responses, but found creating an entire plugin to be excessive, hooks are the perfect solution. They're simpler to implement, focused on specific phases of request processing, and ideal for quick customizations.
Understanding Kong Request Lifecycle
Before diving into hooks, it's important to understand the request lifecycle within Kong:
Kong processes each API request through several phases:
- Access Phase: When Kong first receives a request
- Upstream Phase: When Kong sends the request to the backend service
- Response Phase: When Kong receives a response from the backend
- Header Filter Phase: Before sending headers back to client
- Body Filter Phase: Before sending the response body back to client
Hooks allow you to tap into specific points within these phases.
Types of Kong Hooks
Kong offers several types of hooks that correspond to different phases of request processing:
- request-transformers: Modify incoming requests
- response-transformers: Modify outgoing responses
- access-control: Implement custom authentication or authorization
- logging: Custom logging implementations
- error-handlers: Custom error processing
Basic Hook Structure
A Kong hook is typically structured as a Lua function that gets registered to a specific phase. Here's the basic structure:
local MyHook = {}
function MyHook:access(conf)
-- Custom logic for the access phase
kong.log.debug("Hook executed in access phase!")
-- Example: Add a custom header
kong.service.request.set_header("X-Custom-Hook", "true")
end
return MyHook
Setting Up Your First Hook
Let's create a simple hook that adds a custom header to every request:
Step 1: Create a Hook File
Create a file named custom-header-hook.lua
in your Kong project:
local CustomHeaderHook = {}
function CustomHeaderHook:access(conf)
-- Add a timestamp header to track when requests were processed
kong.service.request.set_header("X-Request-Timestamp", os.time())
kong.log.debug("Added timestamp header: " .. os.time())
end
return CustomHeaderHook
Step 2: Register the Hook in Kong
Add your hook to Kong's configuration in kong.conf
:
plugins = bundled,custom-header-hook
Or register it using the Admin API:
curl -X POST http://localhost:8001/plugins \
--data "name=custom-header-hook" \
--data "config.header_name=X-Request-Timestamp" \
--data "config.header_value=dynamic"
Hook Configuration
Hooks can accept configuration parameters, making them flexible and reusable. Here's how to define configuration parameters for your hook:
local RequestModifierHook = {}
-- Define the schema for configuration
RequestModifierHook.SCHEMA = {
name = "request-modifier-hook",
fields = {
{ header_name = { type = "string", required = true } },
{ header_value = { type = "string", required = true } }
}
}
function RequestModifierHook:access(conf)
-- Use the configuration parameters
kong.service.request.set_header(conf.header_name, conf.header_value)
kong.log.debug("Added header: " .. conf.header_name .. "=" .. conf.header_value)
end
return RequestModifierHook
Hook Priority and Execution Order
When multiple hooks are registered for the same phase, Kong executes them based on their priority settings. Higher priority hooks execute first:
local HighPriorityHook = {}
-- Set a high priority (higher numbers = higher priority)
HighPriorityHook.PRIORITY = 1000
function HighPriorityHook:access(conf)
kong.log.debug("This hook runs before hooks with lower priority")
end
return HighPriorityHook
Practical Examples
Let's explore some practical applications of Kong Hooks:
Example 1: Request Transformation Hook
This hook modifies incoming requests by adding correlation IDs for tracking:
local uuid = require "kong.tools.uuid"
local CorrelationHook = {}
function CorrelationHook:access(conf)
-- Generate a correlation ID if one doesn't exist
local correlation_id = kong.request.get_header("X-Correlation-ID")
if not correlation_id then
correlation_id = uuid()
kong.service.request.set_header("X-Correlation-ID", correlation_id)
end
-- Store it in Kong context for access in other phases
kong.ctx.shared.correlation_id = correlation_id
end
return CorrelationHook
Example 2: Response Transformation Hook
This hook modifies outgoing responses by adding CORS headers:
local CorsHook = {}
function CorsHook:header_filter(conf)
kong.response.set_header("Access-Control-Allow-Origin", conf.allow_origin or "*")
kong.response.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
kong.response.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
kong.response.set_header("Access-Control-Max-Age", "3600")
end
return CorsHook
Example 3: Error Handling Hook
This hook provides custom error responses:
local cjson = require "cjson"
local ErrorHandlerHook = {}
function ErrorHandlerHook:header_filter(conf)
local status = kong.response.get_status()
-- Only process error responses (4xx and 5xx)
if status >= 400 then
kong.response.set_header("Content-Type", "application/json")
end
end
function ErrorHandlerHook:body_filter(conf)
local status = kong.response.get_status()
-- Only process error responses (4xx and 5xx)
if status >= 400 then
local body = {
error = true,
status = status,
message = conf.error_messages[tostring(status)] or "An error occurred",
request_id = kong.ctx.shared.correlation_id or "unknown"
}
-- Replace the response body
kong.response.set_raw_body(cjson.encode(body))
end
end
return ErrorHandlerHook
Example 4: Advanced Rate Limiting Hook
This hook implements a custom rate limiting strategy:
local redis = require "resty.redis"
local IPRateLimitHook = {}
function IPRateLimitHook:access(conf)
-- Get client IP
local client_ip = kong.client.get_ip()
-- Connect to Redis
local red = redis:new()
red:set_timeout(conf.redis_timeout or 1000)
local ok, err = red:connect(conf.redis_host, conf.redis_port)
if not ok then
kong.log.err("Failed to connect to Redis: ", err)
return
end
-- Increment counter for this IP
local key = "rate_limit:" .. client_ip
local count, err = red:incr(key)
if not count then
kong.log.err("Redis error: ", err)
return
end
-- Set expiry on first request
if count == 1 then
red:expire(key, conf.window_size or 60)
end
-- Check if rate limit exceeded
if count > (conf.limit or 10) then
return kong.response.exit(429, { message = "Rate limit exceeded" })
end
-- Return connection to pool
local ok, err = red:set_keepalive()
if not ok then
kong.log.err("Failed to set keepalive: ", err)
end
end
return IPRateLimitHook
Debugging Hooks
Debugging Kong hooks can be challenging. Here are some tips:
- Use Kong's logging: Add
kong.log.debug()
statements in your hooks - Enable verbose logging in Kong: Set
log_level = debug
inkong.conf
- Use the Kong Admin API to check if your hook is registered:
curl -X GET http://localhost:8001/plugins
- Check HTTP headers to verify your hooks are modifying requests/responses:
curl -v http://your-kong-gateway/your-api-endpoint
Best Practices for Kong Hooks
- Keep hooks focused: Each hook should do one thing well
- Be mindful of performance: Hooks run on every request, so optimize for speed
- Handle errors gracefully: Never let your hook crash Kong
- Use namespaces for shared data: Use
kong.ctx.shared
for data that needs to be accessed across hooks - Test thoroughly: Verify your hooks under various conditions
- Document your hooks: Include comments explaining the purpose and behavior
Summary
Kong Hooks provide a lightweight, flexible approach to customizing Kong API Gateway behavior without creating full plugins. They allow you to inject logic at specific phases of request processing, making them perfect for simple customizations like adding headers, implementing CORS, or handling errors.
Key takeaways:
- Hooks are phase-specific event listeners in Kong
- They're simpler than plugins but powerful enough for many customization needs
- Common hook types include request transformers, response transformers, and access control
- Hooks can be configured with parameters to make them reusable
- Priority settings determine the execution order of multiple hooks
Exercises
- Create a hook that adds the current server time to every response
- Implement a hook that logs basic request metrics (path, method, status code)
- Build a hook that redirects specific URL patterns to new locations
- Create a hook that implements basic IP-based access control
- Develop a hook that modifies JSON response bodies to add a standard wrapper structure
Additional Resources
- Kong Documentation
- Kong Plugin Development Guide
- Lua Programming Language
- OpenResty Project (which Kong is built on)
- Kong Community Forums
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)