Resources

Well-defined artifacts that are accessible

What is a Resource?

In simple terms, a resource is an artifact managed by the application that has a unique id. The resource is expected to support the following actions:

  • create

  • retrieve

  • update

  • delete

These actions are also know as CRUD operations.

In Blueprint, we consider resources to be first-class entities because implementing resource-like behavior is quite common. For example, the message example we used to discuss both routers and controllers implemented the create and retrieve actions. We were just missing the update and delete action. One thing you will notice over time is that you will implement behavior similar to the message example each time you need to interact with a entity managed application. Blueprint therefore wants to minimize the amount of code you must reinvent—thereby improving productivity and reduce potential errors.

Defining a Resource

You define a resource by first binding a resource to path in the router. Then, you implement the corresponding resource controller.

Declaring a Resource Path

You declare a resource path using the resource keyword. As shown in the example below, we are declaring the /messages path as a resource path. As part of the declaration, we specify what controller the resource path should use to create its complete routes.

app/routers/message.js
const { Router } = require ('@onehilltech/blueprint');

module.exports = Router.extend ({
  specification: {
    '/messages': {
      resource: { controller: 'message' }
    }
  }
});

When you declare a resource path, it will automatically define the following routes relative to the resource path.

By default, we do not automatically define a route for deleting all the resources.

Allowing and Denying Routes

You may want to restrict what routes are available to a resource. For example, you prohibit directly creating a resource because it is created someone where else in the application. Likewise, you may prohibit a resource from being able to be deleted.

When you define a resource path, you can use the allow or deny keyword to permit or prohibit actions on the resource, respectively. If you use the allow keyword, then all resource actions are prohibited except the ones listed. Likewise, if you use the deny keyword, then all resource actions are permitted except for the ones listed.

Here is an example that restrict what actions are permitted.

const { Router } = require ('@onehilltech/blueprint');

module.exports = Router.extend ({
  specification: {
    '/messages': {
      resource: { 
        controller: 'message',
        allow: ['getOne', 'getAll']
      }
    }
  }
});

Likewise, here is another example illustrating what actions are not permitted.

const { Router } = require ('@onehilltech/blueprint');

module.exports = Router.extend ({
  specification: {
    '/messages': {
      resource: { 
        controller: 'message',
        deny: ['create', 'delete', 'update']
      }
    }
  }
});

Implementing the Resource Controller

You implementing the resource controller the same way you implement any other controller. But, instead of extending the Controller class, you extend the ResourceController class. The ResourceController class has a methods that correspond to each action listed in the table above. You just have to provide the corresponding implementation for each supported action.

More Information

The ResourceController class has empty methods for each action by design. This is because the implementation of a resource controller depends heavily on where the data is persisted. For example, a in-memory resource controller will store its resources in memory. Whereas, a MongoDB resource controller will persists is resources to a MongoDB database. We therefore recommend that you leverage a domain-specific resource controller since it will provide a default implementation for each action while giving you the ability to customize it accordingly.

Last updated