[Deno] oak_decoratorsのデコレーターでNestJSっぽくAPIサーバーを作る。

# はじめに

Denoのモジュールである[oak](https://deno.land/x/oak)は、Koaにインスパイアされたwebフレームワークです。
このoakにてデコレーターを利用してAPIを実装をするためのモジュール、[oak_decorators](https://github.com/biga816/oak-decorators)を作成して公開したので、これを利用してNestJSっぽくAPIサーバーを作成してみようと思います。

# 実装方法

## 1. Controllerの作成
`@Controller()`デコレーターでリクエストをハンドルするクラスを作成します。
メソッドには`@Get()`や`@Post()`などのデコレーターで各種HTTPメソッドに応じた処理を追加できます。

```typescript:./app.controller.ts
import { Controller, Get, Headers } from "https://deno.land/x/oak_decorators/mod.ts";

@Controller()
export class AppController {
  @Get()
  get(@Headers("user-agent") userAgent: string) {
    return { status: "ok", userAgent };
  }
}
```

`get`メソッドの`@Headers("user-agent")`にてhttpヘッダーよりUser Agentを取得していますが、他にも`@Query（）`や`@Param()`など様々なデコレーターでリクエストの情報を取得することができます。

## 2. Root moduleの作成

作成したControllerを登録するためのRoot moduleを作成します。
routePrefixには、共通のurlとしてrouteにセットされる値を定義できます。
ここでは`v1`とします。

```typescript:./app.module.ts
import { Module } from "https://deno.land/x/oak_decorators/mod.ts";
import { AppController } from "./app.controller.ts";

@Module({
  controllers: [AppController],
  routePrefix: "v1",
})
export class AppModule {}
```

## 3. Entry fileの更新
アプリケーショのエントリーファイルとなる`main.ts`に、assignModuleを用いてRoute moduleを登録します。

```typescript:./main.ts
import { Application } from "https://deno.land/x/oak/mod.ts";
import { assignModule } from "https://deno.land/x/oak_decorators/mod.ts";
import { AppModule } from "./app.module.ts";

const app = new Application();
app.use(assignModule(AppModule));

await app.listen({ port: 8000 });
```

ここまでで、一旦起動できる様になります。
実行すると下記の様なログが出力されます。

```bash
$ deno run --allow-net --allow-write --import-map=test/importMap.json -c tsconfig.json test/demo/main.ts
Check file:///Users/xxx/main.ts
Mapped: [GET]/v1
```

## 4. Child moduleの作成 & DI実装
Moduleをネストして登録するので、Child moduleを作成します。
ここではDIも使用するので、まずはServiceを定義します。

```typescript:./sample/sample.service.ts
import { Injectable } from "https://deno.land/x/oak_decorators/mod.ts";

@Injectable()
export class SampleService {
  get() {
    return { status: "ok" };
  }
}
```

次にこのServiceをDIして利用するControllerを定義します。

```typescript:./sample/sample.controller.ts
import { Controller, Get } from "https://deno.land/x/oak_decorators/mod.ts";
import { SampleService } from "./sample.service.ts";

@Controller('sample')
export class SampleController {
  constructor(private readonly sampleService: SampleService) {}

  @Get()
  get() {
    return this.sampleService.get();
  }
}
```

最後にmoduleを定義し、Route moduleでインポートします。

```typescript:./sample/sample.module.ts
import { Module } from "https://deno.land/x/oak_decorators/mod.ts";
import { SampleController } from "./sample.controller.ts";
import { SampleService } from "./sample.service.ts";

@Module({
  controllers: [SampleController],
  providers: [SampleService],
})
export class SampleModule {}
```

```typescript:./app.module.ts
import { Module } from "https://deno.land/x/oak_decorators/mod.ts";
import { AppController } from "./app.controller.ts";
import { SampleModule } from "./sample/sample.module.ts";

@Module({
  modules: [SampleModule],
  controllers: [AppController],
  routePrefix: "v1",
})
export class AppModule {}
```

## 5. 実行

`main.ts`を実行してHTTPサーバーを立ち上げます。
実行すると下記の様なログが出力されます。

```bash
$ deno run --allow-net --allow-write --import-map=test/importMap.json -c tsconfig.json test/demo/main.ts
Check file:///Users/xxx/main.ts
Mapped: [GET]/v1
Mapped: [GET]/v1/sample
```


# まとめ
これで実装は完了です。
oak_decoratorsのREADMEに詳しい使い方は書かれているので、詳しくはそちらを参照してください。
https://github.com/biga816/oak-decorators