Implement REST APIs
Controllers
In API Namespaces, there is a file for each operation. Each file will have a method for the actual operation logic and another method for handling the errors that could appear during the processing of the operation.
The auto generated stub for the API operations look like the following:
import { Context as RequestContext } from 'solution-framework';
import { V1BaseControllers, V1Request as Request, V1Response as Response, V1Schema as Schema } from 'solution-framework';
/**
* AddDateApi
*/
export class AddDateApi extends V1BaseControllers.AddDateApiBase {
constructor(requestContext: RequestContext) {
super(requestContext);
}
/**
* @param request Request.AddDateRequest
* addDate logic
*/
addDate = async (request: Request.AddDateRequest) => {
this.util.log.info('start addDate execution');
// TODO: add your implementation logic
}
/**
* @param request Request.AddDateRequest
* @param error TypeError
* addDateErrorHandler logic
*/
addDateErrorHandler = async (error: TypeError, request: Request.AddDateRequest) => {
this.util.log.info('start addDateErrorHandler execution');
// TODO: error handling should go here
// this.response;
}
}
Operation Handler
The Operation Handler method should have the actual implementation logic.
The request is provided as a type-safe object which is dependent on what is modelled in the API Namespace and holds the following properties:
query (containing query parameters of the request)
path (containing path parameters of the request)
body (containing the request body - only for PUT and POST operations)
_request (the original express request object)
The handler also provides type-safe access to the response of the operation. A response always consists out of the following properties:
body (the response body that has been modelled in the API nampespace)
statusCode (HTTP status code also modelled in the API Namespace)
Additionally, errors can be thrown inside the operation handler in case of special or unexpected situations.
Access request object and set response object
addDate = async (request: Request.AddDateRequest) => {
this.util.log.info('start addDate execution');
const value = request.query.prop1;
// alternative way to define myAddDateQueryVar using the provided types
// const value: Request.AddDateQuery = request.query.prop1;
// further operation logic
// .....
if(value == 'Valid') {
// set response body
this.response.body = {
prop1: value
};
// set response status code
this.response.status = 200;
} else {
// throw an error
const myError: Schema.Error = {
code: 400,
message: 'Some invalid value'
};
throw myError;
}
}
Trigger a domain service from an API operation
addDate = async (request: Request.AddDateRequest) => {
// ...
// prepare service input
const input = this.factory.entity.ServiceIdentifier_Input();
input.property1 = request.query.prop1;
// trigger service
const serviceOutput = await this.services.date.ServiceIdentifier(input);
// further logic to set response based on service output
// ...
}
Trigger a command from an API operation
addDate = async (request: Request.AddDateRequest) => {
// ...
// load entity from repository
const thing = await this.repo.date.Thing.findById('someId');
// prepare command input
const input = this.factory.entity.Command1_Input();
input.property1 = request.query.prop1;
// trigger commad
await thing.Command1(input);
// further logic to set response
// ...
}
Error Handler
The Error Handler method should have all error handling logic thrown from the operation handler method. The error that has been thrown and the request will be available as handler parameters. Inside the handler the response can be set to represent what went wrong during the execution of the operation.
Please see the related article Error handling examples for more details.