The source code is organized like below:
When adding your logic to the project, please remember one rule: NEVER CHANGE THE CODE IN _core
, the code in it will be encapsulated in the framework in future versions.
- DI Container
- @fastify/awilix
- Supporting Services
- db + jwt (static jwt for now)
- Router Builder
- controller + action + jwt hook
- Application Class
- Request Schema
- Typesafe Env
- Swagger UI
- DBM
- add/change tables in
src/schema.ts
. npm run generate
.- review generated sql files.
npm run migrate
- continue the loop of 1~4 until you have done.
WARNING Don't change the
drizzle
manually, if you want to drop any migrations, trynpm run drop
.
Note: for db orm tools, we intentionally exclude prisma
, reasons:
prisma
is heavy weight.drizzle
can work withpostgres.js
which is the fastest pg driver in node.drizzle
is more sql aware, without sql in head, orm tools can be abused.
- Application
new Application()
.pgOpts(...)
.controllers(controllers)
.securityRules(securityRules)
.start(port, host);
Note:
- if the predefined options not set, the predefined service will not be initialized. ie: no pgOpts, no dbService.
- you can always register your own service:
Application.register
- the predefined services have predefined names.
- Controller + Action + JWT Rules
function handler1(request: FastifyRequest, reply: FastifyReply) {
reply.code(200).send('handler1');
}
async function handler2(request: FastifyRequest, reply: FastifyReply) {
reply.code(201).send('handler2');
}
const controllers: Controller[] = [
{
prefix: '/',
actions: [{path: '/test', method: 'get', handler: handler1}],
},
{
prefix: '/sub',
actions: [{path: '/test', method: 'post', handler: handler2}],
},
{
prefix: '/protected',
actions: [{path: '/test', method: 'get', handler: handler1}],
},
{
prefix: '/protected',
actions: [{path: '/test', method: 'post', handler: handler2}],
},
{
prefix: '/banned-all',
actions: [{path: '/test', method: 'all', handler: handler1}],
},
];
const securityRules: JwtFilterRule[] = [
{pattern: /^\/protected/, httpMethod: 'post'},
{pattern: /^\/banned-all/},
];
- Get the injected service
// in an action
async function actionHandler(
this: FastifyInstance, // NOTE: it MUST be named 'this'
request: FastifyRequest,
reply: FastifyReply
) {
const injected = this.diContainer.resolve(name);
...
}
// in a service, you can get it in constructor.
// the parameter is Awilix Cradle
constructor(opt: any) {
const injected = opt[name];
}
// out of a service, you can get it by application.resolve
const app = new Application().register({test: asValue('test')});
t.is(app.resolve('test'), 'test');
- Request Schema
export const listUser: Action = {
path: '/',
method: 'get',
options: {
schema: {
response: {
200: z.array(selectSchema),
},
},
},
handler: listUserHandler,
};
- Typesafe Env
export const env = createEnv({
clientPrefix: '',
server: {
JWT_SECRET: z.string().default('SuPeRpaSsW0rd'),
FASTIFY_PORT: z.coerce.number().default(3006),
FASTIFY_ADDRESS: z.string().default('127.0.0.1'),
LOG_LEVEL: z.string().default('debug'),
DB_HOST: z.string().default('127.0.0.1'),
DB_PORT: z.coerce.number().default(5432),
DB_USER: z.string().default('test_admin'),
DB_PASSWORD: z.string().default('admin'),
DB_DATABASE: z.string().default('test'),
},
client: {},
runtimeEnv: process.env,
});
- Swagger UI
npm start
- goto: http://127.0.0.1:3006/documentation