A lightweight Node.js and TypeScript dependency injection framework powered tsyringe. for building APIs and microservices. You can use only with fastify.
npm install --save helocore
Modify your tsconfig.json
to include the following settings
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
// Redis.ts
import { singleton } from "helocore";
@singleton()
export default class Redis {
// ...
}
// ResponseModel.ts
export default class ResponseModel<T> {
public data?: T
public status_code: number = 200
public trace_id: string = ""
}
// application.ts
import { Modules, PermissionModule, HandleErrorResponse, DataSource, dataSourceList, EventsModules, coreSettings } from "helocore";
import TestController from './TestController'
import PermissionControl from './PermissionControl'
import Redis from './Redis'
import ResponseModel from './ResponseModel'
import EventsTest from './Events'
// default true
coreSettings.logger = false
// Controller layer defining
Modules([
TestController
])
// Event layer defining
EventsModules([
EventsTest
])
// Permission Control Layer
PermissionModule(PermissionControl)
// --> Datasource layer. You can take connection container defined key.
// example
// dataSourceList.redis => giving containered Redis
DataSource({
redis: Redis
})
// --> Controller Handle Error
// error => error
// traceId => generated request unique key
// step => <controller | service>
// lang => req.headers['accept-language']
HandleErrorResponse<ResponseModel<any>>(async (error: any, traceId: string, step: string, lang: string) => {
const response = new ResponseModel()
response.status_code = 400
response.trace_id = traceId
return response
})
// index.ts
import "reflect-metadata";
import "./application";
import { fetchRoutes } from "helocore";
const fastify = Fastify()
fastify.register((app, _, done) => {
fetchRoutes(app)
done()
}, { prefix: '/api/v1' })
fastify.listen({ port: 3000 }, () => console.log('API is Running'))
- logger: helocore logger with trace_id to command prompt. it can reachable from
req.trace_id
This decorator is determine your prefix routes and at the same time your controller layer
import { Controller, injectable } from "helocore";
@Controller('/prefix')
@injectable()
export default class TestController {
// ...
}
These decorators are determine your routes endpoints
// TestService.ts
import { Service, injectable } from "helocore";
@Service // not must. only for logs now
@injectable()
export default class TestService {
Get(lang) {
return {
name: 'helocore'
}
}
}
import { Controller, Get, injectable } from "helocore";
import TestService from "./TestService";
@Controller('/prefix')
@injectable()
export default class TestController {
constructor(
private readonly testService: TestService
) { }
@Get('/get') // endpoint => /api/v1/prefix/get -- Method => GET
async Get() {
const testData = await this.testService.Get()
// ...
}
}
import { Controller, Post, Body, injectable } from "helocore";
@Controller('/prefix')
@injectable()
export default class TestController {
@Post('/save')
Save(@Body() body: TBody) {
// ...
}
}
This decorator is determine middleware layer on your routes. this layer run before run from controller layer. We have two selection middleware type
// testvalidation.ts
import { defineMiddleware, Body, singleton } from "helocore";
@defineMiddleware
@singleton()
export default class TestValidation {
Validation(@Body body: TBody) {
// ...
}
}
// testcontroller.ts
import { Controller, Body, injectable, Post } from "helocore";
import TestValidation from "./testvalidation";
@Controller('/prefix', [ // prefix middleware
{
funcs: ['Validation'],
class: TestValidation
}
])
@injectable()
export default class TestController {
@Post('/save')
Save(@Body body: TBody) {
// ...
}
}
// testcontroller.ts
import { Controller, Body, Middleware, injectable, Post } from "helocore";
import TestValidation from "./testvalidation";
@Controller('/prefix')
@injectable()
export default class TestController {
@Post('/save')
@Middleware([
{
funcs: ['Validation'],
class: TestValidation
}
])
Save(@Body body: TBody) {
// ...
}
}
This decorator is determine permission middleware layer.
// PermissionControl.ts
import { singleton, definePermission, Request, Body, IDefinePermission } from "helocore";
@definePermission
@singleton()
export default class PermissionControl implements IDefinePermission {
async CheckPermission(permissions: Array<string>, @Request req: FastifyRequest, @Body body: TBody): Promise<boolean> {
// ...
// permissions => ['test.create', 'test.list']
return true
}
}
// testcontroller.ts
import { Controller, Body, Middleware, injectable, Post, Permissions } from "helocore";
@Controller('/prefix')
@injectable()
export default class TestController {
@Post('/save')
@Permissions('test.create', 'test.list')
Save(@Body body: TBody) {
// ...
}
}
This decorator is determine request limit on your route. You can look here ratelimit doc
// index.ts
import "reflect-metadata";
import "./application";
import Fastify from 'fastify';
import RateLimit from '@fastify/rate-limit';
import { fetchRoutes } from "helocore";
const fastify = Fastify()
fastify.register(RateLimit, {
errorResponseBuilder: function (request, context) {
return {
code: 429,
error: 'Too Many Requests',
message: `Rate limit exceeded, retry in ${context.after}. Try again soon.`,
expiresIn: Math.ceil(context.ttl / 1000)// seconds
}
}
})
fastify.register((app, _, done) => {
fetchRoutes(app)
done()
}, { prefix: '/api/v1' })
fastify.listen({ port: 3000 }, () => console.log('API is Running'))
// testcontroller.ts
import { Controller, Body, RateLimit, injectable, Post } from "helocore";
@Controller('/prefix')
@injectable()
export default class TestController {
@Post('/save')
@RateLimit({
max: 3,
timeWindow: 10000
})
Save(@Body body: TBody) {
// ...
}
}
You can define custom param decorator
// customdecorators.ts
import { FastifyRequest } from 'fastify';
import { createParamDecorator } from "helocore";
export const Lang = createParamDecorator((req: FastifyRequest) => {
return req.headers['accept-language']
})
// testcontroller.ts
import { Controller, injectable, Post } from "helocore";
import { Lang } from "./customdecorators";
@Controller('/prefix')
@injectable()
export default class TestController {
@Post('/save')
Save(@Lang lang: string ) {
console.log(lang)
// ...
}
}
You can determine service for logs
// TestService.ts
import { Service, injectable } from "helocore";
@Service // not must. only for logs now
@injectable()
export default class TestService {
Save(lang) {
console.log(lang)
// ...
}
}
// testcontroller.ts
import { Controller, injectable, Post } from "helocore";
import TestService from "./TestService";
@Controller('/prefix')
@injectable()
export default class TestController {
constructor(
private readonly testService: TestService
) { }
@Post('/save')
Save() {
this.testService.Save(lang)
// ...
}
}
You can determine events
// events.ts
import { OnEvent, injectable } from 'helocore'
@injectable()
export default class EventTest {
@OnEvent('test')
create(data: object) {
console.log(data)
}
}
// application.ts
import { EventsModule } from 'helocore'
import EventTest from "./EventTest"
// ..
EventsModule([
EventTest
])
// ..
// eventcontroller.ts
import { Controller, Events, Get, injectable } from 'helocore'
@Controller('/event')
@injectable()
export default class EventController {
constructor(
private readonly events: Events
) { }
@Get('/')
async EventTest() {
this.events.emit('test', { message: 'test_message' })
// ...
}
}
it can use fastify route options
// controller.ts
import { Controller, EndpointOptions, Post, injectable } from 'helocore'
@Controller('/')
@injectable()
export default class TestController {
@Post('/')
@EndpointOptions({ // fastify route options
bodyLimit: 10485760 // json body 11MB
})
async Test() {
// ...
}
}
This project welcomes contributions and suggestions. If you see missing or want to improve on this project, you can create issue and open pull request.