npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

automated-crud

v1.2.0

Published

Fast and easy way to add CRUD actions to a path

Downloads

14

Readme

AUTOMATED CRUD

A module to get started in seconds with basic CRUD operations Built with express (uses Router) and mongodb.

For most basic setup, install it, require it (or import), connect to DB, and use it. Example:

...
const automatedCrud = require('automated-crud')
automatedCrud.connectDB({
	// ...options
	dbName: 'store',
	collectionName: 'items'
})
app.use('/items', automatedCrud())
app.use('/users', automatedCrud({
	// ... options
	collectionName: 'users'
}))
...

Route Methods:

  1. POST -> Create a new DB object
    • id will be added automatically (except if id_type is set to skip)
    • Will return the object that was added, unless adding failed (auth: 403, validation: 400, other reason: 500)
  2. GET -> Get 1 or all objects
    • To get 1 object call and include the queryKey value (e.g. /items/1)
    • To get all objects call directly (e.g. /items) - and you can add the following queries:
      • skip: skip X amount of docs.
      • limit: limit to X amount of docs.
      • sortKey: sort by this DB key.
      • sortType: sort in asc/desc order (1/-1) 1 is default.
      • custom: see more info below
    • Will return:
      • Getting 1 doc: the object, 400 if not found, 500 if failed.
      • Getting all docs: Array of docs, empty if non found, 500 if failed.
  3. PUT -> Update 1 object
    • Requires the queryKey value (e.g. /items/2)
    • Will use $set by default, can be changed in options
    • Returns the (full) updated object, 400 if not found, 500 if failed
  4. DELETE -> Delete 1 object
    • Requires the queryKey value (e.g. /users/jacobKlein26)
    • Returns 204, 400 if not found, 500 if failed

GET queries

When calling get without a queryKey (e.g. calling /items/ and not /items/101), you can add queries to the request to control what you get,
You can use the actual DB key as the query, but you can't use the reserved ones (skip, limit, sortKey, and sortType).
For example - if you want to query itemCategory is "book" then call /items?itemCategory=book and { itemCategory: "book"} will be queried to the DB.

You can use dot notation (.) for nested or advanced queries.
\. escapes the nest (see example 2)
All queries are parsed for numbers, etc. see parseQueries option and Example 3

Example 1: (simple nested object)
You want to query the DB item: { itemInfo: { category: "book" } }
Call: /items?itemInfo.category=book

Example 2: (Query if a nested array includes one of a few values)
You want to query the DB item: { itemInfo: { categories: ["book", "reading", "romance"]}}, and you want to query all that include either "romance" OR "fun".
Call: /items?itemInfo\.categories.$in=romance&itemInfo\.categories.$in=fun

($in is from Mongo Docs for Array includes one of...)

express queries convert multiple of the same query name into an array, so this query will actually be: itemInfo\.categories.$in = ["romance", "fun"]

"\." (escaped dot) will avoid splitting the . (dot) into another nested object, and keep the actual "." - this is usefull when making queries which will only work if you do: {'itemInfo.categories': {'$in': ['romance','fun']}} and NOT if you do {itemInfo: {categories: {'$in': ['romance','fun']}}}

Example 3: (Query if a value is greather then)
You want to query the DB item: {totalSales: 25}, and you want to query all that are greater then 20.
Call: /items?totalSales.$gt=20

Queries will be converted using express-query-parser so all numbers/booleans/null/undefined are taken care of.
if you want to skip this (e.g. you want to be able to query a number as a string like: {age: "26"}) then set option parseQueries to false

Options:

  • Options can be sent either when calling connectDB or when configuring route, routes has higher priority
  • options sent to first connectDB call will be re-used for all routes (unless overwritten)
  • If more then 1 DB is connected the db connection information (dbURI or protocol/host/port) has to be passed EVERY time a new route is configured (or the first connection will be used)

| Option name | explanation | type | default | Memo | |--|--|--|--|--| | dbName | The name of the DB | string | none | Required | collectionName | The name of the collection| string | none | Required | dbURI | Full DB connection URI | string| none | Will overwrite protocol/host/port | | protocol | Protocol for DB connection | string | mongodb | | | host | Host for DB connection | string | localhost | | | port | Port for DB connection | int/string | 27017 | | | queryKey | The key that will be used when querying DB| string | id | | | id_type | The type of the (auto created) id | string | number | See id_type section below | | updateMethod | The operator to use when updating | string | $set | See update_method section below | | disableAction | Disable a specific METHOD from CRUD | array strings | [] | Possible values are: C/R/U/D | | validation | Concept: function to validate before C/U, didn't build it yet | Not sure yet | none | See validation below | | auth | concept: function to authorize the user before C/R/U/D, didn't build it yet | not sure yet | none | See authorization below | | authKeys | Other keys (from req.<key>) that will be sent to auth function | array - strings | none | | | parseQueries | parse queries in GET | boolean | true | See GET queries section Example 3 |

Future options:

  1. validation: already in the README, but not yet implemented
  2. auth: already in the README, but not yet implemented
  3. sendErrors: include in the response body the reason why 400/500 response code is being sent (this will include the query for GET)
  4. logErrors: log the reasson why 400/500 response code is being sent (this will include the query for GET)

Other options in thought:

  1. Advanced (hidden) security queries
    • Right now you can query for things using URL queries, (e.g. {a: "b"} by sending ?a=b), (see the GET queries section), but for quries that require middleware or need to be hidden (e.g. userAccess includes userId) URL queries won't work...

Advanced Variables explenation

id_type:

The type of ID that will be used when C (POST) is called, can be:
- number (default): a number starting at 1 and increasing for every doc
- random_number: a 16 digit random number
- random_string: a 16 digit random string
- skip: don't add an id automatically
If using skip - then something unique has to be sent in in the request body, and if the unique value key is not id then queryKey has to be updated to the correct key

updating queryKey will NOT change id to skip - id_type will continue to be set under id key, but will effectively be ignored (as R/U/D will use the queryKey to query the document)


update_method:

see MongoDB docs for more information about operators.
Can be set to false if no operator is to be used - AN OPERATOR IS REQUIRED - so this is useful if you want to include more then 1 operator (e.g. $inc 1 key and $set another key) - but these will have to be sent in request body.
If update_method is defined, the entire request body will be under the operator, for example - when $set is used (default): const updateData = { $set: req.body }


validation:

Still need to work this out.
More or less a function that will be called (C/U) with the Object - and the action will only continue if true is returned
But might be better to be a schema that is passed, and we call the validation ourselves (doing it this way might be a bit more complicated - but will allow us to respond with the reason validation failed, it'll also make it simpler for setup)


authorization:

Still need to work this out.
More or less it'll be a function that is called with certain param passed to it (action being performed, cookies, and other information from the request (see authKeys)
The function will decide according to the params given if the user is allowed to perform the action (C/R/U/D), if not return false and we'll send 403

If you want to use a middleware that adds req.user, or any other keys into the request, then authKeys can be used, For example: if authKeys: ['user'] when the auth function is called req.user (from the request) will be passed to it.


for thought:

  • What about the next() function - should we ever give an option to call that (e.g. if auth fails - maybe call next instead of returning 403)
    • In different words, are we the FULL handler, or can we be used a middleware (in which case we have to call next())
    • as of now we'll build it without it, but the thought exist if I ever want to look at it...

functions

All function names are based that automated-crud is imported on the variable automatedCrud,
const automatedCrud = require('automated-crud')

  1. Main function (automatedCrud())
    • Builds the actual routes
  2. connectDB
    • Connect a mongodb instance
    • Note: If you only connect 1 it'll be reused automatically for all routes, if you connect more then 1 you will have to say the dbURI (or protocol/host/port) and dbName every time you configure routes (or the 1st one will be used)