API layer for the web, organize your api requests nicely.
中文 | English |
Instead of repeating api requests everywhere in your SPA, a maintainable model layer for backend’s api service seems more reasonable. Web-model has many useful features: request/response guards, request caching(web storage), singleton request, and more to come. Web-model rely superagent as ajax tool.
Request lifecycle:
request -> Model.beforeEach -> instance.beforeEach -> (requesting) -> response -> Model.afterEach -> instance.afterEach -> handler
Suppose we have a Restful API resource: ‘https://www.xxxx.com/apple’, organize all related requests inside one appleModel.js
with Web-model:
import Model from 'web-model';
export default new Model({
base: 'https://xxx.xxxx.com',
api: {
getApples() {
return this.request.get('/apple')
},
getApple(id) {
return this.request.get('/apple/' + id)
},
saveApple(apple) {
return this.request.post('/apple').send(apple)
},
updateApple(apple) {
return this.request.put('/apple/' + id).send(apple)
},
deleteApple(id) {
return this.request.del('/apple/' + id)
},
}
})
then use the model instance wherever an apple-resource request needed:
import appleModel from './path/to/appleModel.js'
// ...
appleModel.getApples().then(({body: {apples}}) => {
console.log('all my apples are here: ', apples);
})
// etc.
check superagent’s docs first.
import Model from 'web-model'
Model.use({
// url prefix
base: String,
// public request guard
beforeEach(next) {
/**
* 1. this bind to current request object
* 2. call next(falsy), break the request:
* 3. call next() or next(truthy), continue the request:
*/
},
// public response guard
afterEach(err, res) {
/**
* 1. this bind to current request object
* 2. (err, res) come from superagent's repsonse
*/
}
})
// inside xxxModel.js
import Model from 'web-model'
export default new Model({
// instance specific url prefix, override public url prefix
base: String,
// instance specific request guard
beforeEach(next) {
/**
* 1. this bind to current request object
* 2. call next(falsy), break the request:
* 3. call next() or next(truthy), continue the request:
*/
},
// instance specific response guard
afterEach(err, res) {
/**
* 1. this bind to current request object
* 2. (err, res) come from superagent's response
*/
},
// request method examples
api: {
/**
* @return {Promise}
*/
xxxRequest() {
/**
* 1. this.request is a modified superagent
*/
}
}
})
escape(direction) escape guards manually.
// Example: userModel.js
export default new Model({
api: {
login(user) {
this.request
.post('/login')
.send(user)
.escape() // escape('both') or escape('before') or escape('after')
}
}
})
cache(minutes, useSessionStorage) cache a GET or HEAD request
// Example: userModel.js
export default new Model({
api: {
getUser(id) {
return this.request
.get('/' + id)
.cache(30, true) // 30 minutes' cache in sessionStorage
// .cache(30) // 30 minutes' cache in localStorage
}
}
})
singleton() mark a request as singleton
this means only one request can be in process at the same time, previous request will be aborted.
// Example: userModel.js
export default new Model({
api: {
superComplicatedQuery(id, query) {
return this.request
.get(`/user/${id}/report`)
.singleton()
.query(query)
}
}
})