Security News
Internet Archive Hacked, 31 Million Record Compromised
The Internet Archive's "Wayback Machine" has been hacked and defaced, with 31 millions records compromised.
resource-schema
Advanced tools
Define schemas for RESTful resources from mongoose models, and generate middleware to GET, POST, PUT, and DELETE to those resources.
Define schemas for RESTful resources from mongoose models, and generate express middleware to GET, POST, PUT, and DELETE to those resources.
ResourceSchema allows you to define complex RESTful resources in a simple and declarative way.
Product = require './models/product'
var schema = {
'_id': '_id',
// Get resource field 'name' from model field 'name'
// Convert the name to lowercase whenever saved
'name': {
field: 'name',
set: function (productResource) { return productResource.name.toLowerCase(); }
},
// make sure the day matches the specified format before saving
'day': {
field: 'day',
match: /[0-9]{4}-[0-9]{2}-[0-9]{2}/
},
// Model field 'active' renamed to resource field 'isActive'
'isActive': 'active',
// Dynamically get field 'code' whenever the resource is requested:
'code': {
get: function (productModel) { productModel.letter + productModel.number }
},
// Dynamically get totalQuantitySold whenever the resource is requested.
// Resolve 'totalQuantitySoldByProductId' before applying the getter.
'totalQuantitySold': {
resolve: {
totalQuantitySoldByProductId: function ({models}, done) {
getTotalQuantitySoldById(models, done)
}
},
get: function (productModel, {totalQuantitySoldByProductId}) {
totalQuantitySoldByProductId[productModel._id]
}
}
// field soldOn allows you to query for products sold on the specified days
// e.g. api/products?soldOn=2014-10-01&soldOn=2014-10-05
'soldOn': {
type: String,
isArray: true,
match: /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/,
find: function (days) { return { 'day': $in: days } }
},
// field fromLastWeek allows you to query for products sold in the last week
// e.g. api/products?fromLastWeek=true
'fromLastWeek': {
type: Boolean,
find: (days) -> { 'day': $gt: '2014-10-12' }
}
};
var resource = new ResourceSchema(Product, schema);
// generate express middleware that automatically handles GET, POST, PUT, and DELETE requests:
app.get('/products', resource.get(), resource.send);
app.post('/products', resource.post(), resource.send);
app.put('products/:_id', resource.put('_id'), resource.send);
app.get('products/:_id', resource.get('_id'), resource.send);
app.delete('products/:_id', resource.delete('_id'), resource.send);
This abstracts away a lot of the boilerplate such as building queries, validating values, and handling errors, and allows you to focus on higher-level resource design.
Additionally, this provides a layer of abstraction which helps decouple your server models from your client.
npm install resource-schema --save
The schema allows define the shape of your resource. If you do not provide a schema, the resource will look exactly like the model.
We can define the schema using these properties:
Maps a resource field to a mongoose model field.
schema = {
'name': { field: 'name' }
}
We can also define this with a shorthand notation:
schema = {
'name': 'name'
}
Or even simpler with coffeescript:
schema = {
'name'
}
Note, this can be used to rename a model field to a new field on the resource:
schema = {
'category.name': 'categoryName'
}
// => {
// category: {
// name: 'value'
// }
// }
Dynamically get the value whenever a resource is requested.
var schema = {
'fullName': {
get: function (resource, context) {
resource.firtName + ' ' + resource.lastName
}
}
}
Function that dynamically sets the value whenever a resource is saved or updated.
var schema = {
'name': {
set: function (resource, context) {
return resource.name.toLowerCase()
}
}
}
Key value object where the key is the name of the variable to resolve, and the value is an asynchronous function that returns the value. The function accepts one argument:
Once a variable is resolved, it is attached to the "context" object, and is available to all the getters and setters for that field.
var schema = {
'note': {
resolve:
userNoteByUserId: function(context, done) {
var userIds = context.models.map(function(user) { return user._id });
UserNote.find({userId: $in: userIds}).then(function(notes) {
var userNoteByUserId = _(notes).indexBy('userId');
done(null, userNoteByUserId);
});
})
},
// userNoteByUserId now available on context object
get: function (user, context) {
var userNoteByUserId = context.userNoteByUserId;
return userNoteByUserId[user._id];
}
}
}
Function that dynamically builds a mongoose query whenever querying by this field. Return an object that will extend the mongoose query.
var schema = {
'soldOn': {
find: function (days, context) {
return { 'day': $in: days }
}
}
}
If true, do not include this field in the resource unless specifically requested with the $add query parameter
// GET /api/products?$add=name
var schema = {
'name': {
optional: true,
field: 'name'
}
}
Return a 400 invalid request if the provided value does not pass the validation test.
var schema = {
'date': {
validate: function(value) {
return /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/.test(value)
}
}
}
Return a 400 invalid request if the provided value does match the given regular expression.
var schema = {
'date': {
match: /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/
}
}
Convert the type of the value.
Valid types include
schema = {
'active': {
type: Boolean
}
}
This is especially useful for query parameters, which are a string by default
Convert value to array before saving/querying. This is especially for converting query parameters, which will not be an array if only querying by on value.
schema = {
'daysToSelect': {
isArray: true
find: function(days, context) { ... }
}
}
Options allow you to make configurations for the entire resource.
Filter limits resources returned from every GET request.
new ResourceSchema(Model, schema, {
filter: function(models) {
models.filter(function(model) {
return model.isActive
})
}
})
Limit the number of returned documents for GET requests. Defaults to 1000.
new ResourceSchema(Model, schema, {
limit: 100
})
Function returns an object that will be used at as starting point to build every query.
new ResourceSchema(Product, schema, {
find: function(context) {
active: true,
createdAt: $gt: '2013-01-01'
}
})
Like resolve on schema, but resolved variable available to every getter and setter on the resource.
### queryParams: Object
Define query parameters for this resource. Note, you could define these directly on the schema, but some people prefer to separate query parameters from all other fields.
```javascript
new ResourceSchema(Product, schema, {
queryParams: {
'soldOn': {
type: String,
isArray: true,
match: /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/,
find: function(days) {
return { 'day': $in: days }
}
},
'fromLastWeek': {
type: Boolean,
find: function(days) {
return { 'day': $gt: '2014-10-12' }
}
}
}
})
Once define a new resource, call .get(), .post(), .put(), or .delete() to generate the appropriate middleware to handle the request.
var resource = new ResourceSchema(Model, schema, options);
app.get('/products', resource.get(), function(req, res, next) {
# resources on res.body
});
The middleware will attach the resources to res.body, which can be used by other middleware, or sent immediately back to the client.
Handle bulk GET requests. Results can by filtered by query parameters. Limits response to 1000 resources by default.
var resource = new ResourceSchema(Model, schema, options);
app.get('/products', resource.get(), function(req, res, next) {
// resources on res.body
});
// GET /products?name=magicbox
Handle GET requests for single resource.
var resource = new ResourceSchema(Model, schema, options);
app.get('/products/:_id', resource.get('_id'), function(req, res, next) {
// resources on res.body
});
// GET /products/1234
// => {
// _id: 1234
// name: 'banana bread'
// }
Handle POST requests. Can take a single resource or an array of resources.
var resource = new ResourceSchema(Model, schema, options);
app.post('/products', resource.post(), function(req, res, next) {
// resources on res.body
});
// POST /products
// {
// _id: 1234
// name: 'banana bread'
// }
//
// or
//
// POST /products
// [
// {
// _id: 1234
// name: 'banana bread'
// },
// {
// _id: 4567
// name: 'apples'
// }
// ]
Generate middleware to handle PUT requests to a resource. This does an upsert, so if the resource does not exist, it will create one.
This will handle bulk PUT requests as well, automatically reading the idField and upserting for each resource.
var resource = new ResourceSchema(Model, schema, options);
app.put('/products/:_id', resource.put('_id'), function(req, res, next) {
// resources on res.body
});
// PUT /products/1234
// {
// _id: 1234
// name: 'banana bread'
// }
//
// or
//
// PUT /products
// [
// {
// _id: 1234
// name: 'banana bread'
// },
// {
// _id: 4567
// name: 'apples'
// }
// ]
Generate middleware to handle DELETE requests to a single resource.
var resource = new ResourceSchema(Model, schema, options);
app.delete('/products/:_id', resource.delete('_id'), function(req, res, next) {
// resources on res.body
});
// DELETE /products/1234
Convenience method for sending the resources on res.body back to the client.
var resource = new ResourceSchema(Model, schema, options);
app.get('/products', resource.get(), resource.send);
ResourceSchema allows you to use a variety of query parameters to interact with your resources.
Select fields to return on the resource. Similar to mongoose select.
GET /products?$select=name&$select=active
GET /products?$select[]=name&$select[]=active
GET /products?$select=name active
Limit the number of resources to return in the response
GET /products?$limit=10
Add an optional field to the resource. See optional schema field for more details.
GET /products?$add=quantitySold
You can query by any resource field with a 'field', 'find', or 'filter' attribute.
GET /products?name=strawberry
If the querying against one with a 'field' attribute, it will automatically perform an $in query.
GET /products?name=strawberry&name=apple
=>
Product.find({ 'name': $in: ['strawberry', 'apple'] })
Note that you can query nested fields with Express' [bracket] notation.
GET /products?categrory[name]=fruit
$ git clone https://github.com/goodeggs/resource-schema && cd resource-schema
$ npm install
$ npm test
Code of Conduct for contributing to or participating in this project.
0.15.0
FAQs
Define schemas for RESTful resources from mongoose models, and generate middleware to GET, POST, PUT, and DELETE to those resources.
The npm package resource-schema receives a total of 4 weekly downloads. As such, resource-schema popularity was classified as not popular.
We found that resource-schema demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The Internet Archive's "Wayback Machine" has been hacked and defaced, with 31 millions records compromised.
Security News
TC39 is meeting in Tokyo this week and they have approved nearly a dozen proposals to advance to the next stages.
Security News
Our threat research team breaks down two malicious npm packages designed to exploit developer trust, steal your data, and destroy data on your machine.