Skip to content

Commit

Permalink
[Fixes #108] Refactor CRUD interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajiv Tirumalareddy committed Aug 6, 2015
1 parent 07f47d2 commit aad2561
Show file tree
Hide file tree
Showing 7 changed files with 1,111 additions and 1,199 deletions.
119 changes: 77 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ npm install fetchr --save

Follow the steps below to setup Fetchr properly. This assumes you are using the [Express](https://www.npmjs.com/package/express) framework.

### 1. Middleware
### 1. Configure Server

On the server side, add the Fetchr middleware into your express app at a custom API endpoint.

Expand All @@ -40,41 +40,38 @@ app.use(bodyParser.json());
app.use('/myCustomAPIEndpoint', Fetcher.middleware());
```

### 2. API xhrPath and xhrTimeout
### 2. Configure Client

`xhrPath` is an optional config property that allows you to customize the endpoint to your services, defaults to `/api`.

`xhrTimeout` is an optional config property that allows you to set timeout (in ms) for clientside requests, defaults to `3000`.
On the client side, it is necessary for the `xhrPath` option to match the path where the middleware was mounted in the previous step

On the clientside, xhrPath and xhrTimeout will be used for XHR requests. On the serverside, xhrPath and xhrTimeout are not needed and are ignored.

Note: Even though xhrPath is optional, it is necessary for xhrPath on the clientside fetcher to match the path where the middleware was mounted on in the previous step.
`xhrPath` is an optional config property that allows you to customize the endpoint to your services, defaults to `/api`.

```js
var Fetcher = require('fetchr');
var fetcher = new Fetcher({
xhrPath: '/myCustomAPIEndpoint',
xhrTimeout: 4000
xhrPath: '/myCustomAPIEndpoint'
});
```

### 3. Register data fetchers
### 3. Register data services

You will need to register any data fetchers that you wish to use in your application. The interface for your fetcher will be an object that must define a `name` property and at least one [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operation. The `name` propety will be used when you call one of the CRUD operations.
You will need to register any data services that you wish to use in your application.
The interface for your service will be an object that must define a `name` property and at least one [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operation.
The `name` propety will be used when you call one of the CRUD operations.

```js
// app.js
var Fetcher = require('fetchr');
var myDataFetcher = require('./dataFetcher');
Fetcher.registerFetcher(myDataFetcher);
var myDataService = require('./dataService');
Fetcher.registerService(myDataFetcher);
```

```js
// dataFetcher.js
// dataService.js
module.exports = {
// name is required
name: 'data_api_fetcher',
// at least one of the CRUD methods is Required
name: 'data_service',
// at least one of the CRUD methods is required
read: function(req, resource, params, config, callback) {
//...
},
Expand All @@ -87,20 +84,22 @@ module.exports = {

### 4. Instantiating the Fetchr Class

Data fetchers might need access to each individual request, for example, to get the current logged in user's session. For this reason, Fetcher will have to be instantiated once per request.
Data services might need access to each individual request, for example, to get the current logged in user's session.
For this reason, Fetcher will have to be instantiated once per request.

On the serverside, this requires fetcher to be instantiated per request, in express middleware. On the clientside, this only needs to happen on page load.
On the serverside, this requires fetcher to be instantiated per request, in express middleware.
On the clientside, this only needs to happen on page load.


```js
// app.js - server
var express = require('express');
var Fetcher = require('fetchr');
var app = express();
var myDataFetcher = require('./dataFetcher');
var myDataService = require('./dataService');

// register the fetcher
Fetcher.registerFetcher(myDataFetcher);
// register the service
Fetcher.registerService(myDataService);

// register the middleware
app.use('/myCustomAPIEndpoint', Fetcher.middleware());
Expand All @@ -113,9 +112,12 @@ app.use(function(req, res, next) {
});

// perform read call to get data
fetcher.read('data_api_fetcher', {id: ###}, {}, function (err, data, meta) {
fetcher
.read('data_service')
.params({id: ###})
.end(function (err, data, meta) {
// handle err and/or data returned from data fetcher in this callback
})
});
});
```

Expand All @@ -126,18 +128,47 @@ var Fetcher = require('fetchr');
var fetcher = new Fetcher({
xhrPath: '/myCustomAPIEndpoint' // xhrPath is REQUIRED on the clientside fetcher instantiation
});
fetcher.read('data_api_fetcher', {id: ###}, {}, function (err, data, meta) {
fetcher
.read('data_api_fetcher')
.params({id: ###})
.end(function (err, data, meta) {
// handle err and/or data returned from data fetcher in this callback
})
});
```

## Usage Examples

See the [simple example](https://github.com/yahoo/fetchr/tree/master/examples/simple).

## XHR Timeouts

`xhrTimeout` is an optional config property that allows you to set timeout (in ms) for all clientside requests, defaults to `3000`.
On the clientside, xhrPath and xhrTimeout will be used for XHR requests.
On the serverside, xhrPath and xhrTimeout are not needed and are ignored.

```js
var Fetcher = require('fetchr');
var fetcher = new Fetcher({
xhrPath: '/myCustomAPIEndpoint',
xhrTimeout: 4000
});
```

If you have an individual request that you need to ensure has a specific timeout you can do that via the `timeout` option in `clientConfig`:

```js
fetcher
.read('someData')
.params({id: ###})
.clientConfig({timeout: 5000}) // wait 5 seconds for this request before timing it out
.end(function (err, data, meta) {
// handle err and/or data returned from data fetcher in this callback
});
```

## CORS Support

Fetchr provides [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) support by allowing you to pass the full origin host into `corsPath`.
Fetchr provides [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) support by allowing you to pass the full origin host into `corsPath` option.

For example:

Expand All @@ -147,9 +178,11 @@ var fetcher = new Fetcher({
corsPath: 'http://www.foo.com',
xhrPath: '/fooProxy'
});
fetcher.read('service', { foo: 1 }, {
cors: true
}, callbackFn);
fetcher
.read('service')
.params({ foo: 1 })
.clientConfig({ cors: true })
.end(callbackFn);
```

Additionally, you can also customize how the GET URL is constructed by passing in the `constructGetUri` property when you execute your `read` call:
Expand All @@ -169,10 +202,14 @@ var fetcher = new Fetcher({
corsPath: 'http://www.foo.com',
xhrPath: '/fooProxy'
});
fetcher.read('service', { foo: 1 }, {
cors: true,
constructGetUri: customConstructGetUri
}, callbackFn);
fetcher
.read('service')
.params({ foo: 1 })
.clientConfig({
cors: true,
constructGetUri: customConstructGetUri
})
.end(callbackFn);
```


Expand Down Expand Up @@ -201,22 +238,20 @@ This `_csrf` will be sent in all XHR requests as a query parameter so that it ca

When calling a Fetcher service you can pass an optional config object.

When this call is made from the client the config object is used to define XHR request options and can be used to override default options:
When this call is made from the client, the config object is used to define XHR request options and can be used to override default options:

```js
//app.js - client
var config = {
timeout: 6000, // Timeout (in ms) for each request
retry: {
interval: 100, // The start interval unit (in ms)
max_retries: 2 // Number of max retries
},
unsafeAllowRetry: false // for POST requests, whether to allow retrying this post
};

fetcher.read('data_api_fetcher', {id: ###}, config, function (err, data, meta) {
//handle err and/or data returned from data fetcher in this callback
});
fetcher
.read('service')
.params({ id: 1 })
.clientConfig(config)
.end(callbackFn);
```

For requests from the server, the config object is simply passed into the service being called.
Expand Down
2 changes: 1 addition & 1 deletion examples/simple/server/fetchers/flickr.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FlickrFetcher = {
api_key: flickr_api_key,
method: params.method || 'flickr.photos.getRecent',
per_page: parseInt(params.per_page, 10) || 10,
format: config.format || 'json',
format: 'json',
nojsoncallback: config.nojsoncallback || 1
},
url = flickr_api_root + '?' + querystring.stringify(paramsObj);
Expand Down
25 changes: 12 additions & 13 deletions examples/simple/shared/getFlickrPhotos.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
module.exports = function flickrRead (fetcher, callback) {
fetcher.read('flickr', {
method: 'flickr.photos.getRecent',
per_page: 5
},
{
format: 'json'
},
function(err, data) {
if (err) {
callback && callback(new Error('failed to fetch data ' + err.message));
}
callback && callback(null, data);
});
fetcher
.read('flickr')
.params({
method: 'flickr.photos.getRecent',
per_page: 5
})
.end(function(err, data) {
if (err) {
callback && callback(new Error('failed to fetch data ' + err.message));
}
callback && callback(null, data);
});

};
Loading

0 comments on commit aad2561

Please sign in to comment.