웹 애플리케이션을위한 범용 데이터 액세스 계층.
일반적으로 서버에서 API 또는 데이터베이스를 직접 호출하여 일부 데이터를 가져옵니다. 그러나 클라이언트에서는 항상 같은 방식으로 서비스를 호출 할 수는 없습니다 (예 : 크로스 도메인 정책). 대신 XHR/Fetch 요청은 서비스로 전달되는 서버로 작성해야합니다.
두 환경 모두에 대해 다르게 코드를 작성 해야하는 것은 복제적이고 오류가 발생하기 쉽습니다. Fetchr은 데이터 서비스 호출에 대한 추상화 계층을 제공하여 서버 및 클라이언트 측에서 동일한 API를 사용하여 데이터를 가져올 수 있도록합니다.
npm install fetchr --save 중요 : 브라우저에서 Fetchr Fetch API에 완전히 의존합니다. 이전 브라우저를 지원 해야하는 경우 폴리 필 (예 : https://github.com/github/fetch)도 설치해야합니다.
아래 단계를 따라 FetchR을 올바르게 설정하십시오. 이것은 당신이 Express 프레임 워크를 사용하고 있다고 가정합니다.
서버 측면에서 사용자 정의 API 엔드 포인트의 Express 앱에 Fetchr 미들웨어를 추가하십시오.
Fetchr Middleware는 Fetchr Middleware를 사용하기 전에 body-parser Middleware (또는 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 속성과 하나 이상의 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 }) {}
} ;예를 들어 현재 로그인 한 사용자 세션에서 데이터 서비스는 각 개별 요청에 대한 액세스가 필요할 수 있습니다. 이러한 이유로 Fetcher는 요청 당 한 번 인스턴스화해야합니다.
서버 사이드에서는 Express Middleware에서 Fetcher가 요청 당 인스턴스화되어야합니다. ClientSide에서는 페이지로드에서만 발생하면됩니다.
// 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
} ) ; 간단한 예를 참조하십시오.
클라이언트에 대한 서비스 호출은 투명하게 페치 요청이됩니다. 일반적인 페치 호출에서 캐시 헤더를 설정하는 것이 좋습니다. 서비스의 콜백에 세 번째 매개 변수를 제공하여 그렇게 할 수 있습니다. 방금 호출 한 서비스에서 설정 한 헤더를 보려면 콜백에서 세 번째 매개 변수를 검사하십시오.
참고 : 약속을 사용하는 경우 메타 데이터는 해결 된 값의 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 라는 편의 방법이 있습니다. 이 방법은 지금까지 발생한 모든 호출에 대한 메타 데이터를 배열 형식으로 반환합니다. 서버에는 현재 요청에 대한 모든 서비스 호출이 포함됩니다. 클라이언트에는 현재 세션에 대한 모든 서비스 요청이 포함됩니다.
일반적으로 전체 브라우저 세션에 대한 기본 옵션으로 Fetcher를 인스턴스화하지만 같은 세션에서 나중에 이러한 옵션을 업데이트 할 경우가있을 수 있습니다.
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 은 모든 ClientSide 요청에 대한 시간 초과 (MS)를 3000 으로 설정할 수있는 선택적 구성 속성입니다. ClientSide에서는 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 메소드로 전달되는 함수입니다. 요청 객체, ServiceInfo 객체 및 서비스 매개 변수 객체의 세 인수가 전달됩니다. 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 메소드로 전달되는 함수입니다. 요청 오브젝트, 응답 개체 및 서비스 응답 개체 (예 : 서비스에서 반환 된 데이터) 인 세 가지 인수가 전달됩니다. 그런 다음 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은 전체 원산지 호스트를 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 토큰이 요청과 함께 전송되어 검증 될 수 있도록해야합니다. 이렇게하려면 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 모든 클라이언트 요청에서 쿼리 매개 변수로 전송되어 서버에서 검증 될 수 있습니다.
Fetcher 서비스를 호출 할 때 선택적 구성 객체를 전달할 수 있습니다.
이 호출이 클라이언트에서 이루어지면 구성 객체는 일부 요청 옵션을 설정하는 데 사용되며 기본 옵션을 무시하는 데 사용될 수 있습니다.
//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 ) ;서버의 요청의 경우 구성 객체가 단순히 호출되는 서비스로 전달됩니다.
글로벌 또는 요청 구성에서 retry 구성을 지정하여 실패한 요청을 자동으로 재 시도하도록 FetchR을 설정할 수 있습니다.
// Globally
const fetchr = new Fetchr ( {
retry : { maxRetries : 2 } ,
} ) ;
// Per request
fetchr . read ( 'service' ) . clientConfig ( {
retry : { maxRetries : 1 } ,
} ) ; 위의 구성을 사용하면 Fetchr은 실패한 모든 요청을 두 번 다시 시도하지만 read('service') 호출 할 때만 한 번만 다시 시도합니다.
재 시도 메커니즘이 어떻게 작동하는지 더 사용자 정의 할 수 있습니다. 이들은 모두 설정과 기본값입니다.
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는 자동 회수를위한 좋은 후보가 될 수 있습니다).
비 안전한
기본적으로 Fetchr은 read Rectrive 만 재시험 할 수 있습니다. 안전상의 이유로 이루어집니다. 데이터베이스에서 항목을 두 번 읽는 것은 항목을 두 번 만드는 것만 큼 나쁘지 않습니다. 그러나 응용 프로그램이나 리소스에 이러한 종류의 보호가 필요하지 않은 경우 unsafeAllowRetry 설정하여 true 로 설정하여 검색을 허용 할 수 있으며 Fetchr은 모든 작업을 재 시도합니다.
기본적으로 Fetchr은 모든 컨텍스트 값을 query params로 요청 URL에 추가합니다. contextPicker 사용하면 요청 메소드 ( GET 또는 POST )에 따라 어떤 컨텍스트 변수가 쿼리 매개 변수로 전송되는지를 더 잘 제어 할 수 있습니다. 이는 실수로 파열을하지 않기 위해 GET URL의 변수 수를 제한하려는 경우 유용합니다.
contextPicker (value, key) lodash/pickBy 의 predicate 매개 변수와 동일한 형식을 따릅니다.
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
} ,
} ) ; Fetcher 서비스를 호출 할 때 사용자 정의 요청 헤더를 추가 할 수 있습니다.
요청에는 'ClientConfig'에 headers 옵션을 추가 할 때 사용자 정의 헤더가 포함됩니다.
const config = {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ;
fetcher . read ( 'service' ) . params ( { id : 1 } ) . clientConfig ( config ) ; 모든 요청에는 'Fetcher'의 생성자 인수에 headers 옵션을 추가 할 때 사용자 정의 헤더가 포함됩니다.
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ) ; Fetcher Service의 성공/실패/대기 시간 통계를 수집하려면 Fetchr 의 statsCollector 구성 할 수 있습니다. statsCollector 함수는 하나의 논쟁으로 호출됩니다 : 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!에서 무료로 사용할 수 있습니다. BSD 라이센스. 라이센스 텍스트 및 저작권 정보는 라이센스 파일을 참조하십시오.