LUCIDQL - Need help with code

#1

I really like the idea of GraphQL but I think it has too many issues and adds so much more work to setup your data sources.

I had the idea however that LUCID could do almost the same thing very easily and given a structured JSON post you could have a single end-point with very little code to query your whole database.

I have already written something that works for basic queries

//routes.js
Route.post('/api/lucidql', 'LucidQLController.index')
LucidQLController.js
'use strict'

const itemsGeneral = use('App/Models/ItemsGeneral')
const businessPartner = use('App/Models/BusinessPartner')
const buyFromBP = use('App/Models/BuyFromBp')
const plannedOrder = use('App/Models/PlannedOrder')

const classes = {
  __proto__: null, // to avoid people being able to pass something like `toString`
  itemsGeneral,
  businessPartner,
  buyFromBP,
  plannedOrder
};

class LucidQlController {
  async index({ request }) {
    
    var query = request.all()
    var baseTable = Object.keys(query)[0]
    
    var queryOptions = query[baseTable]
    var baseQuery = classes[baseTable].query()

    if(queryOptions.hasOwnProperty('fields')){
      for(var f of queryOptions.fields){
        baseQuery.select(`${f}`)
      }
    }

    if(queryOptions.hasOwnProperty('where')){
      for(var w of queryOptions.where){
        baseQuery.where(`${Object.keys(w)[0]}`, `${Object.values(w)[0]}`)
      }
    }

    if(queryOptions.hasOwnProperty('with')){
      
      for(var w of queryOptions.with){
        console.log(w.fields)
        baseQuery.with(w.table, (builder)=>{
          console.log(w.fields)
          if(w.hasOwnProperty('where')){
            for(var v of w.where){
              builder.where(`${Object.keys(v)[0]}`, `${Object.values(v)[0]}`)
            }
          }

          let selectFields = ''
          let count = 0
          
          for(var f of w.fields){
            selectFields += `'${f}'`
            count ++
            if(w.fields.length > count){
              selectFields += ','
            }
          }
          builder.select(selectFields)
        })
      }
    }
    
    return await baseQuery.fetch()
  }
}

module.exports = LucidQlController

All the models are setup in the normal way.

You can then pass in a query as a post request like so

{
	"plannedOrder": {
		"where": [
				{"OrderItem": "1234"}
			],
			"fields": ["OrderItem", "BuyFromBP"],
		
		"with": [
			{
				"table": "itemsGeneral",
				"fields": ["Item", "Description"]
			}
		]
	}
}

The great thing is this will produce only a single request where as GraphQL would produce N+1 for each row from itemsGeneral.

My problem comes if I try to add another table to “with”. It will work just fine without “fields”. Like this

This works

{
	"plannedOrder": {
		"where": [
				{"OrderItem": "1234"}
			],
			"fields": ["OrderItem", "BuyFromBP"],
		
		"with": [
			{
				"table": "itemsGeneral"
			},
			{
				"table": "businessPartner"
			}
		]
	}
}

but this does not work

{
	"plannedOrder": {
		"where": [
				{"OrderItem": "1234"}
			],
			"fields": ["OrderItem", "BuyFromBP"],
		
		"with": [
			{
				"table": "itemsGeneral",
				"fields": ["Item", "Description"]
			},
			{
				"table": "businessPartner",
				"fields": ["BusinessPartner", "Name"]
			}
		]
	}
}

What seems to happen is that this piece of code loops over the fields of the last entry twice, trying to select BusinessPartner and Name from itemsGeneral

 if(w.hasOwnProperty('fields')){
            for(var f of w.fields){
              builder.select(`${f}`)
            }
          }

If anyone would like to work on this with me, I think this could be really powerful. One end point allows you to access your entire data source and gives the client full control over which fields to select just like GraphQL but without the downfall of GraphQL.

I think with some work a client could be setup to introspect the fields like GraphiQL does.

0 Likes

GraphQL and Adonis project
AdonisJS + GraphQL
LUCID .with as a promise