• Stars
    star
    129
  • Rank 270,874 (Top 6 %)
  • Language
    JavaScript
  • License
    Other
  • Created about 8 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Querystring parser middleware for MongoDB, Express and Nodejs (MEN)

Querymen

JS Standard Style NPM version Build Status Coveralls Status Dependency Status Downloads

Querystring parser middleware for MongoDB, Express and Nodejs

Install

npm install --save querymen

Examples

Pagination

Querymen has a default schema to handle pagination. This is the most simple and common usage.

import { middleware as query } from 'querymen';

app.get('/posts', query(), ({ querymen: { query, select, cursor } }, res) => {

  Post.find(query, select, cursor).then(posts => {
    // posts are proper paginated here
  });
});

User requests /posts?page=2&limit=20&sort=-createdAt querymen will be:

querymen = {
  query: {},
  select: {},
  cursor: {
    limit: 20, 
    skip: 20, 
    sort: { createdAt: -1 }
  }
}

User requests /posts?q=term&fields=title,desc querymen will be:

When user requests /posts?q=term, querymen parses it to {keywords: /term/i}. It was designed to work with mongoose-keywords plugin, which adds a keywords field to schemas (check that out).

querymen = {
  query: {
    keywords: /term/i
  },
  select: {
    title: 1,
    desc: 1
  },
  cursor: {
    // defaults
    limit: 30, 
    skip: 0, 
    sort: { createdAt: -1 }
  }
}

User requests /posts?fields=-title&sort=name,-createdAt querymen will be:

querymen = {
  query: {},
  select: {
    title: 0
  },
  cursor: {
    limit: 30, 
    skip: 0, 
    sort: {
      name: 1,
      createdAt: -1
    }
  }
}

Custom schema

You can define a custom schema, which will be merged into querymen default schema (explained above).

import { middleware as query } from 'querymen';

app.get('/posts', query({
  after: {
    type: Date,
    paths: ['createdAt'],
    operator: '$gte'
  }
}), ({ querymen }, res) => {
  Post.find(querymen.query).then(posts => {
    // ...
  });
});

User requests /posts?after=2016-04-23 querymen will be:

querymen = {
  query: {
    createdAt: { $gte: 1461369600000 }
  },
  select: {},
  cursor: {
    // defaults
    limit: 30, 
    skip: 0, 
    sort: { createdAt: -1 }
  }
}

Reusable schemas

You can create reusable schemas as well. Just instantiate a Schema object.

import { middleware as query, Schema } from 'querymen';

const schema = new Schema({
  tags: {
    type: [String],
  }
});

// user requests /posts?tags=world,travel
// querymen.query is { tags: { $in: ['world', 'travel'] }}
app.get('/posts', query(schema));
app.get('/articles', query(schema));

Advanced schema

import { middleware as query, Schema } from 'querymen';

const schema = new Schema({
  active: Boolean, // shorthand to { type: Boolean }
  sort: '-createdAt', // shorthand to { type: String, default: '-createdAt' }
  term: {
    type: RegExp,
    paths: ['title', 'description'],
    bindTo: 'search' // default was 'query'
  },
  with_picture: {
    type: Boolean,
    paths: ['picture'],
    operator: '$exists'
  }
}, {
  page: false, // disable default parameter `page`
  limit: 'max_items' // change name of default parameter `limit` to `max_items`
});

app.get('/posts', query(schema), ({ querymen }, res) => {
  // user requests /posts?term=awesome&with_picture=true&active=true&max_items=100
  // querymen.query is { picture: { $exists: true }, active: true }
  // querymen.cursor is { limit: 100, sort: { createdAt: -1 } }
  // querymen.search is { $or: [{ title: /awesome/i }, { description: /awesome/i }]}
});

Dynamic advanced schema

import { middleware as query, Schema } from 'querymen';
const schema = new Schema();

schema.formatter('scream', (scream, value, param) => {
  if (scream) {
    value = value.toUpperCase() + '!!!!!!!';
  }
  return value;
});

schema.param('text', null, { type: String }); // { type: String }
schema.param('text').option('scream', true); // { type: String, scream: true }
schema.param('text').value('help');
console.log(schema.param('text').value()); // HELP!!!!!!!

schema.validator('isPlural', (isPlural, value, param) => {
  return {
    valid: !isPlural || value.substr(-1) === 's',
    message: param.name + ' must be in plural form.'
  };
});

schema.param('text').option('isPlural', true); // { type: String, scream: true, isPlural: true }
console.log(schema.validate()); // false
schema.param('text', 'helps');
console.log(schema.validate()); // true
console.log(schema.param('text').value()); // HELPS!!!!!!!

schema.parser('elemMatch', (elemMatch, value, path, operator) => {
  if (elemMatch) {
    value = { [path]: { $elemMatch: {[elemMatch]: {[operator]: value } }}};
  }
  return value;
});

schema.param('text', 'ivegotcontrols');
console.log(schema.param('text').parse()); // { text: 'IVEGOTCONTROLS!!!!!!!' }

schema.param('text').option('elemMatch', 'prop');
console.log(schema.param('text').parse()); // { text: { $elemMatch: { prop: { $eq: 'IVEGOTCONTROLS!!!!!!!'} }}}

Geo queries

Querymen also support geo queries, but it's disabled by default. To enable geo queries you just need to set near option to true in schema options.

import { middleware as query } from 'querymen';

app.get('/places', query({}, { near: true }), (req, res) => {
  
});

Its paths option is set to ['location'] by default, but you can change this as well:

import { middleware as query } from 'querymen';

app.get('/places', 
  query({
    near: { paths: ['loc'] }
  }, {
    near: true
  }), 
  (req, res) => {
  
  });

User requests /places?near=-22.332113,-44.312311 (latitude, longitude), req.querymen.query will be:

req.querymen.query = {
  loc: {
    $near: {
      $geometry: {
        type: 'Point',
        coordinates: [-44.312311, -22.332113]
      }
    }
  }
}

User requests /places?near=-22.332113,-44.312311&min_distance=200&max_distance=2000 (min_distance and max_distance in meters), req.querymen.query will be:

req.querymen.query = {
  loc: {
    $near: {
      $geometry: {
        type: 'Point',
        coordinates: [-44.312311, -22.332113]
      },
      $minDistace: 200,
      $maxDistance: 2000
    }
  }
}

You can also use legacy geo queries as well. Just set geojson option in param:

import { middleware as query } from 'querymen';

app.get('/places', 
  query({
    near: {
      paths: ['loc'],
      geojson: false
    }
  }, {
    near: true
  }), 
  (req, res) => {
  
  });

User requests /places?near=-22.332113,-44.312311&min_distance=200&max_distance=2000, req.querymen.query will be:

req.querymen.query = {
  loc: {
    $near: [-44.312311, -22.332113],
    // convert meters to radians automatically
    $minDistace: 0.000031,
    $maxDistance: 0.00031
  }
}

Error handling

// user requests /posts?category=world
import { middleware as query, querymen, Schema } from 'querymen';

const schema = new Schema({
  category: {
    type: String,
    enum: ['culture', 'general', 'travel']
  }
});

app.get('/posts', query(schema));

// create your own handler
app.use((err, req, res, next) => {
  res.status(400).json(err);
});

// or use querymen error handler
app.use(querymen.errorHandler());

Response body will look like:

{
  "valid": false,
  "name": "enum",
  "enum": ["culture", "general", "travel"],
  "value": "world",
  "message": "category must be one of: culture, general, travel"
}

Contributing

This package was created with generator-rise. Please refer to there to understand the codestyle and workflow. Issues and PRs are welcome!

License

MIT Β© Diego Haz

More Repositories

1

constate

React Context + State
TypeScript
3,916
star
2

arc

React starter kit based on Atomic Design
JavaScript
2,910
star
3

rest

REST API generator with Node.js, Express and Mongoose
JavaScript
1,778
star
4

generact

Generate React components by replicating your own
JavaScript
1,481
star
5

awesome-react-context

😎 A curated list of stuff related to the new React Context API
JavaScript
915
star
6

styled-tools

Useful interpolated functions for CSS-in-JS
JavaScript
805
star
7

schm

Composable schemas for JavaScript and Node.js
JavaScript
513
star
8

reuse

♻️ Reuse React components to create new ones
TypeScript
496
star
9

singel

Single Element Pattern
JavaScript
409
star
10

nod

Node.js module generator/boilerplate with Babel, Jest, Flow, Documentation and more
JavaScript
360
star
11

redux-saga-thunk

Dispatching an action handled by redux-saga returns promise
JavaScript
221
star
12

styled-theme

Extensible theming system for styled-components πŸ’…
JavaScript
183
star
13

parse-prop-types

Parses React prop-types into a readable object
JavaScript
67
star
14

bodymen

Body parser middleware for MongoDB, Express and Nodejs (MEN)
JavaScript
48
star
15

redux-saga-social-login

Facebook/Google login implementation with redux-saga
JavaScript
25
star
16

list-react-files

List React component files inside a directory
JavaScript
24
star
17

mongoose-keywords

Mongoose plugin that generates a keywords path combining other paths values
JavaScript
22
star
18

redux-modules

A modular approach to better organize redux stuff (not another library)
JavaScript
21
star
19

generator-rest-example

A fully commented RESTful API example generated with generator-rest
JavaScript
18
star
20

styled-selector

Get static CSS(-in-JS) selectors from React components
TypeScript
17
star
21

coolors-to-hex

Get hexadecimal values from a coolors url
JavaScript
16
star
22

webpack-blocks-split-vendor

A webpack block that splits vendor javascript into separated bundle
JavaScript
15
star
23

webpack-blocks-happypack

A webpack block that adds happypack support to your webpack config
JavaScript
12
star
24

redux-form-submit

Adds an async submit action creator to redux-form
JavaScript
9
star
25

webpack-spawn-plugin

A webpack plugin that runs child_process.spawn within compilation
JavaScript
7
star
26

waterfall-grid

A Polymer wrapper element for waterfall.js, a 1KB Javascript library for Pinterest-like grids.
HTML
7
star
27

webpack-blocks-server-source-map

A webpack block that adds source map support to server bundle
JavaScript
5
star
28

yo

A Yeoman generator for generating next generation of Yeoman generators
JavaScript
5
star
29

webpack-assets-by-type-plugin

A webpack plugin that save assets by type
JavaScript
5
star
30

arc-universal-redux

Universal Redux version of ARc boilerplate
JavaScript
5
star
31

webpack-sort-chunks

Sorts webpack chunks by dependency
JavaScript
4
star
32

webpack-child-config-plugin

A webpack plugin that runs/watches another config
JavaScript
4
star
33

rest-api

REST API Boilerplate with Mongoose, Express and Nodejs
JavaScript
4
star
34

rich-param

An object with name and value which accepts pluggable methods as formatters or validators
JavaScript
3
star
35

reakit-code-lab

TypeScript
3
star
36

arc-fullstack

Fullstack version of ARc boilerplate
JavaScript
3
star
37

mongoose-create-unique

Mongoose plugin to create a document or return the existing one based on the unique index
JavaScript
3
star
38

divisa

A game about war and peace.
JavaScript
2
star
39

eyeport

Collection of useful methods to deal with element and viewports in DOM
JavaScript
2
star
40

alpha-bank

JavaScript
2
star
41

guild

Just a social network prototype made with jQuery
HTML
2
star
42

is-git-rev

Verify if a string is a git-rev hash
JavaScript
2
star
43

clone

JavaScript
1
star
44

radiotoca

A PHP web radio with Twitter integration
PHP
1
star
45

adjacente

Blog para disciplina ExpansΓ£o dos Sentidos
JavaScript
1
star
46

diegohaz

1
star
47

hear-parse

JavaScript
1
star
48

hear-api

JavaScript
1
star
49

diegohaz.com

JavaScript
1
star