FastifyをTypeScriptで利用する方法

ExpressやHapiと比べて、Fastifyはベンチマークがものすごいという情報があったので、試しにFastifyでAPIサーバーを作成してみようと思います。
https://www.fastify.io/benchmarks/

併せて、やはりNode.jsでも型やinterfaceなどを用いてきっちりとしたAPIサーバーを作成したいので、FastifyをTypeScriptで利用しようと思います。

# 1.プロジェクトのベースを作る。
まずはプロジェクトのベースを作ります。
touchしたファイルの中身は後ほど記述します。

```:command
npm init
mkdir src

touch src/server.ts
touch tsconfig.json
touch gulpfile.js
```

# 2.各種ライブラリをインストールする
fastifyとcorsをインストールします。
gulpを用いてtypescriptをコンパイルするので、併せてgulp関連ライブラリもインストールします。

```
npm install --save fastify cors
npm install --save-dev gulp gulp-typescript rimraf typescript
```
# 3.JSONを返却する処理を作成する。
jsonを返却するだけのシンプルな処理をserver.tsに記述します。

```ts:src/server.ts
import * as fastify from 'fastify'
import * as cors from 'cors'

const server = fastify();

class REST {
  constructor() {
    this.init();
  }

  init() {
    server.use(cors());
    server.get('/', this.getHelloHandler);
    
    server.listen(3000, err => {
      if (err) throw err;
      console.log(`server listening on ${server.server.address().port}`);
    });
  }

  getHelloHandler (req: any, reply: any) {
    console.log("get success:)");
    reply.header('Content-Type', 'application/json').code(200);
    reply.send({ hello: 'world' });
  }
}

new REST();
```

# 4.TypeScriptをコンパイルする準備をする。
tsconfig.jsonとgulpfile.jsを修正し、それぞれコンパイルの設定をします。

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "dist"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

```js:gulpfile.js
const gulp = require('gulp');
const ts = require('gulp-typescript');
const rimraf = require('rimraf');

const tsProject = ts.createProject('tsconfig.json');
const JSON_FILES = ['src/*.json', 'src/**/*.json'];

gulp.task('clean', function (cb) {
  rimraf('./dist', cb);
});

gulp.task('scripts', ['clean'], () => {
  const tsResult = tsProject.src()
  .pipe(tsProject());
  return tsResult.js.pipe(gulp.dest('dist'));
});

gulp.task('watch', ['scripts'], () => {
  gulp.watch('src/**/*.ts', ['scripts']);
});

gulp.task('assets', function() {
  return gulp.src(JSON_FILES)
  .pipe(gulp.dest('dist'));
});

gulp.task('serve', ['watch', 'assets']);
gulp.task('build', ['scripts']);
```

# 5.TypeScriptをコンパイルする。
まずはpackage.jsonのscriptsを更新します。
単発でコンパイルする場合は`npm run build`、tsファイルを監視し、tsファイルが更新される度にコンパイルするに場合には`npm run serve`を実行する形式にます。

```json:package.json
{
    :
  "scripts": {
    "start": "node ./dist/server.js", 
    "serve": "gulp serve",
    "build": "gulp build"
  },
    :
}
```

では、コンパイルします。
<img width="703" alt="fastify-sample_—_-bash_—_99×15.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/dee14898-a1af-ec46-f0f7-1ccb15133ac2.png">

dist配下にserver.jsが出力されていれば成功です。
<img width="957" alt="server_js_—_fastify-sample.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/035f662e-ec3a-5d59-95c7-5896fe060096.png">

# 6.サーバーを起動する。
`npm run start`でサーバーを起動します。
ブラウザから`http://localhost:3000`にアクセスし、hello worldが表示されていればAPIサーバー作成完了です。

<img width="728" alt="localhost_3000.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/1d04904f-06f5-c171-5fa2-5b564b9058e8.png">

<img width="703" alt="fastify-sample.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/b502f0b0-e51c-d1a4-12ce-a29c67eeb584.png">


# 7.おまけ：Visual Studio Codeでデバッグする
このままだとコンパイル後のjsファイルをデバッグすることになってしまうので、tsファイルに対してデバッグできるようにします。

## 1) gulpの設定の変更。
`npm install --save-dev gulp-sourcemaps`を実行し、gulpfile.jsのscriptsタスクを以下のように修正します。

```js:gulpfile.js
const sourcemaps = require('gulp-sourcemaps');

   :

gulp.task('scripts', ['clean'], () => {
  return tsProject.src()
    .pipe(sourcemaps.init())
    .pipe(tsProject())
    .js
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist'));
});

```

## 2) Visual Studio Codeのデバッグ設定の追加
Visual Studio Codeで作成中のプロジェクトを開き、F5を押下し、「環境の選択」でNode.jsを選択します。

<img width="876" alt="vscode-debug2.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/75938427-bb12-31cb-9000-82c05247b9ce.png">


Node.jsを選択すると、エラーが表示されるので、「launch.jsonを開く」を押下し、もう一度「環境の選択」でNode.jsを選択します。

<img width="876" alt="vscode-debug2.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/6f479fa0-e039-d290-d656-bd74ce411c62.png">

launch.jsonが開かれるので、もう一度F5を押下します。
するとまたエラーが表示されるので今度は「タスクの構成」を押下します。

<img width="876" alt="vscode-debug3.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/75ebbbba-a2f1-62fe-c2f6-266c3340e17e.png">

「構成するタスクを選択」で`npm: build`を選択します。

<img width="876" alt="vscode-debug4.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/b0ade63c-a7d2-7dac-9e84-8938a1426546.png">

デバッグに必要なlaunch.jsonとtasks.jsonのベースができたので、以下のようにそれぞれ更新していきます。

```json:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "program": "${workspaceFolder}/dist/server.js", // <- ここを修正
      "preLaunchTask": "build", // <- ここを修正
      "sourceMaps": true // <- ここを追加
    }
  ]
}
```
```json:tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build", // <- ここを追加
      "type": "npm",
      "script": "build",
      "problemMatcher": []
    }
  ]
}
```

以上でデバッグの設定は終了です。
F5を押してみてください。
コンパイルが実行され、サーバーが起動すれば成功です。
<img width="876" alt="vscode-debug5.png" src="https://qiita-image-store.s3.amazonaws.com/0/92492/924bb8d2-f7fe-508e-13ed-1eca67d2c941.png">


---
<参考リンク>
https://qiita.com/yonboroid/items/242ba4f4f23d8f33a472
https://github.com/fastify/fastify/tree/master/examples