Plugin Development - Plugin Configuration

Most of the time, it makes sense for your plugin to be configurable to answer all of your users’ needs. Your plugin’s configuration is stored in the datastore for Kong to retrieve it and pass it to your handler.lua methods when the plugin is being executed.

The configuration consists of a Lua table in Kong that we call a schema. It contains key/value properties that the user will set when enabling the plugin through the Admin API. Kong provides you with a way of validating the user’s configuration for your plugin.

Your plugin’s configuration is being verified against your schema when a user issues a request to the Admin API to enable or update a plugin on a given Service, Route, or Consumer.

For example, a user performs the following request:

  1. $ curl -X POST http://kong:8001/services/<service-name-or-id>/plugins \
  2. -d "name=my-custom-plugin" \
  3. -d "config.foo=bar"

If all properties of the config object are valid according to your schema, then the API would return 201 Created and the plugin would be stored in the database along with its configuration:

  1. {
  2. foo = "bar"
  3. }

If the configuration is not valid, the Admin API would return 400 Bad Request and the appropriate error messages.

Module

  1. kong.plugins.<plugin_name>.schema

schema.lua specifications

This module is to return a Lua table with properties that will define how your plugins can later be configured by users. Available properties are:

Property nameLua typeDescription
namestringName of the plugin, e.g. key-auth.
fieldstableArray of field definitions.
entity_checksfunctionArray of conditional entity level validation checks.

All the plugins inherit some default fields which are:

Field nameLua typeDescription
idstringAuto-generated plugin id.
namestringName of the plugin, e.g. key-auth.
created_atnumberCreation time of the plugin configuration (seconds from epoch).
routetableRoute to which plugin is bound, if any.
servicetableService to which plugin is bound, if any.
consumertableConsumer to which plugin is bound when possible, if any.
protocolstableThe plugin will run on specified protocol(s).
enabledbooleanWhether or not the plugin is enabled.
tagstableThe tags for the plugin.

In most of the cases you can ignore most of those and use the defaults. Or let the user specify value when enabling a plugin.

Here is an example of a potential schema.lua file (with some overrides applied):

  1. local typedefs = require "kong.db.schema.typedefs"
  2. return {
  3. name = "<plugin-name>",
  4. fields = {
  5. {
  6. -- this plugin will only be applied to Services or Routes
  7. consumer = typedefs.no_consumer
  8. },
  9. {
  10. -- this plugin will only run within Nginx HTTP module
  11. protocols = typedefs.protocols_http
  12. },
  13. {
  14. config = {
  15. type = "record",
  16. fields = {
  17. -- Describe your plugin's configuration's schema here.
  18. },
  19. },
  20. },
  21. },
  22. entity_checks = {
  23. -- Describe your plugin's entity validation rules
  24. },
  25. }

Describing your configuration schema

The config.fields property of your schema.lua file describes the schema of your plugin’s configuration. It is a flexible array of field definitions where each field is a valid configuration property for your plugin, describing the rules for that property. For example:

  1. {
  2. name = "<plugin-name>",
  3. fields = {
  4. config = {
  5. type = "record",
  6. fields = {
  7. {
  8. some_string = {
  9. type = "string",
  10. required = false,
  11. },
  12. },
  13. {
  14. some_boolean = {
  15. type = "boolean",
  16. default = false,
  17. },
  18. },
  19. {
  20. some_array = {
  21. type = "array",
  22. elements = {
  23. type = "string",
  24. one_of = {
  25. "GET",
  26. "POST",
  27. "PUT",
  28. "DELETE",
  29. },
  30. },
  31. },
  32. },
  33. },
  34. },
  35. },
  36. }

Here is the list of some common (not all) accepted rules for a property (see the fields table above for examples):

RuleDescription
typeThe type of a property.
requiredWhether or not the property is required
defaultThe default value for the property when not specified
elementsField definition of array or set elements.
keysField definition of map keys.
valuesField definition of map values.
fieldsField definition(s) of record fields.

There are many more, but the above are commonly used.

You can also add field validators, to mention a few:

RuleDescription
betweenChecks that the input number is between allowed values.
eqChecks the equality of the input to allowed value.
neChecks the inequality of the input to allowed value.
gtChecks that the number is greater than given value.
len_eqChecks that the input string length is equal to the given value.
len_minChecks that the input string length is at least the given value.
len_maxChecks that the input string length is at most the given value.
matchChecks that the input string matches the given Lua pattern.
not_matchChecks that the input string doesn’t match the given Lua pattern.
match_allChecks that the input string matches all the given Lua patterns.
match_noneChecks that the input string doesn’t match any of the given Lua patterns.
match_anyChecks that the input string matches any of the given Lua patterns.
starts_withChecks that the input string starts with a given value.
one_ofChecks that the input string is one of the accepted values.
containsChecks that the input array contains the given value.
is_regexChecks that the input string is a valid regex pattern.
custom_validatorA custom validation function written in Lua.

There are some additional validators, but you get a good idea how you can specify validation rules on fields from the above table.

Examples

This schema.lua file is for the key-auth plugin:

  1. -- schema.lua
  2. local typedefs = require "kong.db.schema.typedefs"
  3. return {
  4. name = "key-auth",
  5. fields = {
  6. {
  7. consumer = typedefs.no_consumer
  8. },
  9. {
  10. protocols = typedefs.protocols_http
  11. },
  12. {
  13. config = {
  14. type = "record",
  15. fields = {
  16. {
  17. key_names = {
  18. type = "array",
  19. required = true,
  20. elements = typedefs.header_name,
  21. default = {
  22. "apikey",
  23. },
  24. },
  25. },
  26. {
  27. hide_credentials = {
  28. type = "boolean",
  29. default = false,
  30. },
  31. },
  32. {
  33. anonymous = {
  34. type = "string",
  35. uuid = true,
  36. legacy = true,
  37. },
  38. },
  39. {
  40. key_in_body = {
  41. type = "boolean",
  42. default = false,
  43. },
  44. },
  45. {
  46. run_on_preflight = {
  47. type = "boolean",
  48. default = true,
  49. },
  50. },
  51. },
  52. },
  53. },
  54. },
  55. }

Hence, when implementing the access() function of your plugin in handler.lua and given that the user enabled the plugin with the default values, you’d have access to:

  1. -- handler.lua
  2. local BasePlugin = require "kong.plugins.base_plugin"
  3. local kong = kong
  4. local CustomHandler = BasePlugin:extend()
  5. CustomHandler.VERSION = "1.0.0"
  6. CustomHandler.PRIORITY = 10
  7. function CustomHandler:new()
  8. CustomHandler.super.new(self, "my-custom-plugin")
  9. end
  10. function CustomHandler:access(config)
  11. CustomHandler.super.access(self)
  12. kong.log.inspect(config.key_names) -- { "apikey" }
  13. kong.log.inspect(config.hide_credentials) -- false
  14. end
  15. return CustomHandler

Note that the above example uses the kong.log.inspect function of the Plugin Development Kit to print out those values to the Kong logs.


A more complex example, which could be used for an eventual logging plugin:

  1. -- schema.lua
  2. local typedefs = require "kong.db.schema.typedefs"
  3. return {
  4. name = "my-custom-plugin",
  5. fields = {
  6. {
  7. config = {
  8. type = "record",
  9. fields = {
  10. {
  11. environment = {
  12. type = "string",
  13. required = true,
  14. one_of = {
  15. "production",
  16. "development",
  17. },
  18. },
  19. },
  20. {
  21. server = {
  22. type = "record",
  23. fields = {
  24. {
  25. host = typedefs.host {
  26. default = "example.com",
  27. },
  28. },
  29. {
  30. port = {
  31. type = "number",
  32. default = 80,
  33. between = {
  34. 0,
  35. 65534
  36. },
  37. },
  38. },
  39. },
  40. },
  41. },
  42. },
  43. },
  44. },
  45. },
  46. }

Such a configuration will allow a user to post the configuration to your plugin as follows:

  1. $ curl -X POST http://kong:8001/services/<service-name-or-id>/plugins \
  2. -d "name=my-custom-plugin" \
  3. -d "config.environment=development" \
  4. -d "config.server.host=http://localhost"

And the following will be available in handler.lua:

  1. -- handler.lua
  2. local BasePlugin = require "kong.plugins.base_plugin"
  3. local kong = kong
  4. local CustomHandler = BasePlugin:extend()
  5. CustomHandler.VERSION = "1.0.0"
  6. CustomHandler.PRIORITY = 10
  7. function CustomHandler:new()
  8. CustomHandler.super.new(self, "my-custom-plugin")
  9. end
  10. function CustomHandler:access(config)
  11. CustomHandler.super.access(self)
  12. kong.log.inspect(config.environment) -- "development"
  13. kong.log.inspect(config.server.host) -- "http://localhost"
  14. kong.log.inspect(config.server.port) -- 80
  15. end
  16. return CustomHandler

You can also see a real-world example of schema in the Key-Auth plugin source code.


Next: Accessing the Datastore ›