The API is the access point into use cases of the type Command API and Query API:
Name
The Name is used to name the request, response interface in the code. It also defines the URL of the endpoint.
For more details, see Naming.
Actor
The Actor protects the Command API endpoint with user authentication.
Schema (request)
The Request Schema defines the properties of the Command API endpoint's request body.
For more details, see Schema.
Schema (response)
The Name is used to name the request, response interface in the code. It also defines the URL of the endpoint.
For more details, see Naming.
Value Sources
The Response Schema Property Value Sources define how each property is initialized.
For more details, see Data Flow.
Addtionally Command API response can use the following Metadata Value Sources:
Name
The Name is used to name the request, response interface in the code. It also defines the URL of the endpoint.
For more details, see Naming.
Actor
The Actor protects the Query API endpoint with user authentication.
Schema
The Request Schema defines the query parameters of the Query API endpoint's request.
For more details, see Schema.
The Query API has no response sub-element.
Schema
The Query API's response body is defined by the schema and the query result type of the queried readmodel.
The Command API is split into two parts:
The Command API file is located at: src/Task/src/useCases/write/AddTask/infrastructure/AddTaskApi.ts
.
It contains the API class and interfaces for the API Request and the API Response.
1import { AddTaskCommand } from "../application/AddTaskCommand";2import { AddTaskCommandHandler } from "../application/AddTaskCommandHandler";3import { ValidationError, OverwriteProtectionBody, NotFoundError, ConflictError, PreconditionFailedError } from "@codebricks/typebricks";4import { TaskAggregate } from "shared/domain/aggregate/TaskAggregate";5import { TaskTitleValueObject } from "shared/domain/valueObjects/TaskTitleValueObject";6import { TaskDescriptionValueObject } from "shared/domain/valueObjects/TaskDescriptionValueObject";7import { AssigneeIdValueObject } from "shared/domain/valueObjects/AssigneeIdValueObject";89export interface AddTaskApiRequest {10 title: string;11 description: string;12 assigneeId: string;13}1415export interface AddTaskApiResponseBody {16 taskId: string;17}1819export interface AddTaskApiResponse {20 statusCode: number;21 body: string;22 headers: any;23}2425export class AddTaskApi {26 constructor(readonly commandHandler: AddTaskCommandHandler = new AddTaskCommandHandler()) {27 }2829 @OverwriteProtectionBody(false)30 async handle(request: AddTaskApiRequest): Promise<AddTaskApiResponse> {31 const headers = {32 'Access-Control-Allow-Origin': '*',33 'Access-Control-Allow-Credentials': true34 };35 try {36 const command: AddTaskCommand = new AddTaskCommand({37 title: new TaskTitleValueObject(request.title),38 description: new TaskDescriptionValueObject(request.description),39 assigneeId: new AssigneeIdValueObject(request.assigneeId),40 });41 const aggregate: TaskAggregate = await this.commandHandler.handleAddTask(command);42 const responseBody: AddTaskApiResponseBody = {43 taskId: aggregate.id,44 };45 return {46 statusCode: 200,47 body: JSON.stringify({ data: responseBody }),48 headers: headers49 };50 } catch (error) {51 console.log(error);52 if (error instanceof ValidationError) {53 return {54 statusCode: 400,55 body: JSON.stringify({ error: error.message }),56 headers: headers57 };58 } else if (error instanceof SyntaxError && error.message.match(/Unexpected.token.*JSON.*/i)) {59 return {60 statusCode: 400,61 body: '{ "error": "bad request: invalid json"}',62 headers: headers63 };64 } else if (error instanceof NotFoundError) {65 return {66 statusCode: 404,67 body: JSON.stringify({ error: error.message }),68 headers: headers69 };70 } else if (error instanceof ConflictError) {71 return {72 statusCode: 409,73 body: JSON.stringify({ error: error.message }),74 headers: headers75 };76 } else if (error instanceof PreconditionFailedError) {77 return {78 statusCode: 412,79 body: JSON.stringify({ error: error.message }),80 headers: headers81 };82 }8384 return {85 statusCode: 500,86 body: '{ "error": "Internal Server Error"}',87 headers: headers88 };89 }90 }91}
Key Functions of the API Class
The API handler file is located at: src/Task/src/useCases/write/AddTask/infrastructure/AddTaskApiHandler.ts
.
It serves as an entry point into the Lambda function and translates AWS API Gateway events into vendor-agnostic requests.
1import { APIGatewayProxyEvent, APIGatewayProxyResult, parseToDateTime } from "@codebricks/typebricks";2import { AddTaskApi } from "./AddTaskApi";3import { AddTaskApiRequest } from "./AddTaskApi";4import { initDataSource, destroyDataSource } from "shared/infrastructure/persistence/AppDataSource";56/** @overwrite-protection-body false */7export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {8 try {9 await initDataSource();10 const addTaskApi: AddTaskApi = new AddTaskApi();11 const request: AddTaskApiRequest = JSON.parse(event.body ? event.body : '{}', parseToDateTime) as AddTaskApiRequest;12 return await addTaskApi.handle(request) as unknown as Promise<APIGatewayProxyResult>;13 } catch (error: any) {14 console.log(error);15 if (error instanceof SyntaxError && error.message.match(/Unexpected.token.*JSON.*/i)) {16 return Promise.resolve({17 statusCode: 400,18 body: '{ "error": "bad request: invalid json"}',19 headers: {20 'Access-Control-Allow-Origin': '*',21 'Access-Control-Allow-Credentials': true22 }23 }) as Promise<APIGatewayProxyResult>;24 } else {25 return Promise.resolve({26 statusCode: 500,27 body: '{ "error": "Internal Server Error"}',28 headers: {29 'Access-Control-Allow-Origin': '*',30 'Access-Control-Allow-Credentials': true31 }32 }) as Promise<APIGatewayProxyResult>;33 }34 } finally {35 await destroyDataSource();36 }37}
Key Functions of the API Handler Class:
The Query API is split into two parts:
The Query API implementation is found at: Task/src/useCases/read/AssigneeTaskOverview/infrastructure/AssigneeTaskOverviewApi.ts
.
1import { AddTaskCommand } from "../application/AddTaskCommand";2import { AddTaskCommandHandler } from "../application/AddTaskCommandHandler";3import { ValidationError, OverwriteProtectionBody, NotFoundError, ConflictError, PreconditionFailedError } from "@codebricks/typebricks";4import { TaskAggregate } from "shared/domain/aggregate/TaskAggregate";5import { TaskTitleValueObject } from "shared/domain/valueObjects/TaskTitleValueObject";6import { TaskDescriptionValueObject } from "shared/domain/valueObjects/TaskDescriptionValueObject";7import { AssigneeIdValueObject } from "shared/domain/valueObjects/AssigneeIdValueObject";89export interface AddTaskApiRequest {10 title: string;11 description: string;12 assigneeId: string;13}1415export interface AddTaskApiResponseBody {16 taskId: string;17}1819export interface AddTaskApiResponse {20 statusCode: number;21 body: string;22 headers: any;23}2425export class AddTaskApi {26 constructor(readonly commandHandler: AddTaskCommandHandler = new AddTaskCommandHandler()) {27 }2829 @OverwriteProtectionBody(false)30 async handle(request: AddTaskApiRequest): Promise<AddTaskApiResponse> {31 const headers = {32 'Access-Control-Allow-Origin': '*',33 'Access-Control-Allow-Credentials': true34 };35 try {36 const command: AddTaskCommand = new AddTaskCommand({37 title: new TaskTitleValueObject(request.title),38 description: new TaskDescriptionValueObject(request.description),39 assigneeId: new AssigneeIdValueObject(request.assigneeId),40 });41 const aggregate: TaskAggregate = await this.commandHandler.handleAddTask(command);42 const responseBody: AddTaskApiResponseBody = {43 taskId: aggregate.id,44 };45 return {46 statusCode: 200,47 body: JSON.stringify({ data: responseBody }),48 headers: headers49 };50 } catch (error) {51 console.log(error);52 if (error instanceof ValidationError) {53 return {54 statusCode: 400,55 body: JSON.stringify({ error: error.message }),56 headers: headers57 };58 } else if (error instanceof SyntaxError && error.message.match(/Unexpected.token.*JSON.*/i)) {59 return {60 statusCode: 400,61 body: '{ "error": "bad request: invalid json"}',62 headers: headers63 };64 } else if (error instanceof NotFoundError) {65 return {66 statusCode: 404,67 body: JSON.stringify({ error: error.message }),68 headers: headers69 };70 } else if (error instanceof ConflictError) {71 return {72 statusCode: 409,73 body: JSON.stringify({ error: error.message }),74 headers: headers75 };76 } else if (error instanceof PreconditionFailedError) {77 return {78 statusCode: 412,79 body: JSON.stringify({ error: error.message }),80 headers: headers81 };82 }8384 return {85 statusCode: 500,86 body: '{ "error": "Internal Server Error"}',87 headers: headers88 };89 }90 }91}
Key Functions of the API Class:
The API Handler is located at: Task/src/useCases/read/AssigneeTaskOverview/infrastructure/AssigneeTaskOverviewApiHandler.ts
.
1import { APIGatewayProxyEvent, APIGatewayProxyResult, parseToDateTime } from "@codebricks/typebricks";2import { AddTaskApi } from "./AddTaskApi";3import { AddTaskApiRequest } from "./AddTaskApi";4import { initDataSource, destroyDataSource } from "shared/infrastructure/persistence/AppDataSource";56/** @overwrite-protection-body false */7export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {8 try {9 await initDataSource();10 const addTaskApi: AddTaskApi = new AddTaskApi();11 const request: AddTaskApiRequest = JSON.parse(event.body ? event.body : '{}', parseToDateTime) as AddTaskApiRequest;12 return await addTaskApi.handle(request) as unknown as Promise<APIGatewayProxyResult>;13 } catch (error: any) {14 console.log(error);15 if (error instanceof SyntaxError && error.message.match(/Unexpected.token.*JSON.*/i)) {16 return Promise.resolve({17 statusCode: 400,18 body: '{ "error": "bad request: invalid json"}',19 headers: {20 'Access-Control-Allow-Origin': '*',21 'Access-Control-Allow-Credentials': true22 }23 }) as Promise<APIGatewayProxyResult>;24 } else {25 return Promise.resolve({26 statusCode: 500,27 body: '{ "error": "Internal Server Error"}',28 headers: {29 'Access-Control-Allow-Origin': '*',30 'Access-Control-Allow-Credentials': true31 }32 }) as Promise<APIGatewayProxyResult>;33 }34 } finally {35 await destroyDataSource();36 }37}
Key Functions of the API Handler Class: