Webアプリケーション用のユニバーサルデータアクセスレイヤー。
通常、サーバーでは、APIまたはデータベースを直接呼び出してデータを取得します。ただし、クライアントでは、常に同じようにサービスを呼び出すことはできません(つまり、クロスドメインポリシー)。代わりに、サービスに転送されるサーバーにXHR/Fetchリクエストを行う必要があります。
両方の環境でコードを異なる方法で書き込むことは、重複していることとエラーが発生しやすいです。 FETCHRは、データサービスコールに抽象化レイヤーを提供して、サーバーとクライアント側の同じAPIを使用してデータを取得できるようにします。
npm install fetchr --save重要:ブラウザでは、 Fetchr Fetch APIに完全に依存しています。古いブラウザをサポートする必要がある場合は、ポリフィルもインストールする必要があります(https://github.com/github/fetchなど)。
以下の手順に従って、FETCHRを適切にセットアップします。これは、Expressフレームワークを使用していることを前提としています。
サーバー側では、Custom API EndpointでFETCHRミドルウェアをExpressアプリに追加します。
FETCHR Middlewareは、FETCHRミドルウェアを使用する前に、 body-parserミドルウェア(またはreq.bodyを入力する代替ミドルウェア)を使用していることを期待しています。
import express from 'express' ;
import Fetcher from 'fetchr' ;
import bodyParser from 'body-parser' ;
const app = express ( ) ;
// you need to use body-parser middleware before fetcher middleware
app . use ( bodyParser . json ( ) ) ;
app . use ( '/myCustomAPIEndpoint' , Fetcher . middleware ( ) ) ;クライアント側では、 xhrPathオプションが前のステップにミドルウェアがマウントされたパスを一致させる必要があります
xhrPath 、エンドポイントをサービスにカスタマイズできるオプションの構成プロパティであり、デフォルト/apiです。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
} ) ;アプリケーションで使用するデータサービスを登録する必要があります。サービスのインターフェイスは、 resourceプロパティと少なくとも1つのCRUD操作を定義する必要があるオブジェクトです。 CRUD操作のいずれかを呼び出すと、 resourceプロパティが使用されます。
// app.js
import Fetcher from 'fetchr' ;
import myDataService from './dataService' ;
Fetcher . registerService ( myDataService ) ; // dataService.js
export default {
// resource is required
resource : 'data_service' ,
// at least one of the CRUD methods is required
read : async function ( { req , resource , params , config } ) {
return { data : 'foo' } ;
} ,
// other methods
// create: async function({ req, resource, params, body, config }) {},
// update: async function({ req, resource, params, body, config }) {},
// delete: async function({ req, resource, params, config }) {}
} ;データサービスは、ユーザーのセッションで現在のログインを取得するために、個々のリクエストにアクセスする必要がある場合があります。このため、フェッチャーはリクエストごとに一度インスタンス化する必要があります。
サーバーサイドでは、エクスプレスミドルウェアで、リクエストごとにフェッチャーをインスタンス化する必要があります。クライアントサイドでは、これはページの読み込みでのみ発生する必要があります。
// app.js - server
import express from 'express' ;
import Fetcher from 'fetchr' ;
import myDataService from './dataService' ;
const app = express ( ) ;
// register the service
Fetcher . registerService ( myDataService ) ;
// register the middleware
app . use ( '/myCustomAPIEndpoint' , Fetcher . middleware ( ) ) ;
app . use ( function ( req , res , next ) {
// instantiated fetcher with access to req object
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath will be ignored on the serverside fetcher instantiation
req : req ,
} ) ;
// perform read call to get data
fetcher
. read ( 'data_service' )
. params ( { id : 42 } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle error
} ) ;
} ) ; // app.js - client
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath is REQUIRED on the clientside fetcher instantiation
} ) ;
fetcher
. read ( 'data_api_fetcher' )
. params ( { id : 42 } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle errors
} ) ;
// for create you can use the body() method to pass data
fetcher
. create ( 'data_api_create' )
. body ( { some : 'data' } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle errors
} ) ; 簡単な例を参照してください。
クライアントのサービスコールは、透過的にフェッチリクエストになります。一般的なフェッチコールにキャッシュヘッダーを設定することをお勧めします。サービスのコールバックに3番目のパラメーターを提供することで、これを行うことができます。電話をかけたサービスによってヘッダーが設定されたものを見たい場合は、コールバックの3番目のパラメーターを検査するだけです。
注:約束を使用している場合、メタデータは解決された値のmetaプロパティで利用可能になります。
// dataService.js
export default {
resource : 'data_service' ,
read : async function ( { req , resource , params , config } ) {
return {
data : 'response' , // business logic
meta : {
headers : {
'cache-control' : 'public, max-age=3600' ,
} ,
statusCode : 200 , // You can even provide a custom statusCode for the fetch response
} ,
} ;
} ,
} ; fetcher
. read ( 'data_service' )
. params ( { id : ### } )
. then ( ( { data , meta } ) {
// data will be 'response'
// meta will have the header and statusCode from above
} ) ; fetchrインスタンスには、 fetcher.getServiceMetaと呼ばれる便利な方法があります。この方法は、これまでに発生したすべての呼び出しに対して、メタデータを配列形式で返します。サーバーには、現在のリクエストに対するすべてのサービスコールが含まれます。クライアントでは、これには現在のセッションのすべてのサービスコールが含まれます。
通常、ブラウザセッション全体のデフォルトオプションを使用してフェッチャーをインスタンス化しますが、同じセッションの後半でこれらのオプションを更新する場合がある場合があります。
updateOptionsメソッドでそれを行うことができます。
// Start
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
xhrTimeout : 2000 ,
} ) ;
// Later, you may want to update the xhrTimeout
fetcher . updateOptions ( {
xhrTimeout : 4000 ,
} ) ; fetchr crudメソッドでエラーが発生した場合、エラーオブジェクトをスローする必要があります。エラーオブジェクトには、クライアントに送信されるJSONシリアル化可能なオブジェクトを含むstatusCode (デフォルト500)とoutputプロパティを含める必要があります。
export default {
resource : 'FooService' ,
read : async function create ( req , resource , params , configs ) {
const err = new Error ( 'it failed' ) ;
err . statusCode = 404 ;
err . output = { message : 'Not found' , more : 'meta data' } ;
err . meta = { foo : 'bar' } ;
throw err ;
} ,
} ;そしてあなたのサービスで:
fetcher
. read ( 'someData' )
. params ( { id : '42' } )
. catch ( ( err ) => {
// err instanceof FetchrError -> true
// err.message -> "Not found"
// err.meta -> { foo: 'bar' }
// err.name = 'FetchrError'
// err.output -> { message: "Not found", more: "meta data" }
// err.rawRequest -> { headers: {}, method: 'GET', url: '/api/someData' }
// err.reason -> BAD_HTTP_STATUS | BAD_JSON | TIMEOUT | ABORT | UNKNOWN
// err.statusCode -> 404
// err.timeout -> 3000
// err.url -> '/api/someData'
} ) ; クライアントにFETCHRリクエストを作成するときに、 abortメソッドを持つオブジェクトが返されます。これは、リクエストが完了する前に中止する場合に便利です。
const req = fetcher
. read ( 'someData' )
. params ( { id : 42 } )
. catch ( ( err ) => {
// err.reason will be ABORT
} ) ;
req . abort ( ) ; xhrTimeout 、すべてのクライアントサイドリクエストのタイムアウト(MS)を設定できるオプションの構成プロパティであり、デフォルトは3000です。クライアントサイドでは、すべてのリクエストにXhrpathとXhrtimeoutが使用されます。サーバーサイドでは、XhrpathとXhrtimeoutは必要ありません。無視されます。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
xhrTimeout : 4000 ,
} ) ;リクエストごとにタイムアウトを設定する場合は、 timeoutプロパティを使用してclientConfigを呼び出すことができます。
fetcher
. read ( 'someData' )
. params ( { id : 42 } )
. clientConfig ( { timeout : 5000 } ) // wait 5 seconds for this request before timing out
. catch ( ( err ) => {
// err.reason will be TIMEOUT
} ) ; 一部のアプリケーションでは、実際のサービスに送信される前にリクエストで渡されたサービスパラメーションを処理する必要がある状況がある場合があります。通常、サービス自体でそれらを処理します。ただし、多くのサービスで処理を実行する必要がある場合(つまり、セキュリティのためのサニタイゼーション)、 paramsProcessorオプションを使用できます。
paramsProcessor 、 Fetcher.middlewareメソッドに渡される関数です。リクエストオブジェクト、serveryInfoオブジェクト、およびService Paramsオブジェクトの3つの引数に渡されます。 paramsProcessor関数は、必要に応じてサービスパラメーションを変更できます。
これが例です:
/**
Using the app.js from above, you can modify the Fetcher.middleware
method to pass in the paramsProcessor function.
*/
app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
paramsProcessor : function ( req , serviceInfo , params ) {
console . log ( serviceInfo . resource , serviceInfo . operation ) ;
return Object . assign ( { foo : 'fillDefaultValueForFoo' } , params ) ;
} ,
} ) ,
) ; 一部のアプリケーションでは、クライアントに渡される前に応答を変更する必要がある状況がある場合があります。通常、サービス自体に変更を適用します。ただし、多くのサービスで応答を変更する必要がある場合(つまり、デバッグ情報を追加)、 responseFormatterオプションを使用できます。
responseFormatter 、 Fetcher.middlewareメソッドに渡される関数です。 3つの引数、リクエストオブジェクト、応答オブジェクト、およびサービス応答オブジェクト(つまり、サービスから返されたデータ)に渡されます。 responseFormatter関数は、サービス応答を変更して追加情報を追加できます。
以下の例をご覧ください。
/**
Using the app.js from above, you can modify the Fetcher.middleware
method to pass in the responseFormatter function.
*/
app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
responseFormatter : function ( req , res , data ) {
data . debug = 'some debug information' ;
return data ;
} ,
} ) ,
) ;これで、リクエストが実行されると、応答には上記のdebugプロパティが含まれます。
FETCHRは、完全なOriginホストをcorsPathオプションに渡すことができるようにすることにより、CORSサポートを提供します。
例えば:
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
corsPath : 'http://www.foo.com' ,
xhrPath : '/fooProxy' ,
} ) ;
fetcher . read ( 'service' ) . params ( { foo : 1 } ) . clientConfig ( { cors : true } ) ;さらに、 readコールを実行すると、 constructGetUriプロパティを渡すことで、Get URLの構築方法をカスタマイズすることもできます。
import qs from 'qs' ;
function customConstructGetUri ( uri , resource , params , config ) {
// this refers to the Fetcher object itself that this function is invoked with.
if ( config . cors ) {
return uri + '/' + resource + '?' + qs . stringify ( this . context ) ;
}
// Return `falsy` value will result in `fetcher` using its internal path construction instead.
}
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
corsPath : 'http://www.foo.com' ,
xhrPath : '/fooProxy' ,
} ) ;
fetcher . read ( 'service' ) . params ( { foo : 1 } ) . clientConfig ( {
cors : true ,
constructGetUri : customConstructGetUri ,
} ) ; 前のミドルウェアを追加することにより、CSRF攻撃からFETCHRミドルウェアパスを保護できます。
app.use('/myCustomAPIEndpoint', csrf(), Fetcher.middleware());
例として、これにはhttps://github.com/expressjs/csurfを使用できます。
次に、CSRFトークンがリクエストを検証できるように、CSRFトークンがリクエストで送信されていることを確認する必要があります。これを行うには、クライアントのoptions.contextオブジェクトのキーとしてトークンを渡します。
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath is REQUIRED on the clientside fetcher instantiation
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
} ,
} ) ;この_csrf 、サーバーで検証できるように、クエリパラメーターとしてすべてのクライアントリクエストで送信されます。
フェッチャーサービスを呼び出すときは、オプションの構成オブジェクトを渡すことができます。
この呼び出しがクライアントから行われると、構成オブジェクトはいくつかの要求オプションを設定するために使用され、デフォルトオプションをオーバーライドするために使用できます。
//app.js - client
const config = {
timeout : 6000 , // Timeout (in ms) for each request
unsafeAllowRetry : false , // for POST requests, whether to allow retrying this post
} ;
fetcher . read ( 'service' ) . params ( { id : 1 } ) . clientConfig ( config ) ;サーバーからのリクエストの場合、configオブジェクトは単に呼び出されているサービスに渡されます。
fetchrを設定して、グローバルまたはリクエスト構成でretry構成を指定することにより、失敗した要求を自動的に再試行できます。
// Globally
const fetchr = new Fetchr ( {
retry : { maxRetries : 2 } ,
} ) ;
// Per request
fetchr . read ( 'service' ) . clientConfig ( {
retry : { maxRetries : 1 } ,
} ) ;上記の構成を使用すると、FETCHRはread('service')を呼び出すときに1回だけ失敗するすべての要求を2回再試行します。
再試行メカニズムの仕組みをさらにカスタマイズできます。これらはすべて設定であり、デフォルト値です。
const fetchr = new Fetchr ( {
retry : {
maxRetries : 2 , // amount of retries after the first failed request
interval : 200 , // maximum interval between each request in ms (see note below)
statusCodes : [ 0 , 408 ] , // response status code that triggers a retry (see note below)
} ,
unsafeAllowRetry : false , // allow unsafe operations to be retried (see note below)
}間隔
各リクエスト間の間隔は、このAWSアーキテクチャブログ投稿に掲載されている指数関数的バックオフとフルジッター戦略に基づいて、次の式を尊重します。
Math . random ( ) * Math . pow ( 2 , attempt ) * interval ; attempt 0から始まる現在の再試行の数です。デフォルトでは、 interval 200msに相当します。
ステータスコード
歴史的な理由から、FETCHRは408回の応答のみを取得し、応答はまったくありません(たとえば、ステータスコード0で示されるネットワークエラー)。ただし、他のコードでも再試行するのに役立つ場合があります(502、503、504は、自動レトリの適切な候補になる可能性があります)。
unsafeallowretry
デフォルトでは、FETCHRはreadリクエストのみを取得します。これは安全上の理由で行われます。データベースから2回のエントリを読むことは、エントリを2回作成するほど悪くはありません。ただし、アプリケーションやリソースがこの種の保護を必要としない場合は、 unsafeAllowRetry trueに設定することでRetriesを許可することができます。FetchRはすべての操作を再試行します。
デフォルトでは、fetchrはすべてのコンテキスト値をクエリパラメーションとしてリクエストURLに追加します。 contextPicker使用すると、リクエストメソッド( GETまたはPOST )に応じて、どのコンテキスト変数がクエリパラメーションとして送信されるかをより強く制御できます。これは、誤ってバストをキャッシュしないように、 GET URLの変数の数を制限する場合に役立ちます。
contextPicker 、2つの引数を持つlodash/pickByのpredicateパラメーターと同じ形式に従います。 (value, key) 。
const fetcher = new Fetcher ( {
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
device : 'desktop' ,
} ,
contextPicker : {
GET : function ( value , key ) {
// for example, if you don't enable CSRF protection for GET, you are able to ignore it with the url
if ( key === '_csrf' ) {
return false ;
}
return true ;
} ,
// for other method e.g., POST, if you don't define the picker, it will pick the entire context object
} ,
} ) ;
const fetcher = new Fetcher ( {
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
device : 'desktop' ,
} ,
contextPicker : {
GET : [ 'device' ] , // predicate can be an array of strings
} ,
} ) ; フェッチャーサービスを呼び出すときは、カスタムリクエストヘッダーを追加できます。
リクエストには、「ClientConfig」にheadersオプションを追加すると、カスタムヘッダーが含まれます。
const config = {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ;
fetcher . read ( 'service' ) . params ( { id : 1 } ) . clientConfig ( config ) ;すべてのリクエストには、「フェッチャー」のコンストラクター引数にheadersオプションを追加するときにカスタムヘッダーが含まれています。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ) ; Fetcher Serviceの成功/失敗/レイテンシの統計を収集するには、 FetchrのstatsCollectorを構成できます。 statsCollector関数は、1つの段階で呼び出されます: stats 。 statsオブジェクトには、次のフィールドが含まれます。
create|read|update|delete import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
statsCollector : function ( stats ) {
// just console logging as a naive example. there is a lot more you can do here,
// like aggregating stats or filtering out stats you don't want to monitor
console . log (
'Request for resource' ,
stats . resource ,
'with' ,
stats . operation ,
'returned statusCode:' ,
stats . statusCode ,
' within' ,
stats . time ,
'ms' ,
) ;
} ,
} ) ; app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
statsCollector : function ( stats ) {
// just console logging as a naive example. there is a lot more you can do here,
// like aggregating stats or filtering out stats you don't want to monitor
console . log (
'Request for resource' ,
stats . resource ,
'with' ,
stats . operation ,
'returned statusCode:' ,
stats . statusCode ,
' within' ,
stats . time ,
'ms' ,
) ;
} ,
} ) ,
) ; このソフトウェアはYahoo!で無料で使用できますInc. BSDライセンス。ライセンステキストと著作権情報については、ライセンスファイルを参照してください。