js-data-http
is an HTTP adapter for JSData.
Table of contents
Quick Start
Browser:
npm install --save js-data@rc js-data-http@rc
or
bower install --save js-data@rc js-data-http@rc
or install from a CDN:
Load js-data-http.js
after js-data.js
.
import { DataStore } from 'js-data';
import { HttpAdapter } from 'js-data-http';
const httpAdapter = new HttpAdapter();
const store = new DataStore();
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('school');
store.defineMapper('student');
// GET /school/1
store.find('school', 1).then((school) => {
console.log('school');
});
Node.js:
npm install --save axios js-data@rc js-data-http-node@rc
import { Container } from 'js-data';
import { HttpAdapter } from 'js-data-http-node';
const httpAdapter = new HttpAdapter({
basePath: 'https://mydomain.com'
});
const store = new Container();
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('school');
store.defineMapper('student');
// GET /school/1
store.find('school', 1).then((school) => {
console.log('school');
});
Dependencies
js-data-http.js
bundles axios and depends onjs-data
js-data-fetch
depends onjs-data
js-data-http-node
depends onjs-data
and optionally axios
See also JSData's dependencies.
Configuring the adapter
The JSData HTTP adapter can be configured upon instantiation and anytime thereafter. See the configuration options
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
const httpAdapter = new HttpAdapter(options);
console.log(httpAdapter.basePath); // "https://mydomain.com"
httpAdapter.basePath = 'https://otherdomain.com';
console.log(httpAdapter.basePath); // "https://otherdomain.com"
Custom deserialization
When an HTTP request has completed, the HTTP adapter must get the data out of the response so the data can be forwarded to JSData. The HTTP adapter does this with a deserialize
method. The default deserialize
method takes the response
object returned by the underlying HTTP library and extracts the data
property and forwards it to JSData, basically like this: return response.data;
The adapter's deserialize
method is used by the adapter's CRUD methods (find
, findAll
, update
, etc.).
Depending on how your server formats response data, you may find it necessary to customize the HTTP adapter's deserialize
method. Here are a bunch of examples:
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com',
deserialize: function (mapper, response, opts) {
if (/* some condition */) {
// return something custom
}
// Else, do default behavior
return HttpAdapter.prototype.deserialize.call(this, mapper, id, opts);
}
};
// pass options to the constructor
const httpAdapter = new HttpAdapter(options);
import { DataStore } from 'js-data';
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com'
};
const httpAdapter = new HttpAdapter(options);
const store = new DataStore();
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('book', {
deserialize: function (mapper, response, opts) {
return response.data.books;
}
});
import { DataStore } from 'js-data';
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
const httpAdapter = new HttpAdapter(options);
const store = new DataStore();
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('book');
const query = {};
const options = {
params: { details: true },
deserialize: function (BookMapper, response, opts) {
return response.data.items;
}
};
// GET https://mydomain.com/book?details=true
store.findAll('book', query, options)
.then((bookDetails) => {
console.log(bookDetails);
})
import { HttpAdapter } from 'js-data-http';
class CustomHttpAdapter extends HttpAdapter {
deserialize(mapper, response, opts) {
if (/* some condition */) {
// return something custom
}
// Else, do default behavior
return super.deserialize(mapper, response, opts);
}
}
const options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
const httpAdapter = new CustomHttpAdapter(options);
Extending the adapter
The options
arguments passed to the HttpAdapter
constructor function is mixed directly into the new instance. This allows you to extend the adapter during construction:
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com',
find: function (mapper, id, opts) {
if (/* some condition */) {
// do something custom
}
// Else, do default behavior
return HttpAdapter.prototype.find.call(this, mapper, id, opts);
}
};
// pass options to the constructor
const httpAdapter = new HttpAdapter(options);
You can also extend the HttpAdapter
class:
import { HttpAdapter } from 'js-data-http';
class CustomHttpAdapter extends HttpAdapter {
find(mapper, id, opts) {
if (/* some condition */) {
// do something custom
}
// Else, do default behavior
return super.find(mapper, id, opts);
}
}
const options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
const httpAdapter = new CustomHttpAdapter(options);
var HttpAdapter = require('js-data-http').HttpAdapter;
var CustomHttpAdapter = HttpAdapter.extend({
find: function (mapper, id, opts) {
if (/* some condition */) {
// do something custom
}
// Else, do default behavior
return HttpAdapter.prototype.find.call(this, mapper, id, opts);
}
});
var options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
var httpAdapter = new CustomHttpAdapter(options);
HTTP Actions
Try the HTTP actions live demo.
import { DataStore } from 'js-data';
import { HttpAdapter, addAction } from 'js-data-http';
const store = new DataStore();
const httpAdapter = new HttpAdapter();
store.registerAdapter('http', httpAdapter, { 'default': true });
store.defineMapper('school', {
endpoint: 'schools'
});
// Setup action: GET /reports/schools/:school_id/teachers
addAction('getTeacherReports', {
pathname: 'teachers',
method: 'GET'
})(store.getMapper('school'));
// GET /reports/schools/1234/teachers
store.getMapper('school').getTeacherReports(1234, {
basePath: 'reports'
}).then((response) => {
console.log('response', response.data);
});
import { DataStore } from 'js-data';
import { HttpAdapter, addAction } from 'js-data-http';
const store = new DataStore();
const httpAdapter = new HttpAdapter();
store.registerAdapter('http', httpAdapter, { 'default': true });
// Setup action: GET /reports/schools/:school_id/teachers
@addAction('getTeacherReports', {
pathname: 'teachers',
method: 'GET'
});
store.defineMapper('school', {
endpoint: 'schools'
});
// GET /reports/schools/1234/teachers
store.getMapper('school').getTeacherReports(1234, {
basePath: 'reports'
}).then((response) => {
console.log('response', response.data);
});
Using the HTTP adapter's lifecycle hooks
Like JSData itself, the HTTP adapter has lifecycle hooks. You can find them enumerated in the API Reference Documentation. The HTTP adapter has the CRUD lifecycle hooks similar to JSData, but adds some HTTP-specific lifecycle hooks, like beforeGET
, beforeHTTP
, afterPOST
, etc.
Here are some demos:
beforeCreate
andafterCreate
- Go to demobeforeFindAll
andafterFindAll
- Go to demo
Here's an example of using the beforeHTTP
hook as an HTTP interceptor to set a header:
import { HttpAdapter } from 'js-data-http';
const options = {
basePath: 'https://mydomain.com',
beforeHTTP: function (config, opts) {
config.headers || (config.headers = {});
config.headers.authorization = `Bearer ${localStorage.get('auth_token')}`;
// Now do the default behavior
return HttpAdapter.prototype.beforeHTTP.call(this, config, opts);
}
};
// pass options to the constructor
const httpAdapter = new HttpAdapter(options);
import { HttpAdapter } from 'js-data-http';
class CustomHttpAdapter extends HttpAdapter {
beforeHTTP(config, opts) {
config.headers || (config.headers = {});
config.headers.authorization = `Bearer ${localStorage.get('auth_token')}`;
// Now do the default behavior
return super.beforeHTTP(config, opts);
}
}
const options = {
basePath: 'https://mydomain.com'
};
// pass options to the constructor
const httpAdapter = new CustomHttpAdapter(options);
Using window.fetch
You can configure the HTTP adapter to use window.fetch
if available by setting the useFetch
option to true
. window.fetch
is experimental, so be sure to polyfill it if necessary.
Here's a polyfill: https://github.com/github/fetch
Using a custom HTTP library
To use a custom HTTP implementation, set the http
option when instantiating the adapter. The http
option must be a function that takes a single config
argument and returns a promise that resolves with the HTTP response. The config
argument that will be passed to your function follows the config format required by axios. The expected response format matches that of axios as well.
If you're using the HTTP adapter in the browser and you want to avoid loading axios, then install js-data-fetch
instead of js-data-http
. js-data-fetch
is identical to js-data-http
, except it does not bundle axios, perfect for when you want to use a custom HTTP library with the HTTP adapter.
Try the a live demo that uses superagent
as the HTTP underlying library.
Here's an example that makes the HTTP adapter use the $http
component from Angular 1.x:
app.service('httpAdapter', ($http) => {
return new JSDataHttp.HttpAdapter({
http: $http
});
});
Links
See an issue with this tutorial?
You can open an issue or better yet, suggest edits right on this page.
Need support?
Have a technical question? Post on the JSData Stack Overflow channel or the Mailing list.
Want to chat with the community? Hop onto the JSData Slack channel.
Updated over 7 years ago