ES2017のスタイルでKoaJS 2のマイクロサービスを作成しています。 パートII:最小限のREST

コアv2



これは記事の続きであり、ES2017スタイルでKoaJS 2のマイクロサービスを作成しています。 パートI:そのような異なる非同期 。 エクスプレスを手放したいが、方法がわからない初心者開発者を喜ばせようとします。 多くのコードがあり、十分なテキストはありません-私は怠け者ですが、反応が良いです。



Koaは最小限であり、ルーターもフレームワーク自体には含まれていません。 ここで、プログラマーからの最小電圧を必要とするモジュールをすぐに装備します。



問題の声明



簡単な例:MySQLには製品(製品)のテーブルがあります。 私たちのタスクは、記述しなければならないRESTサービスを介して、このテーブルの製品を追加、削除、変更する機能を提供することです。



始めましょう



./config./appの 2つのフォルダーを作成します。 ./appフォルダーからbabelとアプリケーションを接続するサービスのルートフォルダーにあるファイルは1つだけです。

require('babel-core/register'); const app = require('./app');
      
      





babelの設定を.babelrcに移動しました



アプリケーションのメインファイルは次のようになります。

 import Koa from 'koa'; import config from 'config'; import err from './middleware/error'; import {routes, allowedMethods} from './middleware/routes'; const app = new Koa(); app.use(err); app.use(routes()); app.use(allowedMethods()); app.listen(config.server.port, function () { console.log('%s listening at port %d', config.app.name, config.server.port); });
      
      







構成設定を保存するには、構成モジュールをお勧めします。 これにより、構成を1つのインスタンスまで簡単に整理できます。



./middlewareフォルダーにカスタムミドルウェアを作成します。



エラーに関する情報をJSON形式で提供するには、。/ middleware / error.jsを記述します

 export default async (ctx, next) => { try { await next(); } catch (err) { // will only respond with JSON ctx.status = err.statusCode || err.status || 500; ctx.body = { message: err.message }; } }
      
      







ルートはメインファイルに配置できますが、コードはより複雑に見えます。



サービスのバックボーンの準備ができました。



ルートを書く



koa 2をサポートするものを含め、ルート用のモジュールが多数あります 。私はkoa-routerを好むので、このモジュールの長所を例で検討します。

 import Router from 'koa-router'; import product from '../models/product'; import convert from 'koa-convert'; import KoaBody from 'koa-body'; const router = new Router(), koaBody = convert(KoaBody()); router .get('/product', async (ctx, next) => { ctx.body = await product.getAll() }) .get('/product/:id', async (ctx, next) => { let result = await product.get(ctx.params.id); if (result) { ctx.body = result } else { ctx.status = 204 } }) .post('/product', koaBody, async (ctx, next) => { ctx.status = 201; ctx.body = await product.create(ctx.request.body) }) .put('/product/:id', koaBody, async (ctx, next) => { ctx.status = 204; await product.update(ctx.params.id, ctx.request.body); }) .delete('/product/:id', async (ctx, next) => { ctx.status = 204; await product.delete(ctx.params.id); }); export function routes () { return router.routes() } export function allowedMethods () { return router.allowedMethods() }
      
      







UPD: ラムキンの発言によると 、私はエクスポートの最後の行を再編集しました-簡潔でしたが、風水ではありませんでした。 それで、それに応じて、。/ app / index.jsのインポートを修正しました



少し後で説明する製品モデルと、ポストリクエストの本文をオブジェクトに解析するkoa-bodyモジュールを接続します。 koa-convertを使用して、 koa-bodyをkoa 2のミドルウェアに変換します。



最も単純な場合、ルートは予測可能に見えます:

  .get('/product', async (ctx, next) => { ctx.body = await product.getAll() })
      
      





/ productアドレスでのgetリクエストの場合、モデルからすべてのレコードを取得し、それらをctx.bodyに渡してJSONクライアントに転送します。 必要なすべてのヘッダーkoaはそれ自体をインストールします。



しかし、POST要求はより興味深い方法で処理されます-ルートでは、2番目の引数から開始して、ミドルウェアを無制限に追加できます。 私たちの場合、これにより、このデータが次のミドルウェアによって処理される前に、koa-bodyを接続してリクエスト本文を取得する機会が与えられます。



ルートについてはこれで終わりです。まだ質問がある場合は、コメントで質問してください。



どのようなエラーがどのような場合にRESTから返されるかについては、「それは可能です。そのように可能です」という明確な説明はどこにもありません。 私は自分にとって都合が良いようにコーディングしたので、この部分についてコメントがあれば、喜んで検討します。



「製品」モデルを作成する



MySQLをデータベースとして選択したのは、単に「手元に」あったからです。

 import query from 'mysql-query-promise'; import config from 'config'; const tableName = config.product.tableName; const crud = { getAll: async () => { return query(`SELECT * from ${tableName}`); }, get: async (id) => { let products = await query(`SELECT * FROM ${tableName} WHERE id=?`,[Number(id)]); return products[0]; }, create: async function ({ id, name, price = 0, currency = 'UAH' }) { let product = {name: String(name), price: Number(price), currency: String(currency)}; if (id > 0) product.id = Number(id); let result = await query(`INSERT INTO ${tableName} SET ? ON DUPLICATE KEY UPDATE ?`,[product,product]); if (result.insertId) id = result.insertId; return crud.get(id); }, update: async (id, product)=> { if (typeof product === 'object') { let uProduct = {}; if (product.hasOwnProperty('name')) uProduct.name = String(product.name); if (product.hasOwnProperty('price')) uProduct.price = Number(product.price); if (product.hasOwnProperty('currency')) uProduct.currency = String(product.currency); let result = await query(`UPDATE ${tableName} SET ? WHERE id=?`,[uProduct, Number(id)]); return result.affectedRows; } }, delete: async (id) => { let result = await query(`DELETE FROM ${tableName} WHERE id=?`,[Number(id)]); return result.affectedRows; } }; export default crud;
      
      





mysql-query-promiseモジュールは当社で作成されています。これはエンジニアリングの傑作であるとは言えません。これは構成モジュールに厳密に関連付けられているためです。 しかし、私たちの場合、それは適用可能です。

単純なメソッドget、delete、put、コメントは意味がありません。コードはそれ自体を表していますが、POSTについて少し説明します。 私は矢印関数を使用しませんでした ES6 / ES2015標準からパラメーターを渡すための高度な機能を適用しました。 例と同じように、シーケンスの順序を気にせずにパラメータの転送を整理できます。さらに、未定義のパラメータに「デフォルト値」を設定できます。



テスト中



すべて、サービスの準備ができました。コマンドラインから開始してテストできます。

 curl -XPOST "127.0.0.1:3001/product" -d '{"id":1,"name":"Test","price":1}' -H 'Content-Type: application/json' curl -XGET "127.0.0.1:3001/product" curl -XPUT "127.0.0.1:3001/product/1" -d '{"name":"Test1"}' -H 'Content-Type: application/json' curl -XGET "127.0.0.1:3001/product/1" curl -XDELETE "127.0.0.1:3001/product/1"
      
      





すべてが機能します。コードをさらにシンプルで理解しやすくするための提案を歓迎します。



結論の代わりに



Express、Resify、およびその他の確立されたフレームワークのほとんどのファンは、ファンのコアが形成されつつあり、バージョンごとに大幅に変化しているフレームワークにすべてとコードをドロップする準備ができていることを理解しています。 koaによると、これまでに小さなミドルウェアが作成されており、ロシア語のドキュメントはほとんどありません。 しかし、それにも関わらず、ハブに関するランダムなコメントからコアについて学んだ2年前に選択しました



記事の最初の部分へのコメントで、fawerはasync / awaitが既にbabelなしのchrome 52で利用可能である書いています 。 近い将来、見逃さないでください!



便利なリンク






All Articles