Server Side TypeScriptでdecoratorを使い倒す
サーバーサイドを TypeScript で書くなら decorator を活用するとシンプルに書ける。 decorator はむやみに使うと可読性・保守性が下がるけど、汎用性の高いケースに活用すればビジネスロジックに集中した快適コーディングができる、と思う。
decorator ベースの便利なライブラリを3つ紹介する。
- ルーディング: routing-controllers
- ORM: typeorm
- DI(依存性注入): typedi
これら3つを組み合わせたサーバーアプリの例を GitHub に置いた。
サンプルコード
GitHub リポジトリを見ていただくのが早いけど、記事にもコピペしておく。
データベースのモデル定義はこう。
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm' @Entity() export default class Post { @PrimaryGeneratedColumn() id: number @Column() title: string @Column('text') text: string }
モデルを使用するレポジトリ(モデルとコントローラーの中間層)はこう。
import { Service } from 'typedi' import { InjectRepository } from 'typeorm-typedi-extensions' import { Repository } from 'typeorm' import Post from '../model/Post' @Service() export class PostRepository { @InjectRepository(Post) private repository: Repository<Post> findAll () { return this.repository.find() } findOne (id: number) { return this.repository.findOneById(id) } save (post: Post) { return this.repository.save(post) } remove (id: number) { return this.repository.removeById(id) } }
コントローラーはこう。
import { JsonController, Get, Post as HttpPost, Param, Delete, Body, OnUndefined } from 'routing-controllers' import { Service, Inject } from 'typedi' import { PostRepository } from '../repository/PostRepository' import Post from '../model/Post' @Service() @JsonController() export class PostController { @Inject() private postRepository: PostRepository @Get('/posts') all () { return this.postRepository.findAll() } @Get('/posts/:id') @OnUndefined(404) one (@Param('id') id: number) { return this.postRepository.findOne(id) } @HttpPost('/posts') post (@Body() post: Post) { return this.postRepository.save(post) } @Delete('/posts/:id') @OnUndefined(200) delete (@Param('id') id: number) { return this.postRepository.remove(id) } }
エントリーポイントはこう。
import 'reflect-metadata' import { createKoaServer, useContainer as routingUseContainer } from 'routing-controllers' import { createConnection, useContainer as dbUseContainer } from 'typeorm' import { Container } from 'typedi' import { PostController } from './controllers/PostController' import Post from './model/Post' routingUseContainer(Container) dbUseContainer(Container) main().catch(console.error) async function main () { await createConnection({ type: 'sqlite', database: 'test.database', entities: [ Post, ], synchronize: true, logging: true, }) const app = createKoaServer({ controllers: [ PostController, ], }) app.listen(3000) console.log('Server is up and running at port 3000') }