Plugins
View SourcePlugins are a bit like the handlers except they are run on request. There's currently two different type of plugins; pre_request and post_request.
These can be used to create access logs, insert CORS headers or similar.
Plugins are used to handle things before and/or after a request. They are applied on all requests of a specified protocol.
A rule of thumb is that if you want to store a short-lived state (For the local request) then you could store that under the Env-object. If you want to store a long-lived state (For the global request) then you could store that under the State-object which is initialized in the optional init/0 callback and (potentially) updated in each call to pre_request/4 and post_request/4. The long-lived state is only applicable to the plugin-module while the local state belongs to the request and is distributed to each invoked plugin.
This is an example:
-module(correlation_id).
-behaviour(nova_plugin).
-export([
pre_request/4,
post_request/4,
plugin_info/0
]).
pre_request(Req, _Env, _Opts, State) ->
UUID = uuid:uuid_to_string(uuid:get_v4()),
{ok, cowboy_req:set_resp_header(<<"x-correlation-id">>, UUID, Req), State}.
post_request(Req, _Env, _Opts, State) ->
{ok, Req, State}.
plugin_info() ->
#{
title => <<"nova_cors_plugin">>,
version => <<"0.2.0">>,
url => <<"https://github.com/novaframework/nova">>,
authors => [<<"Nova team <info@novaframework.org">>],
description => <<"Add CORS headers to request">>,
options => [
]
}.This plugin injects a UUID into the headers.
Optional callbacks
There's two optional callbacks for a plugin that can be used for storing a global long-lived state. This can be useful if you want to keep track of something like a pid or similar.
The first callback is init/0 and the second is stop/1. init/0 is called when the plugin is loaded and stop/1 is called when the plugin is unloaded (Usually when the nova-application is started/stopped).
Following example shows how we spawn a process and returns the pid in a map - this map will become our new state. When the plugin is stopped, stop/1 will get invoked and we will send a stop message to the pid.
init() ->
%% Setup some pids
Pid = spawn(fun() -> ok end),
#{my_pid => Pid}.
stop(State) ->
%% Stop the pids
Pid = maps:get(my_pid, State),
Pid ! stop,
ok.Adding a plugin
Example:
A good example of a very useful plugin is the nova_request_plugin. When we are developing a HTTP web api using json as the data format, we need the framework to
decode our message so that we can process it. To do that we need to add decode_json_body => true into the options field in our sys.config.
sys.config
{nova, [
{environment, dev},
{cowboy_configuration, #{
port => 8080
}},
{dev_mode, true},
{bootstrap_application, chatapp},
{plugins, [
{pre_request, nova_request_plugin, #{parse_bindings => true,
decode_json_body => true}}
]}
]} We have added our plugin in the plugins section. As we can see this is a pre_request plugin since it processes and decodes the message to json format
before we can actually use it in our nova application endpoints.
Usage:
controller
-module(test_controller).
-export([increment/1]).
increment(#{<<"json">> := #{<<"id">> := Id, <<"value">> := Value}})->
{json,200,#{},#{<<"id">> => Id , <<"received">> => Value, <<"increment">> => Value+1}}.
Nova plugins
Nova has a couple of plugins for some general purposes.
| Plugin | Description | Code |
|---|---|---|
| nova_correlation_plugin | This plugin will add a correlation id to header response but also add #{correlation_id => CorrelationID} to the request obj that is passed to the controller. | nova_correlation_plugin |
| nova_cors_plugin | This plugin will handle cors and add the cors headers into the request. | nova_cors_plugin |
| nova_request_plugin | This plugin will handle incomming data like qs, form urlencoded and json | nova_request_plugin |
Nova correlation
This plugin will generate a uuid v4 and set it as a response header as X-Correlation-ID if nothing is configuered.
{pre_request; nova_correlation_plugin, #{request_correlation_header => CorrelationHeader,
logger_metadata_key => LoggerMetaDataKey}}| Option | Description |
|---|---|
| request_correlation_header | This is if you want a different correlation header than the standard X-Correlation-ID |
| logger_metadata_key | This is if you want to have a different metadata key then the standard correaltion_id |
Nova cors
This plugins will make it so that if we get method OPTIONS it will just return back the CORS headers. In this case you don't need a controller to handle it and the plugin stops after this. For other methods it will add the CORS headers to the request.
{pre_request; nova_cors_plugin, #{allow_origins => <<"*">>}}| Option | Description |
|---|---|
| allow_origins | Specifies which origins to insert into Access-Control-Allow-Origin |
Nova request
This plugins handle incoming data and can transform them to erlang maps depending on what the options are.
{pre_request; nova_correlation_plugin, #{decode_json_body => true,
read_urlencoded_body => true,
parse_qs => true|list}}| Option | Description | Req |
|---|---|---|
| decode_json_body | If header is application/json it will decode the body. | Req#{json => Map} |
| read_urlencoded_body | If header is application/x-www-form-urlencoded it will decode it. | Req#{params => Map} |
| parse_qs | If the path have qs in it we will get them. | Req#{parsed_qs => Map or List} |