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

Node.jsでも綺麗なコードでWebAPIを作る(routing-controllers)

$
0
0

はじめに

Node.jsでWebAPIを作ると、その自由度の高さからコードが綺麗に書けないことが多いと思います。
そんなときにはrouting-controllersを使うのがおすすめです。
今回はrouting-controllersを使ったモダンなWebAPIの書き方を紹介します。

routing-controllersとは

https://github.com/typestack/routing-controllers

Allows to create controller classes with methods as actions that handle requests. You can use routing-controllers with express.js or koa.js.

いわゆるMVCのコントローラーをTypescriptのクラスベースで書くことができるライブラリで、express.jsやkoa.jsなどのフレームワークに適応しています。
クラスベースであることにより、構造的かつ綺麗なコードを書くことができます。
例として、以下のようにクラスのメソッドをコントローラーのハンドラーとして書くことができます。

sampleController.ts
import{Controller,Param,Body,Get,Post,Put,Delete}from"routing-controllers";@Controller()exportclassUserController{@Get("/users")getAll(){return"This action returns all users";}@Get("/users/:id")getOne(@Param("id")id:number){return"This action returns user #"+id;}@Post("/users")post(@Body()user:any){return"Saving user...";}@Put("/users/:id")put(@Param("id")id:number,@Body()user:any){return"Updating a user...";}@Delete("/users/:id")remove(@Param("id")id:number){return"Removing user...";}}

Typescriptに馴染みのない人には見慣れない構文があるかと思います。
@Get("/users")などはTypescriptのデコレーターという機能になります。
https://www.typescriptlang.org/docs/handbook/decorators.html
デコレータとはクラスの宣言などに(ここではメソッドに対して)アタッチできる特別な宣言です。

さっそく作ってみる

こちらに詳細なコードが載っています。
https://github.com/tonio0720/modernApiInTypescript

パッケージインストール

npm init # 初期化
npm i -S express reflect-metadata routing-controllers class-transformer class-validator
npm i -D @types/express ts-node

TSCONFIGの設定

tsc --init# tsconfig.jsonができればOK

以下の3つの設定を変更

tsconfig.json
{"compilerOptions":{~"strictPropertyInitialization":false,/*ExperimentalOptions*/"experimentalDecorators":true,"emitDecoratorMetadata":true~}}

コントローラーを書いてみる

ポケモンのデータを返す処理を書いてみました。

controllers/PokemonController.ts
import{JsonController,Get,QueryParams,Param,}from'routing-controllers';import{IsInt,IsOptional}from'class-validator';interfacePokemon{id:number;name:string;type1:string;type2:string;}constpokemons:Pokemon[]=[{id:1,name:'フシギダネ',type1:'くさ',type2:'どく'},{id:2,name:'フシギソウ',type1:'くさ',type2:'どく'},{id:3,name:'フシギバナ',type1:'くさ',type2:'どく'}]classGetPokemonQuery{@IsInt()@IsOptional()limit?:number;@IsInt()@IsOptional()offset?:number;}@JsonController()exportclassPokemonController{@Get('/pokemons')asyncpokemons(@QueryParams()query:GetPokemonQuery):Promise<Pokemon[]>{const{offset=0,limit=100}=query;returnpokemons.slice(offset,offset+limit);}@Get('/pokemon/:id')asyncpokemon(@Param('id')id:number):Promise<Pokemon>{constpokemon=pokemons.find((pokemon)=>pokemon.id===id);if(pokemon){returnpokemon;}thrownewError('no pokemon');}}

解説

  • クラスに対して@JsonControllerデコレーターを付けることでレスポンスをJSONとして扱うことを意味します。
  • リクエストのメソッドがGETのときは@Get、POSTのときは@Postという風にデコレーターを付与します。
  • クエリパラメータを受け取るときは、@QueryParamsを使います。
    • クエリパラメータもクラスベースで書くことができます。
    • @IsIntを付けることによって、バリデートやサニタイズを自動でしてくれます。
    • 他にも@IsBoolean@IsPositiveなどが使えます。
    • 詳しくはclass-validatorのドキュメントをご参照ください。
  • URL内のパラメータを受け取るときは@Paramを使います。

app.ts

app.tsがメインファイルになります。
Expressサーバーを起動し、ポート3000番でリッスンしています。
先ほど書いたコントローラーをインポートします。
比較的シンプルに書くことができます。

app.ts
import'reflect-metadata';importexpressfrom'express';importbodyParserfrom'body-parser';import{useExpressServer}from'routing-controllers';import{PokemonController}from'./controllers';constPORT=3000;asyncfunctionbootstrap(){constapp=express();app.use(bodyParser.json());useExpressServer(app,{controllers:[PokemonController]});app.listen(PORT,()=>{console.log(`Express server listening on port ${PORT}`);});}bootstrap();

実行してみる

ts-node app.ts
# 「Express server listening on port 3000」となれば成功

ブラウザなどから、
http://localhost:3000/pokemons?limit=1
にアクセスしてレスポンスが返れば成功です。

Express単体との比較

非同期処理

Expressでハンドラーを書く際、非同期処理を即時間数でラップするなど面倒な書き方になってしまいます。

expressの場合
constexpress=require('express');constrouter=express.Router();router.get('/users',(req,res,next)=>{(async()=>{constusers=awaitgetUsers();res.status(200).json(users);})().catch(next);});

一方でrouting-controllersでは、Promise型をそのまま返すだけでOKです。

rcの場合
import{JsonController,Get,}from'routing-controllers';@JsonController()exportclassUserController{@Get('/users')asyncusers():Promise<User[]>{returngetUsers();}}

バリデーション

Expressでバリデーションをするときは、express-validatorを使います。

express-validatorの場合
const{body,validationResult}=require('express-validator');app.post('/user',[body('username').isEmail(),body('password').isLength({min:6})],(req,res)=>{consterrors=validationResult(req);if(!errors.isEmpty()){returnres.status(400).json({errors:errors.array()});}// ...});

routing-controllersでは、デコレーターで書くことができます。

rcの場合
exportclassUser{@IsEmail()email:string;@MinLength(6)password:string;}@JsonController()exportclassUserController{@Post('/login')asynclogin(@Body()user:User){// ...}}

おわりに

いかがでしたでしょうか?
routing-controllersを使うことで、バリデーションやサニタイズもしつつ綺麗にコードを書くことができました。
routing-controllersはexpress.js以外のフレームワークにも適用できるので是非お試しください。


Viewing all articles
Browse latest Browse all 8835

Trending Articles