requrse

0.2.6 • Public • Published

reQurse

Lightweight driven query language

NPM Version requrse CI Coverage Status

What is reQurse

reQurse introduces an innovative approach that overcomes the complexities of CRUD operations. The focus is on delivering a streamlined and efficient CRUD library solution, simplifying API development, and effortlessly handling complex data management tasks. reQurse utilized JSON-based queries, allows multi-tenant API sources, avoid writing lengthy procedural APIs and truly embrace Javascript core philosophy as OOP language. This approach promotes a modular and streamlined code structure, retaining the complexity of Object tree while enhancing flexibility and maintainability.

Here's the first example to get you started. Try it here—no build step required!

This library take some inspirations from NextQL and GraphQL

Usage

A basic usage of reQurse.

import rq from 'requrse'

rq(query, { methods, config })
  • query: (object) required JSON like query.
  • methods: (object) required define methods/computed fields that exist in the query.
  • config: (object) optional extend and added parameterize control over methods.
await rq({
  Test: {
    test: {
      greeting: '*'
    }
  }
},
{
  methods: {
    greeting () {
      return 'hello world'
    }
  }
}).then(console.log, console.error)
// { Test: { test: { greeting: 'hello world' } } }

Advance usage

A proper query should do more, to demystify the capability of this library, create a few data samples, you can imagine this as a setup that your database may have.

const acolyte = { id: '0', progression: ['1', '4'], name: 'Acolyte' }
const priest = { id: '1', progression: ['4'], name: 'Priest' }
const squire = { id: '2', progression: ['3', '4'], name: 'Squire' }
const paladin = { id: '3', progression: ['4'], name: 'Paladin' }
const inquisitor = { id: '4', progression: [], name: 'Inquisitor' }

// we also create the relations between them
const supportData = {
  0: acolyte,
  1: priest,
  4: inquisitor
}

const vanguardData = {
  2: squire,
  3: paladin,
  4: inquisitor
}

Then the helper functions to access these data

/**
 * Helper function to get a class by ID.
 */
function getClass (id) {
  // Returning a promise just to illustrate query support.
  return Promise.resolve(supportData[id] || vanguardData[id])
}
/**
 * Allows us to query for a classes's progression.
 */
function getProgression (classes) {
  return classes.progression.map(id => getClass(id))
}
/**
 * Allows us to query for the support class with the given id.
 */
function getSupport (id) {
  return supportData[id]
}
/**
 * Allows us to query for the vanguard class with the given id.
 */
function getVanguard (id) {
  return vanguardData[id]
}
/**
 * Allows us to query for the player class by gameId.
 */
function getPlayer (gameId) {
  if (gameId === 0) {
    return acolyte
  }
  return inquisitor
}

Then configure requrse to use these methods

const confParams = {
  getPlayer, getClass, getProgression, getSupport, getVanguard
}

const config = (param) => confParams[param]

const methods = {
  player: 'getPlayer',
  class: 'getClass',
  progression: 'getProgression',
  support: 'getSupport',
  vanguard: 'getVanguard'
}

Simple usage

await rq({
  PlayerClass: {
    player: {
      name: 1
    }
  }
}, { methods, config }).then(console.log)
// { PlayerClass: { player: { name: 'Inquisitor' } } }

Use $params to filter result

await rq({
  PlayerClass: {
    player: {
      $params: { gameId: 0 },
      name: 1
    }
  }
}, { methods, config }).then(console.log)
// { PlayerClass: { player: { name: 'Acolyte' } } }

Optimize your query by writing efficient methods, i.e., here progression return the class next progression seamlessly

await rq({
  PlayerClass: {
    player: {
      $params: { gameId: 0 },
      id: 1,
      name: 1,
      progression: {
        name: 1
      }
    }
  }
}, { methods, config }).then(console.log)
// {
//   PlayerClass: {
//     player: {
//       id: '0',
//       name: 'Acolyte',
//       progression: [
//         { name: 'Priest' },
//         { name: 'Inquisitor' }
//       ]
//     }
//   }
// }

You can have multiple same dataset key name by / naming

await rq({
  vanguard: {
    'vanguard/paladin': {
      $params: { id: 3 },
      name: 1
    },
    'vanguard/inquisitor': {
      $params: { id: 4 },
      name: 1
    }
  }
}, { methods, config }).then(console.log)
// {
//   vanguard: {
//     'vanguard/paladin': { name: 'Paladin' },
//     'vanguard/inquisitor': { name: 'Inquisitor' }
//   }
// }

Now we expand the dataset to the inventory of the player

const healingPotion = { id: '0', effect: 'heal', dmg: 4, name: 'Healing Potion' }
const bandage = { id: '1', effect: 'heal', dmg: 1, name: 'Bandage' }
const holyWater = { id: '2', effect: 'cleansing', dmg: 2, name: 'Holy Water' }

// add relations to the inventory data
const itemData = {
  0: healingPotion,
  1: bandage,
  2: holyWater
}

// add relations to how many each class have these items in their inventory
const inventoryData = {
  0: [7, 1, 0],
  1: [3, 2, 2],
  2: [0, 5, 0],
  3: [1, 6, 2],
  4: [0, 0, 10]
}

Demonstrate usage of method/computed field to return value that you need, in this case count which came from a relational collection that store the value only, you can use such logic to build a powerful query for your api.

/**
 * Helper function to get a item by ID.
 */
function getItem (count, id) {
  // Returning a promise just to illustrate query support.
  return Promise.resolve({ ...itemData[id], count })
}

/**
 * Allows us to query for the player class inventoryData.
 */
function getInventory ({ id }) {
  return inventoryData[id].map(getItem)
}

Extends the requrse methods/config

const extConfig = {
  methods: {
    ...methods,
    item: 'getItem',
    inventory: 'getInventory'
  },
  config: (param) => ({
    ...confParams,
    getItem,
    getInventory
  })[param]
}

Now see how it perform!

await rq({
  PlayerClass: {
    player: {
      $params: { gameId: 0 },
      name: 1,
      inventory: {
        id: 1,
        name: 1,
        count: 1
      }
    }
  }
}, extConfig).then(console.log)
// {
//   PlayerClass: {
//     player: {
//       name: "Acolyte",
//       inventory: [
//         {
//           id: "0",
//           name: "Healing Potion",
//           count: 7
//         },
//         {
//           id: "1",
//           name: "Bandage",
//           count: 1
//         },
//         {
//           id: "2",
//           name: "Holy Water",
//           count: 0
//         }
//       ]
//     }
//   }
// }

More Samples

You can check samples folder to see more usage cases with Mongoose, Redis and the Starwars examples.

Package Sidebar

Install

npm i requrse

Weekly Downloads

2

Version

0.2.6

License

Apache-2.0

Unpacked Size

79 kB

Total Files

31

Last publish

Collaborators

  • syarul