npm: npm install @bakku/platform
yarn: yarn install @bakku/platform
refer Sample Project
startServer(
appOptions?: TypeServerOptions,
createExpressApp?: () => Express,
ssl?: https.ServerOptions
)
name | type | required | default | description |
---|---|---|---|---|
appOptions | TypeServerOptions | NO | input information for setting app | |
createExpressApp | () => Express | NO | createApp |
custom create express instance, base on that for create APIs. Default createApp function in platform |
ssl | https.ServerOptions | NO | case use directive SSL for server |
name | type | required | default | description |
---|---|---|---|---|
host | string | NO | host which server will be used, should be 0.0.0.0 | |
port | number | NO | 8080 | port which server will be used |
apiPrefix | string | NO | prefix for all API => https://domain/[prefix]/[controller.paht]/[controller.apiPath] | |
logger | log object | NO | console | custom logging |
appHandlers | ExRequestHandler[] | NO | Array handler for express application, EX: CORS, userMiddleware... | |
staticHandlers | [key: string]: ExRequestHandler | ExRequestHandler[] | NO | static handlers, often use for static file |
documentOptions | TypeDocumentHandlers | NO | config for display APIs document | |
documentOptions | TypeDocumentHandlers | NO | config for display APIs document | |
errorHandlerOptions | TypeErrorHandlers | NO | Handle error default | |
bodyJsonSetting | bodyParser.OptionsJson | NO | config for parse json data in express: express.json(..) | |
appSettings | [key: string]: any | NO | config for app use any setting. EX: convert date format, json... |
Example:
const options: TypeServerOptions = {
apiPrefix: 'api', // => all your APis will be have prefix : /api/...
logger: global.applicationContexts.logger,
documentOptions: {
docPath: 'doc', // => API document page
},
};
startServer(options);
@Controller({
name: string | symbol,
path: string,
description?: string
})
EX:
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
....
}
@API(option: { type: TypeRequestMethod; path: string; description?: string; bodyContentType?: string; }): define any API
Note: TypeRequestMethod: 'get' | 'post' | 'put' | 'patch' | 'delete' | 'all'
@Get(path: string, description?: string): define Get API
@Delete(path: string, description?: string): define Delete API
@Post(path: string, description?: string, bodyContentType?: string): define Post API
@Put(path: string, description?: string, bodyContentType?: string): define Put API
@Patch(path: string, description?: string, bodyContentType?: string): define Patch API
@UseAll(path: string, description?: string, bodyContentType?: string): define UseAll API
Note: ExRequestHandler is RequestHandler - (req, res, next) : void EX:
const requireLoggedInMiddleWare = async ( req: ExRequest, _res: ExResponse, next: ExNextFunction) => {
let user;
// check user from header token
//....
if (!user) throw new Error('You need to login first!');
req.user = user;
next();
};
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Get('user')
@Middleware(requireLoggedInMiddleWare)
doSomething(@Req() req: ExRequest) {
return req.user;
}
}
const customRequireAdminInMiddleWare =() => {
// get roles
const adminRoles = ...// do somethings.
return async ( req: ExRequest, \_res: ExResponse, next: ExNextFunction) => {
let user;
// check user from header token
//....
if (!user || !user.role || !adminRole.includes(user.role)) throw new Error('You are not allow process this feature!');
req.user = user;
next();
}
};
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Get('admin')
@MiddlewareCustom(customRequireAdminInMiddleWare)
doSomething(@Req() req: ExRequest) {
return req.user;
}
}
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Get('admin')
doSomething(
@Headers() headers: any,
) {
return {headers};
}
}
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Post('test')
doSomething(
@Queries() queries: any,
) {
return {queries};
}
}
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Post('test')
doSomething(
@Params() params: any,
) {
return {params};
}
}
@Body()
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Post('test')
doSomething(
@Body() body: any,
) {
return {body};
}
}
@Req()
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Post('test')
doSomething(
@Req() req: ExRequest,
) {
console.log(req.header, req.body, ...)
return {ok: true};
}
}
@Res()
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Post('test')
doSomething(
@Res() res: ExResponse,
) {
console.log(res.header,, ...)
return {ok: true};
}
}
In normal when request success, the response will received the returned data from controller method and use it as body data.
I
Error case:
Response:
header:
status: statusCode
body:
error: {
status: statusCode,
code: errorCode, //base on requirement
message: errorMessage //base on requirement
}
refer @bakku/validation
Validation base on schema when getting data from request.
For validation auto run, pass the corresponding validation schema.
EX:
const HeaderSecretDataSchema: IObjectSchema = {
type: 'object',
validation: { isRequired: true },
properties: {
'apikey': StringRequireSchema,
},
};
@Controller({
name: 'HomeController',
path: 'home',
})
export class HomeController {
@Get('admin')
doSomething(
@Headers(HeaderSecretDataSchema) headers: any,
) {
// header.apikey will be validate as required, if not it will throw error
return {headers};
}
}
=====================
=> response:
header: 400
body: {
error: {
status: 400,
code: ...
message: 'headers.apikey' are required
}
}
Note: the same for other data:
@Body(schema) body: any
@Queries(schema) body: any
@Params(schema) body: any
@[DI](options: IDataPropertyInjectedParams)
IDataPropertyInjectedParams is extends data from ISchemaGeneral of @bakku/validation with:
options.sample // sample data for displaying in document api
options.description //// description data for displaying in document api
@DataProperty - require data
@DataPropertyOptional - not require data
EX:
class CreateAccountRequestBodyDto {
@DataProperty({ validation: { format: 'isEmail' } })
email: string;
@DataProperty({ validation: { format: new RegExp('^(?=.*?[0-9])(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$') } })
password: string;
}
//==> Result:
....
@Body() body: CreateAccountRequestBodyDto,
...
@DataEnumProperty -
@DataEnumPropertyOptional
EX:
enum AccountType {
USER = 'USER',
MANAGER = 'MANAGER',
}
class CreateAccountRequestBodyDto {
...
@DataEnumProperty({ enumData: getEnumData(AccountType) })
accountType: AccountType;
...
}
//==> Result:
....
@Body() body: CreateAccountRequestBodyDto,
...
@DataArrayProperty -
@DataArrayPropertyOptional
EX:
class Person {
@DataProperty()
name:
}
const StringSchema: IStringSchema = { type: 'string' };
class RequestBodyDto {
...
@DataArrayProperty({ itemSchema: { propertyType: Person } })
listPerson: Person[];
@DataArrayProperty({ itemSchema: StringSchema })
listMessages: string[];
...
}
//==> Result:
....
@Body() body: RequestBodyDto,
...
3.2.5 Data is file upload
@DataFileProperty -
@DataFilePropertyOptional
const options: TypeServerOptions = {
apiPrefix: 'api',
errorHandlerOptions: {
isSelfHandleError: true,
},
};
const { app } = startServer(options);
routeAppError(app, options);
using with swagger-ui-express
Note: because default handle error will force all APIs which is defined after start server is not found APIs. So for prevent it, and define API for swagger, should use combine with custom handle error above.
....
import swaggerUi from 'swagger-ui-express';
....
const options: TypeServerOptions = {
apiPrefix: 'api',
errorHandlerOptions: {
isSelfHandleError: true,
},
};
const { app, apiData } = startServer(options);
const swaggerJson = convertToSwaggerJson(apiData);
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerJson));
routeAppError(app, options);
...