Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9095

OvernightJSでNode+Express+TypeScriptなAPIサーバーを構築する

$
0
0

日本語情報が全く見当たらないExpressのTypeScript decoratorであるOvernightJSを使ってみたので、導入方法をざっくりまとめます。

Node/ExpressでTypeScriptを使う方法については、簡単に書きはしますが本筋ではないので詳細は他を当たってください。このあたりとか参考になります。

前提環境

  • Node 12.18.4
  • Express 4.17.1
  • TypeScript 4.0.3

開発環境でのTypeScriptの実行にはts-nodets-node-devを使います。ts-node-devはコード修正するとホットリロードしてくれるのでとても楽。

セットアップ

$ npm init
 プロジェクトの設定をよしなに
$ npm install express @overnightjs/core @overnightjs/logger body-parser cors http-status-codes
$ npm install -D typescript npm-run-all rimraf ts-node ts-node-dev @types/node@12 @types/express @types/cors
$ npx tsc --init

tsconfig.jsonが生成されるので、その中にあるtargetES2019に、esModuleInteropexperimentalDecoratorsemitDecoratorMetadataのコメントを外してtrueに設定します。

package.jsonのscripts部分はこんな感じにしました。ここはOvernightJS固有の要素は特になくTypeScript使う場合の一般的な設定内容です。開発環境ではts-nodeやts-node-devでTypeScriptファイルを直接実行、本番環境ではトランスパイルします。

package.json
"scripts":{"dev":"ts-node ./src/index.ts","dev:watch":"ts-node-dev ./src/index.ts","build":"npm-run-all clean tsc","clean":"rimraf dist/*","tsc":"tsc","start":"node ."},

余談ですがトランスパイルして実行するのとts-nodeで直接実行するのでパフォーマンス大差ないという話もあるので、要件がシビアでなければ本番もts-node運用でもいいかもしれません。

実装

大雑把にコントローラーとサーバーの二段構成です。型安全過激派なので徹底的に型で縛ります。

コントローラー

@Controllerというアノテーションを付けたクラスがコントローラーと認識されます。パラメーターはエンドポイントのパスで、サーバーがlocalhostの3000番ポートで動いているとすると以下の例ではhttp://localhost:3000/api/songsがエンドポイントになります。

各HTTPメソッドに対応するアノテーション@Get@Post@Put@Deleteが用意されていて、リクエストを受けるとリクエストメソッドに対応するアノテーションが付いた関数が呼び出されます。@Get(':id')のようにコロンで始まる文字列を渡した場合、req.params.idでパラメーターを受け取ることができます。http://localhost:3000/api/songs/1にGETリクエストを送るとreq.params.idは1になります。

controller/SongController.ts
import{Request,Response}from'express';import{Controller,Get,Post,Put,Delete}from'@overnightjs/core';import{Logger}from'@overnightjs/logger';import{StatusCodes}from'http-status-codes';interfaceISong{id:number;title:string;artist:string;}interfaceISongGetRequestParams{id:number;}interfaceISongUpdateRequestParams{id:number;}interfaceISongDeleteRequestParams{id:number;}@Controller('api/songs')exportclassSongController{@Get('')privateasyncgetAll(req:Request<void,ISong[],void,void>,res:Response<ISong[]>):Promise<Response<ISong[]>>{// ほんとはDBとかからデータ取得するconstsongs:ISong[]=[{id:1,title:'So Sweet',artist:'諏訪ななか'},{id:2,title:'クローバー',artist:'楠木ともり'}];returnres.status(StatusCodes.OK).json(songs);}@Get(':id')privateasyncget(req:Request<ISongGetRequestParams,ISong,void,void>,res:Response<ISong>):Promise<Response<ISong>>{Logger.Info(req.params,true);// ほんとはDBとかからデータ取得するconstsong:ISong={id:1,title:'So Sweet',artist:'諏訪ななか'};returnres.status(StatusCodes.OK).json(song);}@Post()privateasyncadd(req:Request<void,void,ISong,void>,res:Response<void>):Promise<response<void>>{Logger.Info(req.body,true);constsong:ISong=req.body;// ほんとはDBとかにデータ追加するreturnres.status(StatusCodes.OK).json();}@Put(':id')privateasyncupdate(req:Request<ISongUpdateRequestParams,void,ISong,void>,res:Response<void>):Promise<Response<void>>{Logger.Info(req.body,true);constsong:ISong=req.body;// ほんとはDBとか更新するreturnres.status(StatusCodes.OK).json();}@Delete(':id')privateasyncdelete(req:Request<ISongDeleteRequestParams,void,void,void>,res:Request<void>):Promise<Response<void>>{Logger.Info(req.params,true);constid:number=req.params.id;// ほんとはDBとかからデータ削除するreturnres.status(StatusCodes.OK).json();}}

サーバー

Expressに対してあれこれ設定して、コントローラーをインスタンス化してOvernightJSに登録して待ち受けを開始します。

ここではthis.appがExpressのインスタンスなので、普通にExpressを使うときと同様にミドルウェア登録したりとかいろいろできます。OvernightJSのServerがExpressをラップしてる感じですね。

SongServer.ts
import*asbodyParserfrom'body-parser';importcorsfrom'cors';import{Server}from'@overnightjs/core';import{Logger}from'@overnightjs/logger';import{SongController}from'./controller/SongController';classSongServerextendsServer{constructor(){super(process.env.NODE_ENV==='development');this.app.use(bodyParser.json());this.app.use(bodyParser.urlencoded({extended:true}));this.app.use(cors());this.setupControllers();}privatesetupControllers():void{constsongController:SongController=newSongController();super.addControllers([songController]);}publicstart(port:number):void{this.app.listen(port,():void=>{Logger.Imp(`server listening on port ${port}`);});}}exportdefaultSongServer;

index.ts

エントリーポイントですが、やることはServerを叩くだけです。

index.ts
importSongServerfrom'./SongServer';constserver:SongServer=newSongServer();server.start(3000);

起動

$ npm run dev:watch

これでホットリロードありでサーバーが起動します。

おわりに

TypeScriptでExpressのアプリケーション作る場合はexpress-generatorで生成したJavaScript一式をTypeScriptに書き直すという苦行も1つの手段としてありますが、OvernightJSのようなデコレーターを使うとちゃんとTypeScriptらしい書き方で、かつシンプルにREST APIを実装できます。凝ったことしなければRouterとか意識する必要すらないです(今回は触れてないですが、複雑な要件のためにOvernightJSはMiddlewareやRouterを扱う機能も持っています)。

Express+TypeScriptの環境でAPIサーバーをさくっと作りたい方、検討してみるとよいかと思います。


Viewing all articles
Browse latest Browse all 9095

Latest Images

Trending Articles