`@dandi/mvc-hal` provides services for emitting [HAL](http://stateless.co/hal_specification.html) JSON (`application/hal+json`) from `@dandi/mvc` controllers.
npm install @dandi/mvc-hal@dandi/mvc-hal provides services for emitting
HAL JSON
(application/hal+json) from @dandi/mvc controllers.
To use the HAL services from @dandi/mvc-hal, add the MvcHalModule to
your server container.
- HalResultTransformer will intercept the value returned by controller
methods and attempt to compose it into a HAL resource.
- DefaultResourceComposer is the default composer used by
HalResultTransformer, and will automatically add a link for the self
relation, add any other relations specified by the @Relation() or
@ListRelation() decorators, and embed any relations specified on the
request by the _embedded querystring param.
Relations between resources, as well as resource identifiers, are
defined using decorators.
Use @ResourceId() to define the identifier property on a resource:
``typescript`
export class TaskList {
@ResourceId()
@Property(Uuid)
@Required()
public listId: Uuid
}
The @ResourceId() decorator on the resource model must correspond with@ResourceAccessor()
a decorator, which is applied to the controller@AccessorResourceId()
method used to get that resource, and a
decorator on the parameter specifying the source for model's ID:
`typescript
@Controller('/list')
export class TaskListController {
@HttpGet(':listId')
@ResourceAccessor(TaskList)
public getList(@PathParam(Uuid) @AccessorResourceId() listId: Uuid): Promise
...
}
}
`
The @AccessorResourceId() decorator on the listId parameter will be linkedlistId
to the property on the TaskList model since the@ResourceAccessor() decorator specifies TaskList as its type, andlistId is defined as its ID property by its own @ResourceId()
decorator.
Controller methods that list a resource can be identified using the
@ResourceListAccessor() decorator:
`typescript
@Controller('/list')
export class TaskListController {
@HttpGet(':listId')
@ResourceAccessor(TaskList)
public getList(@PathParam(Uuid) @AccessorResourceId() listId: Uuid): Promise
...
}
@HttpGet()
@ResourceListAccessor(TaskList)
public getAllLists(): Promise
...
}
}
`
The combination of these decorators enables the resource composer to
correctly and automatically generate the self relation link.
The @ResourceId() decorator can also be used in correlation with@Relation to define relations of a resource.
`typescript
export class Task {
@Property(Uuid)
@Required()
@ResourceId()
public taskId: Uuid
@Property(Uuid)
@Required()
@ResourceId(List, 'list')
public listId: Uuid
@Relation(List)
public list?: List
}
`
The @Relation() decorator on the list property marks that property@ResourceId()
as a relation. The decorator on the listId propertylist
describes that property as the identifier for the aforementioned TaskController
relation. Assuming a implementation with a@ResourceAccessor
corresponding for the Task resource, theselist
decorators will allow the resource composer to automatically generate
links or embed resources for the relation of a task.
Using the example of a task list, we will probably want the following
relations:
- Task has list relation to the TaskList it belongs toTaskList
- has a tasks relation to the array of Task resources
that belong to it
Attempting to do this with one model per resource will result in
unresolvable circular dependency issues. One way to work around this is
to define the relations in separate models:
`typescript
export class TaskList {
@ResourceId()
@Property(Uuid)
@Required()
public listId: Uuid;
}
export class Task {
@Property(Uuid)
@Required()
@ResourceId()
public taskId: Uuid;
@Property(Uuid)
@Required()
@ResourceId(List, 'list')
public listId: Uuid;
}
export class TaskListResource extends TaskList {
@ListRelation(Task)
public tasks: Task[];
}
export class TaskResource extends Task {
@Relation(List)
public list?: List;
}
@Controller('/list')
export class TaskListController {
@HttpGet(':listId')
@ResourceAccessor(TaskListResource)
public getList(@PathParam(Uuid) @AccessorResourceId() listId: Uuid): Promise
...
}
@HttpGet(':listId/task')
@ResourceListAccessor(Task)
public listTasks(@PathParam(Uuid) @AccessorResourceId(List) listId: Uuid): Promise
...
}
}
@Controller('/task')
export class TaskListController {
@HttpGet(':taskId')
@ResourceAccessor(TaskResource)
public getTask(@PathParam(Uuid) @AccessorResourceId() listId: Uuid): Promise
...
}
}
``