Code Generators#

The rebar3_nova plugin ships with several generators that scaffold controllers, resources and test suites. Instead of copy-pasting boilerplate by hand you can run a single command and get a working starting point.

Generate a controller#

The nova gen_controller command creates a controller module with stub action functions.

$ rebar3 nova gen_controller --name products
===> Writing src/controllers/my_first_nova_products_controller.erl

By default it generates five actions: list, show, create, update and delete. You can pick which actions you want with the --actions flag:

$ rebar3 nova gen_controller --name products --actions list,show
===> Writing src/controllers/my_first_nova_products_controller.erl

The generated controller looks like this:

-module(my_first_nova_products_controller).
-export([
         list/1,
         show/1,
         create/1,
         update/1,
         delete/1
        ]).

list(_Req) ->
    {json, #{<<"message">> => <<"TODO">>}}.

show(_Req) ->
    {json, #{<<"message">> => <<"TODO">>}}.

create(_Req) ->
    {status, 201, #{}, #{<<"message">> => <<"TODO">>}}.

update(_Req) ->
    {json, #{<<"message">> => <<"TODO">>}}.

delete(_Req) ->
    {status, 204}.

Every action returns a valid Nova response tuple so you can compile and run immediately. Replace the TODO values with your actual logic.

Generate a full resource#

The nova gen_resource command is the most powerful generator. It creates a controller, a JSON schema and prints route definitions you can paste into your router.

$ rebar3 nova gen_resource --name products
===> Writing src/controllers/my_first_nova_products_controller.erl
===> Writing priv/schemas/product.json

Add these routes to your router:

  {<<"/products">>, {my_first_nova_products_controller, list}, #{methods => [get]}}
  {<<"/products/:id">>, {my_first_nova_products_controller, show}, #{methods => [get]}}
  {<<"/products">>, {my_first_nova_products_controller, create}, #{methods => [post]}}
  {<<"/products/:id">>, {my_first_nova_products_controller, update}, #{methods => [put]}}
  {<<"/products/:id">>, {my_first_nova_products_controller, delete}, #{methods => [delete]}}

The generated JSON schema in priv/schemas/product.json:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string" }
  },
  "required": ["id", "name"]
}

Edit this schema to match your actual data model. It will be picked up by the OpenAPI generator (covered in the next article) to produce API documentation automatically.

Generate a test suite#

The nova gen_test command generates a Common Test suite with test cases for each CRUD action.

$ rebar3 nova gen_test --name products
===> Writing test/my_first_nova_products_controller_SUITE.erl

The generated suite includes:

-module(my_first_nova_products_controller_SUITE).
-include_lib("common_test/include/ct.hrl").

-export([all/0, init_per_suite/1, end_per_suite/1]).
-export([test_list/1, test_show/1, test_create/1, test_update/1, test_delete/1]).

all() ->
    [test_list, test_show, test_create, test_update, test_delete].

init_per_suite(Config) ->
    application:ensure_all_started(my_first_nova),
    Config.

end_per_suite(_Config) ->
    ok.

test_list(_Config) ->
    {ok, {{_, 200, _}, _, _Body}} =
        httpc:request(get, {"http://localhost:8080/products", []}, [], []).

test_show(_Config) ->
    {ok, {{_, 200, _}, _, _Body}} =
        httpc:request(get, {"http://localhost:8080/products/1", []}, [], []).

test_create(_Config) ->
    {ok, {{_, 201, _}, _, _Body}} =
        httpc:request(post, {"http://localhost:8080/products", [],
                             "application/json", "{}"}, [], []).

test_update(_Config) ->
    {ok, {{_, 200, _}, _, _Body}} =
        httpc:request(put, {"http://localhost:8080/products/1", [],
                            "application/json", "{}"}, [], []).

test_delete(_Config) ->
    {ok, {{_, 204, _}, _, _Body}} =
        httpc:request(delete, {"http://localhost:8080/products/1", []}, [], []).

This gives you a quick smoke-test harness. Update the request bodies and assertions as you flesh out the controller logic.

Putting it all together#

A typical workflow for adding a new resource to your API:

# 1. Generate the resource (controller + schema + route hints)
$ rebar3 nova gen_resource --name products

# 2. Copy the printed routes into your router

# 3. Edit the JSON schema to match your data model

# 4. Generate a test suite
$ rebar3 nova gen_test --name products

# 5. Implement the controller logic

# 6. Run the tests
$ rebar3 ct

This saves you from writing boilerplate and gives you a consistent project structure across resources.

In the next article we will look at how the OpenAPI generator uses these schemas to produce full API documentation with a Swagger UI.