# Controllers

## What is a Controller?

The controller defines the backend logic for the application. It also contains actions responsible for handling requests from the client.&#x20;

All controllers are located in `app/controllers`.

## Defining a Controller

You defined a controller by exporting a class that extends the `Controller` class, or any existing derivation of the `Controller` class. Here we have defined a controller that will act as the corresponding controller from the [message router](/developer-guide/routers-and-controllers/routers.md) from our previous examples.

```javascript
const { Controller } = require ('@onehilltech/blueprint');

module.exports = Controller.extend ({
    // TODO Add properties and actions here
});
```

Right now, the controller is empty. But, just like we learned with [the object model](/developer-guide/the-object-model.md), we just need to provide a hash to the `extend()` method to define the actions what actions the controller supports.

## Actions

Actions implement the business-logic of the controller. The action is responsible for validating and sanitizing the input, executing the request, and sending a response.&#x20;

### Creating an Action

Actions on a controller are methods that return an extended version of the `Action` class. The name of the method represents the name of the action. This is the name that the router binds with when defining its [reactions](/developer-guide/routers-and-controllers/routers.md#reactions-to-paths) for a given path.

In our [examples from learning about routers](/developer-guide/routers-and-controllers/routers.md), we implemented a router named message. We assumed the purpose of this router was to provide an interface for managing messages. In our example, we defined a couple of paths, and reactions using actions on a `message` controller. Let's complete this example by implementing the `message` controller.&#x20;

#### Action Definition

First, we are going to start by defining the action on the `message` controller for creating the message. This action is responsible for handling `POST /messages` requests as defined in the message router.

{% tabs %}
{% tab title="app/controllers/message.js" %}

```javascript
const { Controller, Action } = require ('@onehilltech/blueprint');

module.exports = Controller.extend ({
  create () {
    return Action.extend ({
      execute (req, res) {
        // TODO Add code here
      }
    })
  }
});
```

{% endtab %}

{% tab title="app/routers/message.js" %}

```javascript
const { Router } = require ('@onehilltech/blueprint');
​
module.exports = Router.extend ({ 
  specification: {    
    '/messages': {      
      post: { action: 'message@create' }    
    }  
  }
});
```

{% endtab %}
{% endtabs %}

All actions must implement the `execute(req, res)` method, which is responsible for handling the request. The `execute(req, res)` method must return `null`, `undefined`, or a `Promise`. The `req` parameter is an [HTTP request object](http://expressjs.com/en/4x/api.html#req), and the `res` parameter is a [HTTP response object](http://expressjs.com/en/4x/api.html#res).

Since the extend behavior of this action is the create a message, we know the properties of the message to create will appear in the body of the request. Let's update our controller store the created messages.

{% hint style="info" %}
Ideally, we would store the messages in a database. But, for illustrations purposes only we are going to store the messages locally in the controller.
{% endhint %}

{% code title="app/controllers/message.js" %}

```javascript
const { Controller, Action, BO } = require ('@onehilltech/blueprint');
const { pick } = require ('lodash');

const Message = BO.extend ({
  id: null              // message id
  from: null,           // who the message is from
  to: null,             // who the message is to
  date: null,           // date of the message
  subject: null,        // message subject
  content: null,        // content of the message
  
  init () {
    this._super.call (this, ...arguments);
    
    if (!this.date) this.date = new Date ();
  }
});

module.exports = Controller.extend ({
  messages: null,          //collection of messsages
 
  init () {
    this._super.call (this, ...arguments);
    this.messages = [];
  },
  
  create () {
    return Action.extend ({
      _nextId: 0,      // id of the next message
      
      execute (req, res) {
        let id = this._nextId ++;
        let data = Object.assign ({id}, pick (req.body.message, ['from','to','date','subject','content']));
        let msg = new Message (data);
        
        this.controller.messages.push (msg);
        
        res.status (200).json ({message: msg});
      }
    })
  }
});
```

{% endcode %}

There is a lot going on in the example above, so let's unpack it. First, we create a class named `Message` which is a Wrapper Facade for each message we create. We then add a `messages` property to our controller. This will be used to store the messages we create. If you remember the discussion about [object-like properties in the object model](/developer-guide/the-object-model/classes-and-instances.md#using-the-init-method), then you will remember that we cannot initialize an array property when we define it. Instead, we must initialize the property in the `init()` method. In this case, we initialize the `messages` property to an empty array.

Lastly, we updated the `create()` action to create the message, which is located in `req.body`. We are expecting the data for the message to be under the `message` envelope. To create the message, we first compute the id of the next message using `_nextId`. We then create a data object, and use this data object to create a `Message` object. Last, we push the message object unto the collection of messages, and return a response to the client.

### Validating and Sanitizing Input

In our example above, we are expecting the body to contain the data for the message to create. One important step we failed to do is validate the input. Fortunately, validating the a request's input is a simple process.&#x20;

With Blueprint actions, you can validate input either statically using a schema or dynamically using a validate method.&#x20;

{% hint style="info" %}
Use schema validation when you can define how to validate the input when defining the action. Use dynamic validation when the input itself determine how to validate other parts of the input.
{% endhint %}

We have update the example to use schema validation, and an empty method for dynamic validation. Since we are not really using dynamic validation, we can actually remove the `validate(req)` method.

{% code title="app/controllers/message.js" %}

```javascript
// ...

module.exports = Controller.extend ({
  messages: null,          //collection of messsages
 
  init () {
    this._super.call (this, ...arguments);
    this.messages = [];
  },
  
  create () {
    return Action.extend ({
      // ...
      
      schema: {
        'message.to': { in: 'body', isEmail: true },
        'message.from': { in: 'body', isEmail: true },
        'message.date': { in: 'body', isDate: true, optional: true },
        'message.content': { in: 'body', isLength: { min: 1 } }
      },
      
      validate (req) { },
      
      // ...
    })
  }
});
```

{% endcode %}

Blueprint supports [express-validator](https://github.com/express-validator/express-validator) out-of-the-box. For schema validation, Blueprint uses the [schema validation](https://github.com/express-validator/express-validator#schema-validation) feature in [express-validator](https://github.com/express-validator/express-validator). As shown in the example, each input we need to validate is added the the `schema` property. Now that we have enabled request input validation, the `execute(req, res)` method will only be called if validation succeeds. This means there is no need to add validation logic to the `execute(req, res)` method.

#### Custom Validators and Sanitizers

(Coming Soon)

### Actions for Dynamic Routes

In our router example, we defined a [dynamic route](/developer-guide/routers-and-controllers/routers.md#dynamic-routes) for getting a single message (*i.e.*, `GET /messages/:messageId`). This path for this route had a router parameter named `messageId`. The parameter in the path is available on the `req.params` object. To illustrate how we can use this parameter in our action, below is the implementation of the `getOne()` action on the `message` controller.

{% code title="app/controllers/message.js" %}

```javascript
// ...
module.exports = Controller.extend ({
  // ...
  
  getOne () {
    return Action.extend ({
      schema: {
        messageId: { in: 'params', isInt: {min: 0}, toInt: true}
      },
      
      execute (req, res) {
        const {messageId} = req.params;
        const found = this.controllers.messages.find (message => message.id === messageId);
        
        if (found)
          return res.status (200).json ({message: found});
        else
          return res.sendStatus (404);  
      }
    });
  }
});
```

{% endcode %}

As shown in the `getOne()` method above, the action for this method defines a schema to validate the expected parameter. The action then uses the `messageId` parameter to search for the message that has an id that matches. If the message is found, the message is returned as the response. Otherwise, the action returns a 404 response.

### The Default Action

A default action is the action implied on the controller. The default action can exist if there are other actions on the controller. The [default action is used by the router](/developer-guide/routers-and-controllers/routers.md#binding-to-default-actions) if an action is defined by its controller and no action.

You define the default action of the controller implementing an \_\_invoke() method on the corresponding controller. For example, the code below gives a default action for the message controller.

{% code title="app/controllers/message.js" %}

```javascript
const { Controller } = require ('@onehilltech/blueprint');

module.exports = Controller.extend ({
  __invoke () {
    return Action.extend ({
      execute (req, res) {
        // TODO Add implementation here
      }  
    })
  }
});
```

{% endcode %}

Now, when we just use the `message` controller with no action in the router, it will use the `__invoke()` definition.

### Predefined Actions

The return value of a controller action is an `Action` class. Because the return value is a class and not an object, we can port an action classes to different controller actions. We can also extend an action class to create a more domain-specific action.

There exists several scenarios where we can provide a predefined action class that implements the boilerplate code, and defers context-specific behavior to the extended class.

{% hint style="info" %}
Actions use the [Template Method pattern](https://en.wikipedia.org/wiki/Template_method_pattern) to define the skeleton of an algorithm, and defer the implementation of the steps to subclasses.
{% endhint %}

The Blueprint framework provide the following actions out-of-the-box:

#### Views

This is a collection of actions for generating [view](/developer-guide/application-resources/static-views.md) responses.

* [**`ViewAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/view-action.js) Generates a view based on the content of the request it is processing.
* [**`SingleViewAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/single-view-action.js) Specialization of the ViewAction class that only supports a single view. Subclasses and instances of this class must define the `template` property.�

#### Uploads

This is a collection of actions for handling uploads. The upload actions are a Wrapper Facade for the [multer](https://github.com/expressjs/multer) node module. All subclasses of any action class below must implement the `onUploadComplete(req, res)` method, which is notified when the upload is complete.

* [**`ArrayUploadAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/array-upload-action.js) An action for uploading an array of files. The uploaded files will be accessible on `req.files`.
* [**`FieldsUploadAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/fields-upload-action.js) An action for accepting a mix of files.
* [**`SingleFileUploadAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/single-file-upload-action.js) An action for uploading a single file. The file is expected to be part of an multipart/form-data request.
* [**`TextOnlyUploadAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/text-only-upload-action.js)  An action for uploading text only.
* [**`UploadAction`**](https://github.com/onehilltech/blueprint/blob/master/lib/upload-action.js) Base class for all upload actions. This class will initialize a new instance of [multer](https://github.com/expressjs/multer) and store it internally for subclasses to use.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://blueprint.onehilltech.com/developer-guide/routers-and-controllers/controllers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
