avon-js
v1.0.0
Published
A fluent Node.js API generator.
Downloads
5
Maintainers
Readme
Installation
Resources
Repositories
Filters
Orderings
Actions
Error Handling
Installation
Requirements
Avon has a few requirements you should be aware of before installing:
- Node.js (Version 18)
- Expressjs Framework (Version 4.X)
Installation
via npm:
npm install avon-js
via yarn:
yarn install avon-js
Initialize
At first point you have to register the router:
// index.js
import { Avon } from 'avon-js';
import express from 'express';
const app = express();
// register Avon router
app.use('/api', Avon.routes(express.Router()));
app.listen(3000, () => {
console.log('running')
})
Resources
Introduction
Avon is a beautiful API generator for Node.js applications written in typescript. Of course, the primary feature of Avon is the ability to administer your underlying repository records. Avon accomplishes this by allowing you to define an Avon resource
corresponding to each repository
in your application.
Defining Resources
To create a Resource you have to create a file and put the following in that:
// Resources/Post.js
import { Resource } from 'avon-js';
export default class Post extends Resource {
/**
* Get the repository.
*/
repository() {
throw new Error('Repository not prepared for resource `post`')
}
}
The most basic and fundamental method of a resource is its repository
. This method tells Avon which repository the resource corresponds to. So, let's define a repository for our resources.
// Repositories/Posts.js
import { CollectionRepository } from 'avon-js';
export default class Posts extends CollectionRepository {
/**
* Get searchable attributes.
*/
searchables() {
return [];
}
/**
* Get path of the stored files.
*/
filepath() {
return process.cwd() + '/posts.json';
}
}
Don't worry, you'll learn more about repositories later. now the Post
resource has to change like the following:
import Posts from '../Repositories/Posts.js';
import { Resource } from 'avon-js';
export default class Post extends Resource {
/**
* Get the repository.
*/
repository() {
return new Posts()
}
}
Freshly created Avon resources only contain an ID
field definition. Don't worry, we'll add more fields to our resource soon.
Registering Resources
Before resources are available within your API, they must first be registered with Avon. You may use the resources
method to manually register individual resources:
// resources method
Avon.resources([
New Post(),
])
If you do not want a some resource api to appear in the swagger-ui, you may override the following property of your resource class:
availableForIndex
availableForDetail
availableForCreation
availableForUpdate
availableForDelete
availableForForceDelete
availableForRestore
Configuring Swagger UI
Avon creates a schema based on the OpenApi that enables you to use the swagger-ui for documentation. here we using docker to install swagger-ui
. let's do it:
first run the following command to install swagger-ui:
docker pull swaggerapi/swagger-ui
now we use the previously created URL for schema to run docker:
docker run -p 80:8080 -e SWAGGER_JSON_URL=http://localhost:3000/api/schema swaggerapi/swagger-ui
now you can go to the http://localhost
and see the result.
Attentions
- You have to run the server to see the the documentation in the swagger. maybe you need something like this in the root of your project
npm run start
- If you see
CORS
error when swagger ui loaded the this tutorial can solve your problem.
Resource Hooks
Avon also allows you to define the following methods on a resource to serve as hooks that are only invoked when the corresponding resource action is executed from within Avon:
afterCreate
beforeCreate
afterUpdate
beforeUpdate
afterDelete
beforeDelete
afterForceDelete
beforeForceDelete
Pagination
If you would like to customize the selectable maximum result amounts shown on each resource's "per page" filter menu, you can do so by overriding the perPageOptions
method:
/**
* Get the pagination per-page values
*/
public perPageOptions(): number[] {
return [15, 25, 50];
}
Defining Fields
Each Avon resource contains a fields method. This method returns an array of fields, which generally extend the Fields\Field
class. Avon ships with a variety of fields out of the box, including fields for text inputs, booleans, etc.
To add a field to a resource, you may simply add it to the resource's fields method. Typically, fields have to add as new class with accepts several arguments; however, you usually only need to pass the "attribute" name of the field that normally determine the underlying repository storage column:
// Resources/Post.js
import { ID, Text } from 'avon-js';
/**
* Get the fields available on the entity.
*/
public fields(request: AvonRequest): Field[] {
return [
new ID().filterable().orderable(),
new Text('name').filterable().orderable(),
];
}
Showing / Hiding Fields
Often, you will only want to display a field in certain situations. For example, there is typically no need to show a Password
field on a resource index listing. Likewise, you may wish to only display a Created At
field on the creation / update forms. Avon makes it a breeze to hide / show fields on certain pages.
The following methods may be used to show / hide fields based on the display context:
showOnIndex
showOnDetail
showOnCreating
showOnUpdating
hideFromIndex
hideFromDetail
hideWhenCreating
hideWhenUpdating
onlyOnIndex
onlyOnDetail
onlyOnForms
exceptOnForms
You may chain any of these methods onto your field's definition in order to instruct Avon where the field should be displayed:
new ID().exceptOnForms()
Alternatively, you may pass a callback to that methods as following;
For show*
methods, the field will be displayed if the given callback returns true
:
new Text('name').exceptOnForms((request, resource) => {
return resource?.name === 'something';
}),
For hide*
methods, the field will be hidden if the given callback returns true
:
new Text('name').hideFromIndex((request, resource) => {
return resource?.name === 'something';
}),
Dynamic Field Methods
If your application requires it, you may specify a separate list of fields for specific display contexts. The available methods that may be defined for individual display contexts are:
fieldsForIndex
fieldsForDetail
fieldsForCreate
fieldsForUpdate
fieldsForAssociation
Dynamic Field Methods Precedence
The fieldsForIndex
, fieldsForDetail
, fieldsForCreate
, fieldsForUpdate
and fieldsForAssociation
methods always take precedence over the fields
method.
Default Values
There are times you may wish to provide a default value to your fields. Avon offers this functionality via the default
method, which accepts callback. The result value of the callback will be used as the field's default input value on the resource's creation
API:
new Text('name').default((request) => 'create something')
Field Hydration
On every create or update request that Avon receives for a given resource, each field's corresponding model attribute will automatically be filled before the model is persisted to the database. If necessary, you may customize the hydration behavior of a given field using the fillUsing
method:
new Text('name').fillUsing((request, model, attribute, requestAttribute) => {
model.setAttribute(
attribute,
request.string(attribute) + ' - ' + Date.now()
);
}),
Orderable Fields
When attaching a field to a resource, you may use the orderable
method to indicate that the resource index may be sorted by the given field:
new Text('name').orderable()
Also its possible to passing a callback to customize the ordering behavior:
new Text('name').orderable((request, repository, direction) => {
repository.order({
key: 'another key',
direction: 'desc',
});
})
Filterable Fields
The filterable
method allows you to enable convenient, automatic filtering functionality for a given field on the resource's index:
new Text('name').filterable()
Also its possible to passing a callback to customize the filtering behavior:
new Text('name').filterable((request, repository, value) => {
repository.where({
key: this.filterableAttribute(request),
operator: Operator.like,
value,
});
})
Field Types
Array Field
The Array
field pairs nicely with model attributes that are cast to array
or equivalent:
import { Array } from 'avon-js';
new Array('tags')
Boolean Field
The Boolean
field may be used to represent a boolean / "tiny integer" column in your database. For example, assuming your database has a boolean column named active
, you may attach a Boolean
field to your resource like so:
import { Boolean } from 'avon-js';
new Boolean('active').rules(Joi.required()).nullable(false)
DateTime
The DateTime
field may be used to store a datetime
value.
import { DateTime } from 'avon-js';
new DateTime('publish_at'),
The format
method allows you to customize the date format that accepts any valid moment formatting.
Email Field
The Email
field may be used to store a email
value.
import { Email } from 'avon-js';
new Email('mail'),
ID Field
The ID
field represents the primary key of your resource's repository model. Typically, each Avon resource you define should contain an ID
field. By default, the ID
field assumes the underlying storage column is named id
; however, you may pass the column name when creating an ID
field:
import { ID } from 'avon-js';
new ID()
Json Field
The Json
field provides a convenient interface to edit, key-value
data stored inside JSON
column types. For example, you might store some information inside a JSON
column type (opens new window) named meta
:
import { ID, Json, Text } from 'avon-js';
new Json('meta', [
new Text('title').creationRules(Joi.required()),
])
Integer Field
The Integer
field store / retrieve value as integer
in the model:
import { Integer } from 'avon-js';
new Integer('hits')
Text Field
The Text
field store / retrieve value as string
in the model:
import { Text } from 'avon-js';
new Text('name')
Customization
Nullable Fields
By default, Avon attempts to store all fields with a value, however, there are times where you may prefer that Avon store a null
value in the corresponding sorage column when the field is empty. To accomplish this, you may invoke the nullable
method on your field definition:
new DateTime('publish_at').nullable()
You may also set which values should be interpreted as a null
value using the nullValues
method, which accepts an function as validator:
new DateTime('publish_at').nullable().nullValues((value) => ['', undefined, null].includes(value));
Filterable Fields
The filterable
method allows you to enable convenient, automatic filtering functionality for a given field on resources.
new Boolean('active').filterable()
The filterable
method also accepts a callback as an argument. This callback will receive the filter query, which you may then customize in order to filter the resource results to your liking:
new Boolean('active').filterable((request, repository, value) => {
repository.where({
key: 'active',
operator: Operator.eq,
value: Boolean(value) ? 1 : 0
})
})
Relationships
In addition to the variety of fields we've already discussed, Avon has support for some relationships. Avon relation fields allows you to handle relationships between resources.
BelongsTo
The BelongsTo
field corresponds to a belongs-to
relationship. For example, let's assume a Post
resource belongs to a User
resource. We may add the relationship to our Post
Avon resource like so:
import { BelongsTo } from 'avon-js';
new BelongsTo('users')
As you see, BelongsTo
accepts the uriKey
of the target resource as first argument. By default BelongsTo
field guess the relationship
name from the target resurce, but you can pass the second argument when creating a field to change that.
In the example above, Avon will will give user
value from request and store primary key of the User
resource in the user_id
attribute of the Post
resource. to change that you can follow the below example:
new BelongsTo('users', 'author')
now, Avon retrieve author
from requestn and store it inthe author_id
of post attributes.
Avon determines the default foreign key name by examining the name of the relationship
and suffixing the name with a _
followed by the name of the parent resource model's primary key column. So, in this example, Avon will assume the User
model's foreign key on the posts
repository is author_id
.
However, if the foreign key for your relationship does not follow these conventions, withForeignKey
method allows you change the foreign key of the relation:
new BelongsTo('users', 'author').withForeignKey('user_id')
Also, Avon use the id
column of the parent model to store as foreign key. If your parent model does not use id
as its primary key, or you wish to find the associated model using a different column you can use the withOwnerKey
method to specifying your parent table's custom key:
new BelongsTo('users', 'author').withOwnerKey('userId')
Now, Avon try to find the realted user
by userId
.
Nullable Relationships
If you would like your BelongsTo
relationship to be nullable
, you may simply chain the nullable method onto the field's definition:
new BelongsTo('users', 'author').nullable()
Load Realted Resource
The BelongsTo
field only display the realted resource foreign key on the detail
and index
API but some times you need to load the realted resource instead of foreign key. for exmaple you want to see the User
record on the Post
api. for this situation you can use the load
method on the BelongsTo
field:
new BelongsTo('users').load()
HasMany
The HasMany
field corresponds to a one-to-many
realtionship. A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models. For example, a use may have a many posts in the blog. We may add the relationship to our User
Avon resource like so:
import { HasMany } from 'avon-js';
new HasMany('posts')
Like another realtionships, HasMany
accepts the uriKey
of the target resource as first argument Also, guess the relationship
name from the target resurce, but you can pass the second argument when creating a field to change that.
import { HasMany } from 'avon-js';
new HasMany('posts', 'latestPosts')
Avon determines the default foreign key name by examining the name of the resource and suffixing the name with a _
followed by the name of the resource model's primary key column. So, in this example, Avon will assume the User
model's foreign key on the posts
repository is user_id
but, the withForeignKey
method allows you to change this behavior. so let assume the User
id column stored as author_id
on the posts record, so exmaple will be change like following:
new HasMany('posts', 'latestPosts').withForeignKey('author_id')
Also if you are using the another key instead of id
of the resource, you can change the HasMany
field like below:
new HasMany('posts', 'latestPosts').withOwnerKey('userId')
HasOne
The HasOne
field corresponds to a one-to-one
relationship. For example, let's assume a User
Avon resource hasOne Profile
Avon resource. this field is like the HasMany
field, and the only thing that has changed is the result of loaded resourcse that limited only into one. so the following example will load only the one realted resource detail:
new HasOne('posts', 'latestPosts').withForeignKey('author_id')
BelongsToMany
The BelongsToMany
field corresponds to a many-to-many
relationship. For example, let's assume a Post
Avon resource has many Tag
Avon resource and in reverse Tag
Avon resource has many Post
Avon resource. to show the realted Tag
records on the Post
resource index
/ detail
api, we need two another more resource to hold the pivot
table. so we have to create PostTag
Avon resource to store joining records. we may add the relationship on the Post
resource like so:
import { BelongsToMany } from 'avon-js';
new BelongsToMany('tags', 'post-tags')
The BelongsToMany
field stores the primary key of the resource and realted resource into the pivot resource into attributes by examining the name of the them and suffixing the name with a _
followed by the name of the model's primary key column. the setResourceForeignKey
method allows you to cahnge attribute name for the resource
and withForeignKey
change the realted-resource
attribute foreign key name:
new BelongsToMany('tags', 'post-tags').setResourceForeignKey('postKey').withForeignKey('tagKey')
Also if you are using another key to reffer the resource or the realted resource, setResourceOwnerKey
and withOwnerKey
allows to change this attributes like so:
new BelongsToMany('tags', 'post-tags').setResourceForeignKey('postKey').withForeignKey('tagKey').setResourceOwnerKey('name').withOwnerKey('name')
Pivot Fields
If your belongsToMany
relationship interacts with additional "pivot" fields that are stored on the intermediate table of the many-to-many relationship, you may also attach those to your BelongsToMany
Avon relationship.
For example, let's assume our Post
model belongsToMany
Tag
resource. On our post-tag
intermediate storage, let's imagine we have a order
attribute that contains ordering of relationship. We can attach this pivot
attribute to the BelongsToMany
field using the pivots
method:
new BelongsToMany('tags', 'post-tags').pivots((request) => {
return [
Integer('order'),
];
})
Load Realted Resource
The BelongsToMany
field does not display the realted resource on the detail
and index
API but the load
method allows you to meet the attached resurce like so:
new BelongsToMany('tags').load()
Customization
Relatable Resource Formatting
By default, when you load the relationship filds on the resource API, Avon use the index feilds to format the realted resource. If you would like to customize the related resouce attributes on the parnet
or child
API, the fields
method on the relationship fields allows you to pass some feilds to change the display attributes like so:
new BelongsTo('users').load().fields((request) => {
return [
new Text('name'),
new ID(),
]
})
Also on the BelongsToMany
realtionship you can access pivot
values:
new BelongsToMany('tags').load().fields((request) => {
return [
new Text('name'),
new Integer('order', (value, resource) => {
return resource.getAttribute('pivot').getAttribute('order')
})
]
})
Relatable Query Filtering
For now, the BelongsToMany
and BelongsTo
realtionship field's, allows you to modify their results on the create / update API. for common use case when you want to display the select fields on the UI, you need an API to get realted resource for this fields. Fortunately, Avon create an extra API for this types of realtionships that enables you to have an specific customizeable API for each field. For exmaple, if you have a BelongsTo
field on the Post
resource to show the author of the post, you will see an API like /api/resources/posts/associatable/user
on the swagger-ui. If you would like to customize the association query, you may do so by invoking the relatableQueryUsing
method:
new BelongsTo('users').relatableQueryUsing((request, repository) => {
return repository.where({
key: 'role',
operator: Operator.like,
value : 'admin'
})
})
Limiting Relation Results
You can limit the number of results that are returned when searching the field by defining a relatableSearchResults
property on the class of the resource that you are searching for:
/**
* The number of results to display when searching relatable resource.
*/
relatableSearchResults = 5;
Validation
Unless you like to live dangerously, any Avon fields that are displayed on the Avon creation / update pages will need some validation. Thankfully, it's a cinch to attach all of the Joi validation rules you're familiar with to your Avon resource fields. Let's get started.
Attaching Rules
When defining a field on a resource, you may use the rules
method to attach validation rules to the field:
new Text('name').rules(Joi.string())
Creation Rules
If you would like to define rules that only apply when a resource is being created, you may use the creationRules
method:
new Text('name').rules(Joi.string()).creationRules(Joi.required())
Update Rules
Likewise, if you would like to define rules that only apply when a resource is being updated, you may use the updateRules
method:
new Text('name').rules(Joi.string()).creationRules(Joi.required()).updateRules(Joi.optional())
Authorization
When Avon is accessed only by you or your development team, you may not need additional authorization before Avon handles incoming requests. However, if you provide access to Avon to your clients or a large team of developers, you may wish to authorize certain requests. For example, perhaps only administrators may delete records. Thankfully, Avon takes a simple approach to authorization.
API
To limit which users may view
, create
, update
, delete
, forceDelete
, restore
and add
resources, you can override the authotization methods:
authorizedToviewAny
authorizedToView
authorizedToCreate
authorizedToUpdate
authorizedToDelete
authorizedToForcDelete
authorizedToRestore
authorizedToAdd
Disabling Authorization
If you want to disable authorization for specific resource (thus allowing all actions), change authorizable
method to return false
:
/**
* Determine if need to perform authorization.
*/
public authorizable(): boolean {
return false;
}
Fields
Sometimes you may want to prevent updating certain fields by a group of users. You may easily accomplish this by chaining the canSee
method onto your field definition. The canSee
method accepts a function which should return true
or false
. The function will receive the incoming HTTP request:
new Boolean('active').canSee((request) => false)
Repositories
Defining Repositories
To create a repository you have to create a file that conains class extened Avon Repository
and put the following in that:
// Repositories/Collection.js
import { Repository } from "avon-js";
export class MyRepository extends Repository {
/**
* Run transaction on the storage.
* @param callback { () => Promise<T> }
* @returns {Promise<T>}
*/
async transaction (callback) {
throw new Error('Method not implemented');
}
/**
* Search storage for given query string.
* @param search { string }
* @param page { number }
* @param perPage { number }
* @return {Promise<SearchCollection>}
*/
async search(search, page, perPage) {
throw new Error('Method not implemented');
}
/**
* Find all model's for the given conditions.
* @param key { Where[] }
* @return {Promise<TModel[]>}
*/
async all(wheres) {
throw new Error('Method not implemented');
}
/**
* Find first model for the given conditions.
* @param key { Where[] }
* @return {Promise<TModel | undefined>}
*/
async first(wheres) {
throw new Error('Method not implemented');
}
/**
* Store given model into the storage.
* @param key { TModel }
* @return {Promise<TModel>}
*/
async store(model) {
throw new Error('Method not implemented');
}
/**
* Update the given model in storage.
* @param key { TModel }
* @return {Promise<TModel>}
*/
async update(model) {
throw new Error('Method not implemented');
}
/**
* Delete model for the given key.
* @param key { string | number }
* @return {Promise<void>}
*/
async delete(key) {
throw new Error('Method not implemented');
}
/**
* Delete model for the given key.
* @param key { string | number }
* @return {Promise<void>}
*/
async forceDelete(key) {
throw new Error('Method not implemented');
}
/**
* Create new instance of model.
* @return {TModel}
*/
async model() {
throw new Error('Method not implemented');
}
}
Each repository have to return data as a model that should extend the base Model
like so:
Filters
Defining Filters
Avon filters are simple classes that allow you to scope your Avon index queries with custom conditions.
Before creating your own filters, you may want to check out filterable fields. Filterable fields can solve the filtering needs of most Avon installations without the need to write custom code.
To create a filter you have to create a javascript class that extends the Avon Filter
class like so:
// Filters/ActivePosts.js
import { Filter } from 'avon-js';
export class ActivePosts extends Filter {
/**
* Apply the filter into the given repository.
*/
apply(request, repository,value) {
// modify query
}
}
Each filter is a class that extended the base class filters and contains an apply
method to customize the index query.
Select Filter
The most common type of Avon filter is the "select" filter, which allows the user to select a filter option from a drop-down selection menu on the swagger-ui. to have a select filter your calss should extend the SelectFilter
:
// Filters/FilterByRoles.js
import { SelectFilter } from 'avon-js';
export class FilterByRoles extends SelectFilter {
/**
* Apply the filter into the given repository.
*/
apply(request, repository, value) {
// modify query
}
/**
* Get the possible filtering values.
*/
public options(): any[] {
return ['admin', 'user'];
}
}
Each SelectFilter
should have the options
method that defines the "values" the filter may have.
Boolean Filter
The Avon "boolean" filters, allow the user to determine a filter should apply on the resource or not. to create a boolean filter you have to create class that extended BooleanFilter
:
// Filters/ActivePosts.js
import { BooleanFilter } from 'avon-js';
export class ActivePosts extends BooleanFilter {
/**
* Apply the filter into the given repository.
*/
apply(request, repository, value) {
// modify query
}
}
Range Filter
The "range" filters allow the user to chose records that has a value between a specific range. to create a range filter you have to create class that extended RangeFilter
:
// Filters/FilterByHits.js
import { SelectFilter } from 'avon-js';
export class FilterByHits extends SelectFilter {
/**
* Apply the filter into the given repository.
*/
apply(request, repository, value) {
// modify query
}
}
Registering Filters
Once you have defined a filter, you are ready to attach it to a resource. Each resource created by Avon contains a filters
method. To attach a filter to a resource, you should simply add it to the array of filters returned by this method:
/**
* Get the filters available on the entity.
*/
public filters(request: AvonRequest): Filter[] {
return [
new ActivePosts(),
];
}
After attaching a filter to the resource, the filter will appear in the swagger-ui index API.
Authorization Filters
If fyou need to limit the user to run filters, the canSee
method gives a function
that recieve the current request that should return true
or false
to dewtermine user can use the filter or not. if an restricted filter apper in the request, the Avon will ignore it:
new FilterByHits().canSee((request) => false)
Orderings
Defining Orderings
Avon orderings are simple classes that allow you to order your Avon index queries with custom conditions.
- Before creating your own orderings, you may want to check out orderingable fields. Orderingable fields can solve the orderinging needs of most Avon installations without the need to write custom code.
To create a ordering you have to create a javascript class that extends the Avon Ordering
class like so:
// Orderings/OrderByFullName.js
import { Ordering } from 'avon-js';
export class OrderByFullName extends Ordering {
/**
* Apply the ordering into the given repository.
*/
apply(request, repository, direction) {
// modify query
}
}
Each ordering is a class that extended the base class orderings and contains an apply
method to customize the index query.
Registering Orderings
Once you have defined a ordering, you are ready to attach it to a resource. Each resource created by Avon contains a orderings
method. To attach a ordering to a resource, you should simply add it to the array of orderings returned by this method:
/**
* Get the orderings available on the entity.
*/
public orderings(request: AvonRequest): Ordering[] {
return [
new OrderByFullName(),
];
}
After attaching a ordering to the resource, the ordering will appear in the swagger-ui index API.
Authorization Orderings
If fyou need to limit the user to run orderings, the canSee
method gives a function
that recieve the current request that should return true
or false
to dewtermine user can use the ordering or not. if an restricted ordering apper in the request, the Avon will ignore it:
new OrderingByHits().canSee((request) => false)
Actions
Defining Actions
Avon actions allow you to perform custom tasks on one or more resource records. For example, you might write an action that sends an email to a user containing account data they have requested. Or, you might write an action to transfer a group of records to another user.
Once an action has been attached to a resource definition, you can see extra API on the swagger-ui. to create an action you have to create a class that extended by the Avon Action
:
import { Action } from 'avon-js';
export class Publish extends Action {
/**
* Perform the action on the given models.
*/
protected async handle(fields: Fluent, models: Model[]): Promise<Response | undefined> {
//
}
}
The most important method of an action is the handle
method. The handle
method receives the values for any fields attached to the action, as well as a collection of selected models. The handle
method always receives a Collection of models, even if the action is only being performed against a single model.
Within the handle
method, you may perform whatever tasks are necessary to complete the action. You are free to update database records, send emails, call other services, etc. The sky is the limit!
Action Fields
Sometimes you may wish to gather additional information from the user before dispatching an action. For this reason, Avon allows you to attach most of Avon's supported fields directly to an action. To add a field to an action, add the field to the array of fields returned by the action's fields
method:
/**
* Get the fields available on the action.
*/
public fields(request: AvonRequest): Field[] {
return [];
}
Action Responses
Typically, when an action is executed, a "success" response will create by Avon. However, you are free to return your custom response:
/**
* Perform the action on the given models.
*/
protected async handle(fields: Fluent, models: Model[]): Promise<Response | undefined> {
// did some thing
return new MyResponse();
}
To create a custom response class, you have to create class extended base Avon Response
class like so:
// Actions/Responses/PublishedResponse.js
import { Response } from "avon-js";
export class PublishedResponse extends Response {
constructor(meta= {}) {
super(201, {}, { ...meta, type: 'PublishedResponse is my custom response'});
}
}
Registering Actions
Once you have defined an action, you are ready to attach it to a resource. Each resource created by Avon contains an actions
method. To attach an action to a resource, you should simply add it to the array of actions returned by this method:
/**
* Get the actions available on the entity.
*/
actions(request){
return [
new Publish()
];
}
Authorization Actions
If you would like to only expose a given action to certain users, you may invoke the canSee
method when registering your action. The canSee
method accepts a function which should return true
or false
. The function will receive the incoming HTTP request:
new Publish().canSee(request => false)
Sometimes a user may be able to "run" that an action exists but only against certain resources. you may use the canRun
method in conjunction with the canSee
method to have full control over authorization in this scenario. The callback passed to the canRun
method receives the incoming HTTP request and the resource model:
new Publish().canRun((request, model) => model.getKey() % 2 === 0)
Standalone Actions
Typically, actions are executed against resources selected on a resource index or detail API. However, sometimes you may have an action that does not require any resources / models to run. In these situations, you may register the action as a "standalone" action by invoking the standalone
method when registering the action. These actions always receives an empty collection of models in their handle
method:
/**
* Get the actions available on the entity.
*/
actions(request){
return [
new Publish().canRun(request => false)
];
}
Error Handling
Register Error Handler
The handleErrorUsing
on the Avon allows you to register a cutom callback to handle errors:
Avon.handleErrorUsing((error) => console.error(error))