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

@srtech/lucidql

v1.8.0

Published

Query language to allow client apps to use structured JSON to get granular control over data returned from an API powered by Adonis and LUCID

Downloads

4

Readme

lucidql

npm install @srtech/lucidql

About lucidql

lucidql allows you to query an AdonisJS Lucid backed database from a client using structured JSON in a post request. The principle is very simple and similar to GraphQL which is where my inspiration for lucidql came from

What is the benefit of lucidql

Data Structure

No need to setup and maintain your data structure, LUCID already knows all about this. Lucid also knows all about your table relationships, so getting related data is very simple.

Only get the data you need

lucidql allows you to select only the fields you want to see in both the main table and the related tables

Faimilar Knex syntact for filtering

If your already familier with KnexJS then wirting client queries for lucidql will be a breeze.

Easy to use with a simple POST request

Accessing your data from the client is as easy as making a POST request and passing some JSON to the server.

Getting Started

install lucidql

npm i @srtech/lucidql

setup your Adonis application and create models and a controller

  • Setup your models and relationships as you would normally. Nothing special here
  • Create a new controller, you can call it anything you want and set it up like in the example below, but referencing your own models

You controller might look something like this.

//App/Controllers/Http/LucidQlController.js

const lucidql = require('@srtech/lucidql')

const PurchaseOrder = use('App/Models/PurchaseOrder')
const PurchaseOrderLines = use('App/Models/PurchaseOrderLine')

const classes = {
	__proto__: null, // to avoid people being able to pass something like `toString`
  PurchaseOrder,
  PurchaseOrderLines
};

class LucidQlController {

  async query({ request }) {
    var { baseTable, query } = request.all()
    return lucidql.run(classes[baseTable], query)
  }
}

module.exports = LucidQlController

Note: Only Models that are imported and included in the classes object will be visible to lucidql as base tables for queries.

setup a new route in App/start/routes.js

Route.post('/api/lucidql', 'LucidQlController.query')

Querying data

Simply point your client i.e Axios or Postman etc at your route and send a structured JSON object to request your data.

The structure of the JSON object you send needs to be in the following format

{
  "baseTable": "PurchaseOrder",
  "query": {
    "where": [
      {
        "field": "PurchaseOrder", 
        "value":"POC001460",
        "op":"=="
      }
    ],
    "fields": ["PurchaseOrder", "ShipFromAddress", "Currency"],
    "with": [
      {
        "table": "lines",
        "fields": ["PurchaseOrder", "Position", "Item", "Price"],
        "where": [
          {
            "field": "Position",
            "value": [5, 8],
            "op": "between"
           }	
        ]
      }
    ]
  }
}

The above query is the same as running the following LUCID query in your controller.

return await PurchaseOrder.query().where('PurchaseOrder', "POC001460")
    .with('lines', (builder)=>{
      builder.whereBetween('Position', [5, 9]).select(["PurchaseOrder", "Position", "Item", "Price"])
    })
    .fetch()

baseTable

Accepts a String This should match of the models you specified in the classes object you created in your controller

query

Accepts a JSON object Although the query property is required, it can be left empty. For example, this will return all records from the PurchaseOrder table.

{
  "baseTable": "PurchaseOrder",
  "query": {}
}

The query property accepts these child properties

  • fields
  • where
  • order
  • paginate
  • aggregate
  • with
  • withCount
  • limit

fields

Accepts an Array of Strings

This is the same as using .select(...) in a normal LUCID query. It is the fields that you want to include from your baseTable.

important In order for relations to work using the with property (see below) you must include the fields that form the foreign key relationship, otherwise LUCID will not be able to make the relationship.

where

Accepts an Array of Objects

The where property that is a direct child of "query" will filter the records from your base table, the above example will restrict the base table to returning one record where PurchaseOrder == 'POC001460'

the structure of each condition is setup as follows

{
  "field": <string>,
  "value": <string, int, float, object, array>,
  "op": (many options that mostly map to Knex operators. See below for more details)
}

You can add multiple conditions by simply adding more objects to the array.

order

Accepts an Array of Objects

The order property allows you to order your query. It is an array of objects, each object containing the properties

  • field
  • direction

If direction is omitted it defaults to ASC

"order": [
  {
    "field": <string>,
    "direction": <"ASC":"DESC"> 
  }
]

paginate

Accepts an object

"paginate": {
  "page": <integer>,
  "perPage": <integer>
}

Using page will return a different format of results as it includes details about the pagination. The object that is returned looks like this

{
  total: '',
  perPage: '',
  lastPage: '',
  page: '',
  data: [{...}]
}

Your data is included in the data property, but the other properties give you useful information that allow you to easily build your pagination logic.

aggregate

Accepts an Object

"aggregate": {
  "field": <String>,
  "function": <String>
}

The function property should be any one of those supported by Lucid and documented here https://adonisjs.com/docs/4.1/query-builder#_aggregate_helpers

Examples of aggregate functions you can use are

  • getCount
  • getCountDistinct
  • getMin
  • getMax
  • getSum
  • getSumDistinct
  • getAvg
  • getAvgDistinct

with

Accepts an array of Objects

Just as if you were using LUCID directly in your controller, with allows you to join relations to your baseTable

The structure of with is an array of objects, with each object describing filters and fields to include.

"with": [
  {
    "table": "lines",
    "fields": ["PurchaseOrder", "Position", "Item", "Price"],
    "where": [
      {
        "field": "Position",
        "value": [5, 8],
        "op": "between"
      }	
    ]
  }
]

table Accepts a String this must match the name of the relation you have setup in your model. i.e.

lines() {
  return this.hasMany('App/Models/PurchaseOrderLine', 'PurchaseOrder', 'PurchaseOrder')
}

in the case above you can see that lines is the name of our relation in the model and it is what was used to name the relation in the JSON object.

You can add multiple relationships by simplying adding more objects to the array.

fields - Optional Accepts an Array of Strings

Works identically to the how the fields property is described above for the baseTable, but will limit the fields returned from your relationship, Important You must include in the list the field that forms part of the foreign key relationship

If you ommit the fields property, all fields will be returned for this relationship

order Just as with the baseQuery you can order the results of the related data. It is described in more detail above.

where

Accepts an Array of Objects

Each object in this array is used to filter the results from related table. It works in exactly the same way as described above for the where property for the baseTable.

where op

As mentioned above a where property consists of an Array of Objects, each object describing a where condition, each of these objects must have an op property. Below is a list of the supported op's

==

"where": [
  {
    "field": "Position",
    "value": 8,
    "op": "=="
  }	
]

Would return rows where Position is equal to 8

<> / !=

"where": [
  {
    "field": "Position",
    "value": 8,
    "op": "!="
  }	
]

Would return rows where Position is not equal to 8

<

"where": [
  {
    "field": "Position",
    "value": 8,
    "op": "<"
  }	
]

Would return rows where Position is less than 8

>

"where": [
  {
    "field": "Position",
    "value": 8,
    "op": ">"
  }	
]

Would return rows where Position is greater than 8

between

"where": [
  {
    "field": "Position",
    "value": [4, 8],
    "op": "between"
  }	
]

Would return rows where Position between 4 and 8

notBetween

"where": [
  {
    "field": "Position",
    "value": [4, 8],
    "op": "notBetween"
  }	
]

Would return rows where Position is not between 4 and 8

like

"like": [
  {
    "field": "Company",
    "value": "%widgets",
    "op": "like"
  }	
]

Would return any rows where the Company field ends in widgets

in

"where": [
  {
    "field": "Position",
    "value": [4, 5, 6, 7, 8],
    "op": "in"
  }	
]

Would return rows where Position is one of 4 or 5 or 6 or 7 or 8

notIn

"where": [
  {
    "field": "Position",
    "value": [4, 5, 6, 7, 8],
    "op": "notIn"
  }	
]

Would return rows where Position is NOT one of 4 or 5 or 6 or 7 or 8

not

Using not the field property can be omitted

"where": [
  {
    "value": {
      "Position": 8,
      "Currency": "USD"
    },
    "op": "not"
  }	
]

Would return rows where Position is not 8 and Currency is not "USD"

null

Using null the value property can be omitted

"where": [
  {
    "field": "Position",
    "op": "null"
  }	
]

Would return rows where Position is null

notNull

Using notNull the value property can be omitted

"where": [
  {
    "field": "Position",
    "op": "notNull"
  }	
]

Would return rows where Position is not null

withCount

Accepts an Array of Objects Lucid's own documentation for this feature can be found here https://adonisjs.com/docs/4.0/relationships#_counts

"withCount": [
  {
    "table": <String>
  }
]

The data returned will include a __meta__ property with a count of the records that are related to the baseTable

//The following
"withCount": [
  {
    "table": "tags"
  }
]

//Would produce
{
  ...
  "__meta__": {
    "tags_count": 1
  }
}

limit

Accepts an Object

limit will limit the number of records that Lucid returns to you.

The object should contain only a single property qty with a numeric value

The code below would tell Lucid to limit the number of records returned from baseTable to 3.

limit: { qty: 3}