LoopBackにやらせてください:あなたが夢見てきたノードAPIフレームワークのウォークスルー

公開: 2022-03-11

言うまでもなく、アプリケーション開発でNode.jsの人気が高まっています。 eBayは2011年から本番ノードAPIサービスを実行しています。PayPalはノードでフロントエンドを積極的に再構築しています。 ウォルマートのモバイルサイトは、トラフィックに関して最大​​のノードアプリケーションになりました。 2014年の感謝祭の週末に、ウォルマートサーバーは15億のリクエストを処理し、その70%はモバイル経由で配信され、Node.jsを利用していました。 開発側では、ノードパッケージマネージャー(npm)は急速に成長を続けており、最近ではホストされているモジュールが150,000を超えています。

RubyにはRailsがあり、PythonにはDjangoがありますが、Nodeの主要なアプリケーション開発フレームワークはまだ確立されていません。 しかし、強力な競争相手が勢いを増しています。カリフォルニア州サンマテオのStrongLoop社によって構築されたオープンソースAPIフレームワークであるLoopBackです。 StrongLoopは、現存する最も人気のあるNodeフレームワークの1つであるExpressの長年のメンテナーは言うまでもなく、最新のNodeバージョンへの重要な貢献者です。

すべてを実践に移し、サンプルアプリケーションを構築することで、LoopBackとその機能を詳しく見ていきましょう。

LoopBackとは何ですか?Nodeでどのように機能しますか?

LoopBackは、APIを作成し、それらをバックエンドデータソースに接続するためのフレームワークです。 Expressの上に構築されており、データモデル定義を取得し、任意のクライアントから呼び出すことができる完全に機能するエンドツーエンドのRESTAPIを簡単に生成できます。

LoopBackには、組み込みのクライアントであるAPIExplorerが付属しています。 これを使用すると、作業の結果が見やすくなり、この例ではAPI自体の構築に焦点を当てることができます。

もちろん、フォローするには、マシンにNodeをインストールする必要があります。 ここで入手してください。 npmが付属しているので、必要なパッケージを簡単にインストールできます。 始めましょう。

スケルトンを作成する

私たちのアプリケーションは、贈り物を必要としている可能性のある人に、贈り物や不要になったものを寄付したい人を管理します。 したがって、ユーザーはドナーとレシーバーになります。 寄付者は新しいギフトを作成し、ギフトのリストを見ることができます。 受信者は、すべてのユーザーからのギフトのリストを表示でき、未請求のギフトを請求できます。 もちろん、同じエンティティ(ユーザー)上でドナーとレシーバーを別々の役割として構築することもできますが、LoopBackでリレーションを構築する方法を確認できるように、それらを分離してみましょう。 この画期的なアプリケーションの名前はGivesomebodyになります。

npmを介してStrongLoopコマンドラインツールをインストールします。

 $ npm install -g strongloop

次に、LoopBackのアプリケーションジェネレーターを実行します。

 $ slc loopback _-----_ | | .--------------------------. |--(o)--| | Let's create a LoopBack | `--------- | application! | ( _U`_ ) '--------------------------' /___A___\ | ~ | __'.___.'__ ` |° Y ` ? What's the name of your application? Givesomebody

モデルを追加しましょう。 私たちの最初のモデルはギフトと呼ばれます。 LoopBackは、データソースと基本クラスを要求します。 データソースをまだ設定していないので、 db (memory)を置くことができます。 基本クラスは自動生成されたモデルクラスであり、この場合はPersistedModelを使用します。これは、通常のCRUDメソッドがすべてすでに含まれているためです。 次に、LoopBackは、RESTを介してモデルを公開する必要があるかどうか(yes)、およびRESTサービスの名前を尋ねます。 ここでEnterキーを押すと、デフォルトが使用されます。これは、モデル名(この場合はgifts )の複数形です。

 $ slc loopback:model ? Enter the model name: Gift ? Select the data-source to attach Gift to: (Use arrow keys) ❯ db (memory) ? Select model's base class: (Use arrow keys) Model ❯ PersistedModel ? Expose Gift via the REST API? (Y/n) Yes ? Custom plural form (used to build REST URL):

最後に、プロパティの名前、それらのデータ型、および必須/非必須フラグを示します。 ギフトにはnamedescriptionのプロパティがあります。

 Let's add some Gift properties now. Enter an empty property name when done. ? Property name: name invoke loopback:property ? Property type: (Use arrow keys) ❯ string ? Required? (y/N)Yes

空のプロパティ名を入力して、プロパティの定義が完了したことを示します。

モデルジェネレーターは、アプリケーションのcommon/modelsでモデルを定義する2つのファイルgift.jsongift.jsを作成します。 JSONファイルは、エンティティに関するすべてのメタデータ(プロパティ、リレーション、検証、ロール、メソッド名)を指定します。 JavaScriptファイルは、追加の動作を定義し、特定の操作(作成、更新、削除など)の前または後に呼び出されるリモートフックを指定するために使用されます。

他の2つのモデルエンティティは、ドナーモデルとレシーバーモデルになります。 同じプロセスを使用して作成できますが、今回はUserを基本クラスとして配置します。 それは私達にusernamepassword 、箱から出してemailのようないくつかのプロパティを提供します。 たとえば、名前と国だけを追加して、完全なエンティティを作成できます。 受信者には、配送先住所も追加します。

プロジェクト構造

生成されたプロジェクト構造を見てみましょう。

プロジェクト構造

3つの主要なディレクトリは次のとおりです。- /server server–ノードアプリケーションスクリプトと構成ファイルが含まれています。 - /client – .js、.html、.css、およびその他すべての静的ファイルが含まれています。 - /common –このフォルダーはサーバーとクライアントの両方に共通です。 モデルファイルはここにあります。

LoopBackのドキュメントから抜粋した、各ディレクトリの内容の詳細な内訳は次のとおりです。

ファイルまたはディレクトリ説明コードでアクセスする方法
トップレベルのアプリケーションディレクトリ
package.json 標準のnpmパッケージ仕様。 package.jsonを参照してください該当なし
/serverディレクトリ-ノードアプリケーションファイル
server.js メインアプリケーションプログラムファイル。 該当なし
config.json アプリケーションの設定。 config.jsonを参照してください。 app.get('setting-name')
datasources.json データソース構成ファイル。 datasources.jsonを参照してください。 例については、「新しいデータソースの作成」を参照してください app.datasources['datasource-name']
model-config.json モデル構成ファイル。 model-config.jsonを参照してください。 詳細については、を参照してください。 モデルをデータソースに接続します 該当なし
middleware.json ミドルウェア定義ファイル。 詳細については、ミドルウェアの定義を参照してください。 該当なし
/bootディレクトリ初期化とセットアップを実行するスクリプトを追加します。 ブートスクリプトを参照してください。 スクリプトはアルファベット順に自動的に実行されます。
/clientディレクトリ-クライアントアプリケーションファイル
README.md LoopBackジェネレーターは、マークダウン形式で空のREADMEファイルを作成します。 該当なし
他のHTML、CSS、クライアントJavaScriptファイルを追加します。
/commondirectory-共有アプリケーションファイル
/modelsディレクトリカスタムモデルファイル:
  • モデル定義JSONファイル。慣例によりmodel-name .jsonます。 たとえば、 customer.json
  • model-name .js慣例によるカスタムモデルスクリプト; たとえば、 customer.jsです。
詳細については、モデル定義JSONファイルおよびモデルのカスタマイズを参照してください。
ノード:
myModel = app.models.myModelName

関係を構築する

この例では、モデル化する重要な関係がいくつかあります。 ドナーは多くのギフトを寄付することができます。これにより、ドナーは多くのギフトを持っているという関係になります。 受信者は多くのギフトを受け取ることもできるので、受信者には多くのギフトがあるという関係もあります。 一方、 GiftはDonorに属し、Receiverがそれを受け入れることを選択した場合はReceiverに属することもできます。 これをLoopBackの言語に入れましょう。

 $ slc loopback:relation ? Select the model to create the relationship from: Donor ? Relation type: has many ? Choose a model to create a relationship with: Gift ? Enter the property name for the relation: gifts ? Optionally enter a custom foreign key: ? Require a through model? No

スルーモデルがないことに注意してください。 ギフトへの参照を保持しているだけです。

上記のReceiverの手順を繰り返し、Giftのリレーションに属するものを2つ追加すると、バックエンド側でモデルの設計が完了します。 LoopBackは、モデルのJSONファイルを自動的に更新して、これらの単純なダイアログで行ったことを正確に表現します。

 // common/models/donor.json ... "relations": { "gifts": { "type": "hasMany", "model": "Gift", "foreignKey": "" } }, ...

データソースを追加する

次に、実際のデータソースをアタッチしてすべてのアプリケーションデータを保存する方法を見てみましょう。 この例では、MongoDBを使用しますが、LoopBackには、Oracle、MySQL、PostgreSQL、Redis、およびSQLServerに接続するためのモジュールがあります。

まず、コネクタを取り付けます。

 $ npm install --save loopback-connector-mongodb

次に、プロジェクトにデータソースを追加します。

 $ slc loopback:datasource ? Enter the data-source name: givesomebody ? Select the connector for givesomebody: MongoDB (supported by StrongLoop)

次のステップは、 server/datasources.jsonでデータソースを構成することです。 ローカルのMongoDBサーバーには次の構成を使用します。

 ... "givesomebody": { "name": "givesomebody", "connector": "mongodb", "host": "localhost", "port": 27017, "database": "givesomebody", "username": "", "password": "" } ...

最後に、 server/model-config.jsonを開き、データベースに保持するすべてのエンティティのdatasource"givesomebody"に変更します。

 { ... "User": { "dataSource": "givesomebody" }, "AccessToken": { "dataSource": "givesomebody", "public": false }, "ACL": { "dataSource": "givesomebody", "public": false }, "RoleMapping": { "dataSource": "givesomebody", "public": false }, "Role": { "dataSource": "givesomebody", "public": false }, "Gift": { "dataSource": "givesomebody", "public": true }, "Donor": { "dataSource": "givesomebody", "public": true }, "Receiver": { "dataSource": "givesomebody", "public": true } }

RESTAPIのテスト

これまでに作成したものを確認するときが来ました。 作成したばかりのサービスのクライアントとして使用できる、すばらしい組み込みツールであるAPIExplorerを使用します。 RESTAPI呼び出しをテストしてみましょう。

別のウィンドウで、次のコマンドでMongoDBを起動します。

 $ mongod

次のコマンドでアプリケーションを実行します。

 $ node .

ブラウザで、 http://localhost:3000/explorer/にアクセスします。 利用可能な操作のリストでエンティティを確認できます。 POST /Donors呼び出しで1つのドナーを追加してみてください。

API2のテスト

API3のテスト

APIExplorerは非常に直感的です。 公開されているメソッドのいずれかを選択すると、対応するモデルスキーマが右下隅に表示されます。 dataテキスト領域では、カスタムHTTPリクエストを書き込むことができます。 リクエストが入力されたら、「試してみる」ボタンをクリックすると、サーバーの応答が下に表示されます。

APIのテスト1

ユーザ認証

上記のように、LoopBackで事前に構築されたエンティティの1つは、Userクラスです。 ユーザーはログインメソッドとログアウトメソッドを所有しており、特定のユーザーのトークンを保持するAccessTokenエンティティにバインドできます。 実際、完全なユーザー認証システムは、箱から出してすぐに使用できます。 APIExplorerを介して/Donors/loginを呼び出そうとすると、次のような応答が返されます。

 { "id": "9Kvp4zc0rTrH7IMMeRGwTNc6IqNxpVfv7D17DEcHHsgcAf9Z36A3CnPpZJ1iGrMS", "ttl": 1209600, "created": "2015-05-26T01:24:41.561Z", "userId": "" }

idは、実際にはAccessTokenの値であり、データベースで自動的に生成および保持されます。 ここに表示されているように、アクセストークンを設定して、後続の各リクエストに使用することができます。

ユーザ認証

リモートメソッド

リモートメソッドはモデルの静的メソッドであり、カスタムRESTエンドポイントを介して公開されます。 リモートメソッドを使用して、LoopBackの標準モデルRESTAPIでは提供されていない操作を実行できます。

箱から出してすぐに使えるCRUDメソッドのほかに、必要な数のカスタムメソッドを追加できます。 それらはすべて[model].jsファイルに入れる必要があります。 この例では、ギフトモデルにリモートメソッドを追加して、ギフトがすでに予約されているかどうかを確認し、予約されていないすべてのギフトを一覧表示します。

まず、 reservedという名前のモデルにプロパティを追加しましょう。 これをgift.jsonのプロパティに追加するだけです:

 ... "reserved": { "type": "boolean" } ...

gift.jsのリモートメソッドは次のようになります。

 module.exports = function(Gift) { // method which lists all free gifts Gift.listFree = function(cb) { Gift.find({ fields: { reserved: false } }, cb); }; // expose the above method through the REST Gift.remoteMethod('listFree', { returns: { arg: 'gifts', type: 'array' }, http: { path: '/list-free', verb: 'get' } }); // method to return if the gift is free Gift.isFree = function(id, cb) { var response; Gift.find({ fields: { id: id } }, function(err, gift) { if (err) return cb(err); if (gift.reserved) response = 'Sorry, the gift is reserved'; else response = 'Great, this gift can be yours'; }); cb(null, response); }; // expose the method through REST Gift.remoteMethod('isFree', { accepts: { arg: 'id', type: 'number' }, returns: { arg: 'response', type: 'string' }, http: { path: '/free', verb: 'post' } }); };

そのため、特定のギフトが利用可能かどうかを確認するために、クライアントはPOSTリクエストを/api/Gifts/freeに送信して、問題のギフトのidを渡すことができます。

リモートフック

リモートメソッドの前後に何らかのメソッドを実行する必要がある場合があります。 2種類のリモートフックを定義できます。

  • beforeRemote()は、リモートメソッドの前に実行されます。
  • afterRemote()は、リモートメソッドの後に実行されます。

どちらの場合も、2つの引数を指定します。関数を「フック」するリモートメソッドに一致する文字列と、コールバック関数です。 リモートフックの利点の多くは、文字列にワイルドカードを含めることができるため、任意の一致する方法でトリガーされることです。

この例では、新しいドナーが作成されるたびにコンソールに情報を出力するフックを設定しましょう。 これを実現するために、 donor.jsに「作成前」フックを追加しましょう。

 module.exports = function(Donor) { Donor.beforeRemote('create', function(context, donor, next) { console.log('Saving new donor with name: ', context.req.body.name); next(); }); };

リクエストは指定されたcontextで呼び出され、ミドルウェアのnext()コールバック(以下で説明)は、フックの実行後に呼び出されます。

アクセス制御

LoopBackアプリケーションはモデルを介してデータにアクセスするため、データへのアクセスを制御することは、モデルに対する制限を定義することを意味します。 つまり、データの読み取りと書き込み、またはモデルのメソッドの実行を誰または何ができるかを指定します。 LoopBackアクセス制御は、アクセス制御リスト(ACL)によって決定されます。

ログインしていないドナーとレシーバーがギフトを表示できるようにしますが、ログインしているドナーのみがギフトを作成および削除できるようにします。

 $ slc loopback:acl

まず、すべてのエンドポイントへのすべてのアクセスを拒否しましょう。

 ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: All (match all types) ? Select the role: All users ? Select the permission to apply: Explicitly deny access

次に、誰もがギフトモデルから読むことができるようにします。

 $ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Read ? Select the role: All users ? Select the permission to apply: Explicitly grant access

次に、認証されたユーザーがギフトを作成できるようにします。

 $ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: A single method ? Enter the method name: create ? Select the role: Any authenticated user ? Select the permission to apply: Explicitly grant access

そして最後に、ギフトの所有者が変更を加えることができるようにしましょう。

 $ slc loopback:acl ? Select the model to apply the ACL entry to: Gift ? Select the ACL scope: All methods and properties ? Select the access type: Write ? Select the role: The user owning the object ? Select the permission to apply: Explicitly grant access

これで、 gift.jsonを確認すると、すべてが整っているはずです。

 "acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$everyone", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" } ],

ここでの重要な注意事項: $authenticatedは、システム内のすべてのユーザー(ドナーとレシーバーの両方)に対応する事前定義されたロールですが、ドナーが新しいギフトを作成できるようにするだけです。 したがって、カスタムロールが必要です。 Roleは箱から出してすぐに使えるもう1つのエンティティであるため、そのAPI呼び出しを利用してboot関数に$authenticatedDonorロールを作成し、 pricipalIdgift.jsonを変更するだけです。

新しいファイルserver/boot/script.jsを作成し、次のコードを追加する必要があります。

 Role.create({ name: 'authenticatedDonor' }, function(err, role) { if (err) return debug(err); })

RoleMappingエンティティは、ロールをユーザーにマップします。 RoleとRoleMappingの両方がRESTを介して公開されていることを確認してください。 server/model-config.jsonで、Roleエンティティの"public"trueに設定されていることを確認します。 次に、 donor.jsで、RoleMappingPOSTAPI呼び出しでuserIDroleIDをマップする「変更前」フックを記述できます。

ミドルウェア

ミドルウェアには、RESTエンドポイントに対して要求が行われたときに実行される関数が含まれています。 LoopBackはExpressに基づいているため、「ミドルウェアフェーズ」と呼ばれる1つの追加概念を備えたExpressミドルウェアを使用します。 フェーズは、ミドルウェアの関数が呼び出される順序を明確に定義するために使用されます。

LoopBackドキュメントで提供されている、事前定義されたフェーズのリストは次のとおりです。

  1. initial-ミドルウェアを実行できる最初のポイント。
  2. session-セッションオブジェクトを準備します。
  3. auth-認証と承認を処理します。
  4. parse-リクエスト本文を解析します。
  5. ルート-アプリケーションロジックを実装するHTTPルート。 Express API app.use、app.route、app.get(およびその他のHTTP動詞)を介して登録されたミドルウェアは、このフェーズの開始時に実行されます。 このフェーズは、loopback / server / Middleware/restやloopback-explorerなどのサブアプリにも使用します。
  6. files-静的アセットを提供します(リクエストはここでファイルシステムにヒットしています)。
  7. final-不明なURLのエラーとリクエストを処理します。

各フェーズには3つのサブフェーズがあります。 たとえば、初期フェーズのサブフェーズは次のとおりです。

  1. 初期:前
  2. イニシャル
  3. 初期:後

デフォルトのmiddleware.jsonを簡単に見てみましょう。

 { "initial:before": { "loopback#favicon": {} }, "initial": { "compression": {}, "cors": { "params": { "origin": true, "credentials": true, "maxAge": 86400 } } }, "session": { }, "auth": { }, "parse": { }, "routes": { }, "files": { }, "final": { "loopback#urlNotFound": {} }, "final:after": { "errorhandler": {} } }

初期段階では、 loopback.favicon()を呼び出します( loopback#faviconはその呼び出しのミドルウェアIDです)。 次に、サードパーティのnpmモジュールのcompressioncorsが呼び出されます(パラメーターの有無にかかわらず)。 最終フェーズでは、さらに2つの呼び出しがあります。 urlNotFoundはLoopBack呼び出しであり、 errorhandlerはサードパーティモジュールです。 この例は、多くの組み込み呼び出しが外部npmモジュールと同じように使用できることを示しているはずです。 そしてもちろん、いつでも独自のミドルウェアを作成し、このJSONファイルを介してそれらを呼び出すことができます。

loopback-boot

最後に、アプリケーションを初期化するboot()関数をエクスポートするモジュールについて説明します。 server/server.jsには、アプリケーションをブートストラップする次のコードがあります。

 boot(app, __dirname, function(err) { if (err) throw err; // start the server if `$ node server.js` if (require.main === module) app.start(); });

このスクリプトは、 server/bootフォルダーを検索し、そこで見つかったすべてのスクリプトをアルファベット順にロードします。 したがって、 server/bootでは、起動時に実行する必要のある任意のスクリプトを指定できます。 1つの例は、APIのテストに使用したクライアントであるAPIExplorerを実行するexplorer.jsです。

繰り返しブルースを手に入れましたか? そのノードAPIを最初から作成しないでください。 LoopBackにやらせてください!
つぶやき

結論

離れる前に、 slcコマンドラインツールの代わりに使用できるグラフィカルUIであるStrongLoopArcについて説明します。 また、ノードアプリケーションを構築、プロファイリング、および監視するためのツールも含まれています。 コマンドラインのファンではない人にとって、これは間違いなく試す価値があります。 ただし、StrongLoop Arcは廃止されようとしており、その機能はIBM API ConnectDeveloperToolkitに統合されています。

結論

一般的に言って、LoopBackは、箱から出してたくさんのものを手に入れているので、多くの手作業を節約できます。 これにより、アプリケーション固有の問題とビジネスロジックに集中できます。 アプリケーションがCRUD操作に基づいており、事前定義されたエンティティを操作している場合、多数の開発者があなたの前にそれを書いたときにユーザーの認証および承認インフラストラクチャを書き直すことにうんざりしている場合、または次のような優れたWebフレームワークのすべての利点を活用したい場合Expressしてから、LoopBackを使用してREST APIを構築すると、夢を実現できます。 簡単なことです!