fastro
TypeScript icon, indicating that this package has built-in type declarations

0.3.11 • Public • Published

build js-standard-style

Fastro Core

Simple, clean, fast & scalable. Dependency injection ready.

Main tech stack: fastify, typeorm & typescript.

Getting Started

You can clone this getting-started codes here: https://github.com/fastrojs/fastro-template.git

Install typescript

npm i typescript @types/node -D

Install fastro

npm i fastro

Very Basic Folder Structure:

.
├── src
│   ├── hello.controller.ts
│   └── main.ts
├── package.json
├── server.config.js
└── tsconfig.json

Create configuration file:

Server configuration:

// file server.config.js
module.exports = {
  app: {
    host: '0.0.0.0',
    port: 3000
  },
  database: {
    name: 'default',
    type: 'mysql',
    port: 3306,
    host: 'localhost',
    username: 'root',
    password: 'root',
    database: 'test',
    synchronize: true,
    logging: false
  }
}

Typescript configuration:

// file tsconfig.json
{
  "compilerOptions"{
    "module""commonjs",
    "esModuleInterop"true,
    "resolveJsonModule"true,
    "allowSyntheticDefaultImports"true,
    "target""es6",
    "strict"true,
    "noImplicitAny"false,
    "moduleResolution""node",
    "sourceMap"true,
    "outDir""dist",
    "baseUrl"".",
    "jsx""react",
    "emitDecoratorMetadata"true,
    "experimentalDecorators"true,
    "strictPropertyInitialization"false
  }
}

Create controller and route:

You can make a controller by create *.controller.ts file and use @Controller decorator. Then do not forget to inject a route decorator --for example @Get.

// file hello.controller.ts
import { Controller, Get } from 'fastro'
import { FastifyReply, FastifyRequest } from 'fastify'
import { Http2ServerResponse } from 'http2'
 
@Controller()
export class HelloController {
  @Get()
  sayHello (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): any {
    reply.send('hello')
  }
}
 

You can add prefix and other options on @Controller decorator. Check this for detail options: https://www.fastify.io/docs/latest/Plugins/#plugin-options

If @Controller decorator have no options, default value is:

  { prefix: '/' }

Available route decorator:

  • @Get
  • @Post
  • @Delete
  • @Head
  • @Patch
  • @Put
  • @Options

You can also add options to route decorator. Check this for detail: https://www.fastify.io/docs/latest/Routes/#options

If @Get route decorator have no options, default value is:

  { url: '/' }

Create server:

// file main.ts
import { createServer, start } from 'fastro'
 
createServer()
  .then(server => {
    start(server)
  })

You can pass fastify server options on createServer function.

createServer({ logger: true })

Check this for other options: https://www.fastify.io/docs/latest/Server/

Run server:

Build ts files

npx tsc

Run compiled script

NODE_ENV=production node dist/main.js

Check package.json for better way to run the app.

After create controller, route and start server, you can access end point via http://localhost:3000/

Dependency Injection

You can see this basic dependency injection codes here: https://github.com/fastrojs/fastro-template/tree/di

Create Service

// file hello.service.ts
import { Service } from 'fastro'
 
@Service()
export class HelloService {
  public sayHello (): string {
    return 'Hello'
  }
}
 

Inject service to controller

// file hello.controller.ts
import { Controller, Get, InjectService, Hook } from 'fastro'
import { FastifyReply, FastifyRequest } from 'fastify'
import { Http2ServerResponse } from 'http2'
import { HelloService } from './hello.service'
 
@Controller()
export class HelloController {
  @InjectService(HelloService)
  service: HelloService
 
  @Get()
  sayHello (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): void {
    const hello = this.service.sayHello()
    reply.send(hello)
  }
}

After create a controller, you can access end point via http://localhost:3000/

Create Entity

// file user.entity.ts
import { Entity, Column, Index } from 'typeorm'
import { BasicEntity } from 'fastro'
 
@Entity()
export class User extends BasicEntity {
  @Column({ unique: true })
  email: string
 
  @Column({ unique: true })
  username?: string
 
  @Column()
  password?: string
}
  • Entity is database table abstraction.
  • BasicEntity is class that define id, createdAt, and updatedAt fields.

Use entity on service

To connect with database entity, all service class must extends BasicService.

// file hello.service.ts
import { Service, BasicService } from 'fastro'
import { User } from './user.entity'
 
@Service()
export class HelloService extends BasicService {
  public sayHello (): string {
    return 'Hello'
  }
 
  public async getAllUser (): Promise<User[]> {
    try {
      return this.repo(User).find({
        select: ['username', 'email']
      })
    } catch (error) {
      throw this.err('GET_ALL_USER_ERROR', error)
    }
  }
}
 

Now you can use getAllUser function on controller.

import { Controller, Get, InjectService, Hook } from 'fastro'
import { FastifyReply, FastifyRequest } from 'fastify'
import { Http2ServerResponse } from 'http2'
import { HelloService } from './hello.service'
 
@Controller()
export class HelloController {
  @InjectService(HelloService)
  service: HelloService
 
  @Get()
  sayHello (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): void {
    const hello = this.service.sayHello()
    reply.send(hello)
  }
 
  @Get({ url: '/user' })
  async getUser (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): Promise<void> {
    const user = await this.service.getAllUser()
    reply.send(user)
  }
}

After add getUser route, you can access end point via http://localhost:3000/user

Create Controller Hooks

Hooks allow you to listen to specific events in the application or request/response lifecycle. You can add hook just using @Hook decorator and *.controller.ts file.

Check this for detail: https://www.fastify.io/docs/latest/Hooks/#requestreply-hooks

// file hello.controller.ts
import { Controller, Get, InjectService, Hook } from 'fastro'
import { FastifyReply, FastifyRequest } from 'fastify'
import { Http2ServerResponse } from 'http2'
import { HelloService } from './hello.service'
 
@Controller()
export class HelloController {
  @InjectService(HelloService)
  service: HelloService
 
  @Get()
  sayHello (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): void {
    const hello = this.service.sayHello()
    reply.send(hello)
  }
 
  @Get({ url: '/user' })
  async getUser (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): Promise<void> {
    const user = await this.service.getAllUser()
    reply.send(user)
  }
 
  @Hook('onRequest')
  async myHook (request: FastifyRequest, reply: FastifyReply<Http2ServerResponse>): Promise<void> {
    // This hook will always be executed after the shared `onRequest` hooks
    // Check this for detail: https://www.fastify.io/docs/latest/Hooks/#scope
    console.log('request', request.headers)
  }
}

Create Gateway

You can see this gateway example code here: https://github.com/fastrojs/fastro-template/tree/gateway

The gateway is used to put multiple controllers in one class, so you can access via one prefix and add fastify hook for all injected controllers.

You can create a gateway using @Gateway decorator on *.gateway.ts file.

You can create gateway hook using @GatewayHook decorator.

// web.gateway.ts
import { Gateway, InjectController, GatewayHook } from 'fastro'
import { HelloController } from './hello.controller'
import { FastifyRequest, FastifyReply } from 'fastify'
 
@Gateway({ prefix: 'web' })
export class WebGateway {
  @InjectController(HelloController)
  helloController: HelloController
 
  @GatewayHook('onRequest')
  async myHook (request: FastifyRequest): Promise<void> {
    // This hook will always be executed 
    // after the shared `onRequest` hooks
  }
}
 

After gateway creation, you can access url endpoint via:

Please note that if you put a controller on a gateway, you can not access controller initial end point directly. You can access it via gateway prefix only.

You can add prefix and other options on @Gateway decorator. Check this for detail options: https://www.fastify.io/docs/latest/Plugins/#plugin-options

If @Gateway decorator have no options, default value is:

  { prefix: '/' }

Create Plugin

You can create new plugin by just copy and modify support.plugin.ts file. No need fastify-plugin package again. Server will load and register it automatically.

// support.plugin.ts
import { FastifyInstance } from 'fastify'
 
export const plugin = function (fastify: FastifyInstance, opts: any, next: Function): void {
  fastify.decorate('someSupport', () => 'hugs')
  next()
}
 

Test

You can see basic test example codes here: https://github.com/fastrojs/fastro-template/tree/test

Install jest & ts-jest:

npm i jest @types/jest ts-jest -D

Create jest config:

// file jest.config.js
module.exports = {
  modulePathIgnorePatterns: ['<rootDir>/dist/'],
  globals: {
    'ts-jest': {
      tsConfig: 'tsconfig.json'
    }
  },
  preset: 'ts-jest',
  testEnvironment: 'node',
}
 

Make folder __test__ and create *.spec.ts file in it.

// hello.controller.spec.ts
import { FastifyInstance } from 'fastify'
import { createServer } from 'fastro'
 
let server: FastifyInstance
 
beforeAll(async () => {
  server = await createServer()
})
 
afterAll(() => {
  server.close()
})
 
describe('simple test', () => {
  test('/', async done => {
    const result = await server.inject({
      url: '/',
      method: 'GET'
    })
    // console.log(result.payload)
    expect(result.payload).toContain('hello')
    done()
  })
})
 

Run test:

npm test

Check __test__ folder for another test example.

Fastro Fullstack

You can check react fastro fullstack web template here: https://github.com/fastrojs/fastro-web

Acknowledgements

This project is powered by:

License

  • Licensed under MIT.

Package Sidebar

Install

npm i fastro

Weekly Downloads

1

Version

0.3.11

License

MIT

Unpacked Size

68.4 kB

Total Files

17

Last publish

Collaborators

  • ynwd