Code Monkey home page Code Monkey logo

objection-graphql's Introduction

objection-graphql

Automatic GraphQL API generator for objection.js models.

Usage

objection-graphql automatically generates a GraphQL schema for objection.js models. The schema is created based on the jsonSchema and relationMappings properties of the models. It creates a rich set of filter arguments for the relations and provides a simple way to add custom filters.

The following example creates a schema for three models Person, Movie and Review and executes a GraphQL query:

const graphql = require('graphql').graphql;
const graphQlBuilder = require('objection-graphql').builder;

// Objection.js models.
const Movie = require('./models/Movie');
const Person = require('./models/Person');
const Review = require('./models/Review');

// This is all you need to do to generate the schema.
const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person)
  .model(Review)
  .build();

// Or: 
// const models = [Movie, Person, Review]
// const graphQlSchema = graphQlBuilder().allModels(models).build();

// Execute a GraphQL query.
graphql(graphQlSchema, `{
  movies(nameLike: "%erminato%", range: [0, 2], orderBy: releaseDate) {
    name,
    releaseDate,
    
    actors(gender: Male, ageLte: 100, orderBy: firstName) {
      id
      firstName,
      age
    }
    
    reviews(starsIn: [3, 4, 5], orderByDesc: stars) {
      title,
      text,
      stars,
      
      reviewer {
        firstName
      }
    }
  }
}`).then(result => {
  console.log(result.data.movies);
});

The example query used some of the many default filter arguments. For example the nameLike: "%erminato%" filter is mapped into a where clause where name like '%erminato%'. Similarily the ageLte: 100 is mapped into a where age <= 100 clause. In addition to the property filters there are some special arguments like orderBy and range. Check out this table for a full list of filter arguments available by default.

Getting started

If you are already using objection.js the example in the usage section is all you need to get started. If you are unfamiliar with objection.js you should try our example project.

Filters

argument type action
prop: value property type prop = value
propEq: value property type prop = value
propGt: value property type prop > value
propGte: value property type prop >= value
propLt: value property type prop < value
propLte: value property type prop <= value
propLike: value string prop LIKE value
propIsNull: value boolean prop IS NULL or prop IS NOT NULL
propIn: value Array prop IN value
propNotIn: value Array prop NOT IN value
propLikeNoCase: value string lower(prop) LIKE lower(value)

Special arguments

argument action
orderBy: prop Order the result by some property
orderByDesc: prop Order the result by some property in descending order
range: [start, end] Select a range. Doesn't work for relations!
limit: prop Select a given number of records.
offset: prop Skip a given number of records.

Adding your own custom arguments

Here's an example how you could implement a NotEq filter for primitive values:

const graphql = require('graphql');

const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person)
  .model(Review)
  .argFactory((fields, modelClass) => {
    const args = {};

    _.forOwn(fields, (field, propName) => {
      // Skip all non primitive fields.
      if (field.type instanceof graphql.GraphQLObjectType 
          || field.type instanceof graphql.GraphQLList) {
        return;
      }
    
      args[propName + 'NotEq'] = {
        // For our filter the type of the value needs to be 
        // the same as the type of the field.
        type: field.type,
        
        query: (query, value) => {
          // query is an objection.js QueryBuilder instance.
          query.where(propName, '<>', value);
        }
      };
    });

    return args;
  })
  .build();

Extending your schema with mutations

Often you need to provide mutations in your GraphQL schema. At the same time mutations can be quite opinionated with side effects and complex business logic, so plain CUD implementation is not always a good idea. Therefore we provide a method extendWithMutations which allows you to extend the generated query schema with mutations. You can provide a root GraphQLObjectType or a function as a first argument for this method. Function in this case plays as a strategy which receives current builder as a first argument and returns GraphQLObjectType.

//...
const personType = new GraphQLObjectType({
    name: 'PersonType',
    description: 'Use this object to create new person',
    fields: () => ({
      id: {
        type: new GraphQLNonNull(GraphQLInt),
        description: 'First Name',
      },
      firstName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'First Name',
      },
      lastName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'Last Name',
      },
    }),
});

const createPersonInputType = new GraphQLInputObjectType({
    name: 'CreatePersonType',
    description: 'Person',
    fields: () => ({
      firstName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'First Name',
      },
      lastName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'Last Name',
      },
    }),
});
    
const mutationType = new GraphQLObjectType({
    name: 'RootMutationType',
    description: 'Domain API actions',
    fields: () => ({
      createPerson: {
        description: 'Creates a new person',
        type: personType,
        args: {
          input: { type: new GraphQLNonNull(createPersonInputType) },
        },
        resolve: (root, inputPerson) => {
          const { firstName, lastName } = inputPerson.input;
          return {
              id: 1,
              firstName,
              lastName,
          };
        },
      },
    }),
});

//Here you can use a GraphQLObjectType or function as an argument for extendWithMutations
schema = mainModule
  .builder()
  .model(Person)
  .extendWithMutations(mutationType)
  .build();    

Extending your schema with subscriptions

When you want to implement a real-time behavior in your app like push notifications, you basically have two options in graphql: subscriptions and live queries. The first approach is focused on events and granular control over updates, while the other is based on smart live queries, where most of real-rime magic is hidden from the client. We'd like to stick with the first approach since there are some decent implementations out there like graphql-subscriptions by Apollo.

The implementation is similar to mutations extention point: you've got an extendWithSubscriptions method where you can pass the root GraphQLObjectType or a function which can bahave as a strategy which receives current builder as an argument.

//...
import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();
//...
const personType = new GraphQLObjectType({
    name: 'PersonType',
    description: 'Person',
    fields: () => ({
      id: {
        type: new GraphQLNonNull(GraphQLInt),
        description: 'First Name',
      },
      firstName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'First Name',
      },
      lastName: {
        type: new GraphQLNonNull(GraphQLString),
        description: 'Last Name',
      },
    }),
});

const subscriptionType = new GraphQLObjectType({
    name: 'RootSubscriptionType',
    description: 'Domain subscriptions',
    fields: () => ({
      personCreated: {
        description: 'A new person created',
        type: personType,
        resolve: (payload: any) => payload,
        subscribe: () => pubsub.asyncIterator('PERSON_CREATED'),
      },
    }),
});

//Here you can use a GraphQLObjectType or function as an argument for extendWithSubscriptions
schema = mainModule
  .builder()
  .model(Person)
  .extendWithSubscriptions(subscriptionType)
  .build();  

Misc

defaultArgNames

You can change the default filter suffixes and special filter names using the defaultArgNames method:

const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person)
  .model(Review)
  .defaultArgNames({
    eq: '_eq',
    gt: '_gt',
    gte: '_gte',
    lt: '_lt',
    lte: '_lte',
    like: '_like',
    isNull: '_is_null',
    likeNoCase: '_like_no_case',
    in: '_in',
    notIn: '_not_in',
    orderBy: 'order_by',
    orderByDesc: 'order_by_desc',
    range: 'range',
    limit: 'limit',
    offset: 'offset'
  })
  .build();

Now you would have myProp_lt: value instead of the default myPropLt: value.

By default the model names are pluralized by adding an s to the end of the camelized table name. You can set a custom plural and singular names for the root fields like so:

const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person, {
    listFieldName: 'people',
    fieldName: 'person'
  })
  .model(Review)

onQuery

You can modify the root query by passing an object with onQuery method as the third argument for graphql method:

const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person)
  .model(Review)
  .build();

expressApp.get('/graphql', (req, res, next) => {
  graphql(graphQlSchema, req.query.graph, {
    // builder is an objection.js query builder.
    onQuery(builder) {
      // You can for example store the the logged in user to builder context
      // so that it can be accessed from model hooks.
      builder.mergeContext({
        user: req.user
      });
      
      // Or change the eager fetching algorithm.
      builder.eagerAlgorithm(Model.JoinEagerAlgorithm);
    }
  }).then(result => {
    res.send(result);
  }).catch(err => {
    next(err);
  });
});

setBuilderOptions

Allows you to customize Objection query builder behavior. For instance, you can pass { skipUndefined: true } as an options argument. So, each time the builder is called, it will be called with skipUndefined enabled. This can be useful when you use graphql-tools schema stitching.

objection-graphql's People

Contributors

devel-pa avatar koskimas avatar mattleff avatar maxwellsmart84 avatar nasushkov avatar paulbjensen avatar sbabushkin avatar timhuff avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

objection-graphql's Issues

Correct method for passing "orderBy:" as a variable?

I'm trying to pass in the "orderBy:" parameter as a variable in the GraphiQL client, but I'm not sure how:

query getRegions ($order: String) {
  iwRegions (range: [0, 4], orderBy: $order) {
    name
  }
}

with variables:

{
  "order": "name"
}

but I get an error message:

{
  "errors": [
    {
      "message": "Variable \"$order\" of type \"String\" used in position expecting type \"IwRegionPropertiesEnum\".",
      "locations": [
        {
          "line": 1,
          "column": 19
        },
        {
          "line": 2,
          "column": 38
        }
      ]
    }
  ]
}

How should I do this?

Thanks,
Adrian

Tests don't pass for MySQL?

On MySQL, the table creation does not happen successfully (because of the impossibly stupid handling of foreign keys in MySQL).

I had to modify the 'createTables' class as follows

`

  createTables() {
    const knex = this.knex;

    return knex.schema.dropTableIfExists('Person_Movie')	// changed order of dropping for MySQL
    .then(() => {
      return knex.schema.dropTableIfExists('Review');
    }).then(() => {
      return knex.schema.dropTableIfExists('Person');
    }).then(() => {
      return knex.schema.dropTableIfExists('Movie');
    }).then(() => {
      return knex.schema.createTable('Movie', (table) => {
        table.increments('id').primary().unsigned();
        table.string('name');
        table.date('release_date');
      });
    }).then(() => {
      return knex.schema.createTable('Person', (table) => {
        table.increments('id').primary().unsigned();
        table.string('firstName');
        table.string('lastName');
        table.enum('gender', _.values(models.Person.Gender));
        table.integer('age');
        table.json('addresses', true);
        table.integer('parentId')
          .unsigned()
          .nullable()
          .defaultTo(0)
//          .references('id')		/* delay creation of foreign key for mysql  */
//          .inTable('Person')
          .index();
      });
    }).then(() => {
        return knex.schema
           .raw('alter table Person add CONSTRAINT FOREIGN KEY (`parentId`) REFERENCES `Person` (`id`)');
    }).then(() => {
      return knex.schema.createTable('Review', (table) => {
        table.increments('id').primary().unsigned();
        table.string('title');
        table.integer('stars');
        table.string('text');
        table.integer('movieId')
          .unsigned()
          .nullable()
          .references('id')
          .inTable('Movie')
          .index();
        table.integer('reviewerId')
          .unsigned()
          .nullable()
          .references('id')
          .inTable('Person')
          .index();
      });
    }).then(() => {
      return knex.schema.createTable('Person_Movie', (table) => {
        table.increments('id').primary().unsigned().index();
        table.integer('movieId')
          .unsigned()
          .nullable()
          .references('id')
          .inTable('Movie')
          .index();
        table.integer('personId')
          .unsigned()
          .nullable()
          .references('id')
          .inTable('Person')
          .index();
      });
    });
  }
`
Even after this, though, there were many failures, I have attached a zip file with a log of the errors, please see attached
[test_errors.zip](https://github.com/Vincit/objection-graphql/files/1417732/test_errors.zip)

Is this project still maintained?

There were some PRs in December regarding mutations and no feedback by now. I just wonder if this project is still actively maintained or I should fork it. Thanks!

How to handle jsonSchema relations?

Given a many to many scenario of tags/posts, what is the right approach to complete the jsonSchema for properties tags and posts without causing circular references or unnecessary repetition:

// tag.js
class Tag extends Model {
    static get jsonSchema () {
        return {
            type: 'object',
            properties: {
                id: { type: 'integer' },
                name: { type: 'string' },
                posts: {
                    // what goes here???
                }
            },
        };
    }
}

// post.js
class Post extends Model {
    static getjsonSchema () {
        return {
            type: 'object',
            properties: {
                id: { type: 'integer' },
                title: { type: 'string' },
                tags: {
                    // what goes here???
                }
            },
        };
    }
}

Fine grain access control support ?

Hi
Is there a way to restrict the generated schema to a single root type like User ?

Here is why:
I have Users and Accounts.

const graphQlSchema = graphQlBuilder()
  .model(User)
  .model(Account)
  .build();

But I don't want API users to be able to query all accounts. I only want users to query accounts that they are allowed to see.

The way I see that could be done is to restrict the GraphQL schema to the User type only and not expose the Account type on root level. Then the only way to query accounts would be through the accounts property on the User type. This restricts the accounts one could query to that particular user.

There is still a flaw in that you can still query all users and so get to all accounts.

Is there a way to completely restrict the query to the user that makes the response ? (the user id comes with the response via some server authentication mechanism)

extendWithMutations function takes the last mutation passed always

Hello there! I am trying to create multiple mutations but the function of extendWithMutations only takes the last mutation that I enter as a parameter.

This is my SchemaBuilder

const schemaBuilder = graphQlBuilder().allModels(models);

extendAllMutations(schemaBuilder);

const schema = schemaBuilder.build();

extendAllMutations is a personal function that I created for all mutations to be added to the variable schemaBuilder.

This is the extendAllMutations function:

const extendAllMutations = builder => {
  getMutations().forEach(mutation => builder.extendWithMutations(mutation));
};

getMutations is a function that reads the mutations from a folder that I have all the files that contain the different mutations that I want to implement on the server

I have tried introducing the models one by one and using twice the function spreadWithMutations on that same model and exactly the same thing happens. I think I should let you add all the mutations instead of replacing them.

const schema = graphQlBuilder()
   .model(user)
   .extendWithMutations(loginMutation)
   .extendWithMutations(signupMutation)
   .build();

I have not seen in the documentation anything about it, so I better ask around here. Thank you for your attention and forgive the inconvenience that may cause.

Allow returning GraphQLID type for id fields

We are using objection-graphql in our API server with Relay on the client side. Relay encourages a global ID for each graph object (Docs & Spec). This aids in object identification for caching, as well as cache invalidation and refreshing. The GraphQL Spec states that an ID should be a string. This prevents simply using an id column from the models, at least without type conversion. Additionally, since the ID needs to be globally unique it would need to include the model name/class (i.e., 1Movie, 2Person) to ensure that for a given database id number the generated GraphQL ID is unique.

I'm wondering if you would be open to either of the following solutions:

  1. Making a special case for fields named id that would return the GraphQLID type.
  2. Making an optional method to override the type for a property, something like:
const graphQlSchema = graphQlBuilder()
  .model(Movie)
  .model(Person)
  .model(Review)
  .overridePropertyType( (modelClass, propName, defaultType ) {
    return propName === 'id' ? GraphQLID : defaultType;
  } )
  .build();

Use virtualAttributes for GraphQL schema

Hi there,

I can't find any way to get around the following.

I've got a Model ("Exchange") with a db column ("name") and a virtual attribute ("marketCount"):

class Exchange extends Model {
  static get tableName () {
    return 'exchanges';
  }
  
  static get virtualAttributes() {
    return ['marketCount'];
  }
  
  
  static get jsonSchema () {
    return {
      type: 'object',
      required: ['name'],
      properties: {
        id: {type: 'integer'},
        name: {type: 'string'},
        // marketCount: {type: 'number' }
      }
    };
  }
  
  marketCount () {
    return 1;
  }
}

When defining "marketCount" in jsonSchema properties it tries to select the column from the db, which of course doesn't exist and when leaving it out it isn't available in the GraphQL Schema, so the query fails.

I find it hard to believe this wouldn't be possible but didn't find the answer either.

Anything I don't know of?

Relationship class not being recognized on a Model during `build()`

Hi,

I have encountered a problem that I can't fathom. Please, if anyone can see what I did wrong, I would appreciate the help greatly.

I have two models, CvDevice and CvAccessMap, with a 1:M relation between them, as cv_device.id = cv_access_map.device_id

When I call build(), I get the following error:
Error: relation type "BelongsToOneRelation" is not supported in SchemaBuilder.js, in function

 _relationField(modelData, relation) {
    if (relation instanceof objection.HasOneRelation
      || relation instanceof objection.BelongsToOneRelation
      || relation instanceof objection.HasOneThroughRelation) {
      return {
        type: this._typeForModel(modelData),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS),
      }
    } else if (relation instanceof objection.HasManyRelation || relation instanceof objection.ManyToManyRelation) {
      return {
        type: new GraphQLList(this._typeForModel(modelData)),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS),
      }
    }
    throw new Error(`relation type "${relation.constructor.name}" is not supported on ${modelData.modelClass.tableName}`)
  }

The two models look fine, I really can't see what could be causing this. I have tried several things, but it seems like the relation type is not correctly assigned a Model relation class as expected.

This was previously working with objection: ^0.9.4 and objection-graphql: ^0.2.3.
I've recently upgraded to objection: ^1.3.0 and objection-graphql: ^0.4.2.

Obviously something substantial has changed, but I can't see what.

Any and all help will be appreciated!

Here is the code for the build():

...
const graphQlBuilder = require('./objection-graphql').builder
const objection = require('objection')

// Initialize knex connection.
const knex = require('knex')({ 
  client: 'mysql',
  connection: {
    host: 'localhost',
    user: 'xxxxxxx',
    password: 'xxxxxxx',
    database: 'icam',
  },
})

const Model = objection.Model

// Give the connection to objection.
Model.knex(knex)

import { CvAccessMapBaseModel } from './schema/objection/base/CvAccessMapBaseModel.js'
import { CvDeviceBaseModel } from './schema/objection/base/CvDeviceBaseModel'

const graphQlSchema = graphQlBuilder()
  .model(CvAccessMapBaseModel)
  .model(CvDeviceBaseModel)
  .build()

and the two models:

// file: CvAccessMapBaseModel.js
const { Model } = require('objection')

export class CvAccessMapBaseModel extends Model {

  // Table name is the only required property.
  static get tableName() {
    return 'cv_access_map'
  }

  static get jsonSchema () {
    return {
      type: 'object',
      required: [
      ],

      properties: {
        id: {type: 'integer'},            
        user_id: {type: 'integer'},            
        group_id: {type: 'integer'},            
        device_id: {type: 'integer'},            
        location_id: {type: 'integer'},            
        b_excluded: {type: 'boolean'}
      }
    }
  }

  // This object defines the relations to other models.
  static get relationMappings() {

    return {

      // Parent Relations    
            
      cvDevice: {
        relation: Model.BelongsToOneRelation,   // **** Fails to recognize this assignment when build() is called ****
        modelClass: __dirname + '/CvDeviceBaseModel',
        join: {
          from: 'cv_access_map.device_id',
          to: 'cv_device.id'
        }
      },
    }
  }
}

and

// file: CvDeviceBaseModel.js

const { Model } = require('objection')

export class CvDeviceBaseModel extends Model {

  // Table name is the only required property.
  static get tableName() {
    return 'cv_device'
  }

    static get jsonSchema () {
    return {
      type: 'object',
      required: [

        'client_id',
        'location_id',
        'device_uid',
        'name',
        'sort_order',
        'device_type',
        'user_id'
      ],

      properties: {
            
        id: {type: 'integer'},            
        client_id: {type: 'integer'},            
        location_id: {type: 'integer'},            
        device_uid: {type: 'string'},            
        imei: {type: 'string'},            
        name: {type: 'string'},            
        sort_order: {type: 'number'},            
        placed_on: {type: 'string'},            
        device_type: {type: 'string'},            
        user_id: {type: 'integer'}
      }
    }
  }

  // This object defines the relations to other models.
  static get relationMappings() {

    return {

      cvAccessMaps: {
        relation: Model.HasManyRelation,
        modelClass: __dirname + '/CvAccessMapBaseModel',
        join: {
          from: 'cv_access_map.device_id',
          to: 'cv_device.id'                
        }    
      },
    }
  }
}

Thanks in advance,
Adrian

`Cannot read property '$toJson' of undefined` when no results returned

  knex:client acquired connection from pool: __knexUid1 +12s
  knex:query select "user"."id" from "user" +12s
--------------------
query:
  __knexUid:       __knexUid1
  method:          select
  options:
  timeout:         false
  cancelOnTimeout: false
  bindings:
    (empty array)
  __knexQueryUid:  fe9f1ebd-5a2a-42da-8f02-a4929e4d7626
  toNative:        function() {}
  sql:             select "user"."id" from "user"
--------------------
  knex:bindings [] +12s
  knex:client releasing connection to pool: __knexUid1 +1ms
{ result: undefined }
{ TypeError: Cannot read property '$toJson' of undefined

result is null here.

function toJson(result) {
  if (_.isArray(result)) {
    for (let i = 0, l = result.length; i < l; ++i) {
      result[i] = result[i].$toJson();
    }
  } else {
    result = result.$toJson();
  }

  return result;
}

extras in relationShipMappings not supported ?

Hi this documention says there can be extra columns in relationship tables - and they get mapped into the related models.

But its seems I can't use them in GraphQL Query? Is this possible, or can you please add it?

I also want to be able to provide a parameter to filter on that extra column.

graphql(graphQlSchema, `{
  users {
    id
    name
    accounts {
      id
      name
      somethingExtra
    }
  }
const {Model} = require('objection');

class User extends Model {
    static get tableName() {
        return 'user';
    }

    static get jsonSchema() {
        return {
            type: 'object',
            required: ['name'],

            properties: {
                id: {type: 'integer'},
                name: {type: 'string', minLength: 1, maxLength: 255},
            },
        };
    }

    static get relationMappings() {
        const Account = require('./account');

        return {

            accounts: {
                relation: Model.ManyToManyRelation,
                modelClass: Account,
                join: {
                    from: 'user.id',
                    through: {
                        from: 'user_account.user_id',
                        to: 'user_account.account_id',
                        extra: ['somethingExtra']
                    },
                    to: 'account.id',
                },
            },

        };
    }
}

module.exports = User;

Problems with `build()`, relation class in Models are not being recognized

Hi,

I have encountered a problem that I can't fathom. Please, if anyone can see what I did wrong, I would appreciate the help greatly.

I have two models, CvDevice and CvAccessMap, with a 1:M relation between them, as cv_device.id = cv_access_map.device_id

When I call build(), I get the following error:
Error: relation type "BelongsToOneRelation" is not supported in SchemaBuilder.js, in function

 _relationField(modelData, relation) {
    if (relation instanceof objection.HasOneRelation
      || relation instanceof objection.BelongsToOneRelation
      || relation instanceof objection.HasOneThroughRelation) {
      return {
        type: this._typeForModel(modelData),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS),
      }
    } else if (relation instanceof objection.HasManyRelation || relation instanceof objection.ManyToManyRelation) {
      return {
        type: new GraphQLList(this._typeForModel(modelData)),
        args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS),
      }
    }
    throw new Error(`relation type "${relation.constructor.name}" is not supported on ${modelData.modelClass.tableName}`)
  }

The two models look fine, I really can't see what could be causing this. I have tried several things, but it seems like the relation type is not correctly assigned a Model relation class as expected.

This was previously working with objection: ^0.9.4 and objection-graphql: ^0.2.3.
I've recently upgraded to objection: ^1.3.0 and objection-graphql: ^0.4.2.

Obviously something substantial has changed, but I can't see what.

Any and all help will be appreciated!

Here is the code for the build():

...
const graphQlBuilder = require('./objection-graphql').builder
const objection = require('objection')

// Initialize knex connection.
const knex = require('knex')({ 
  client: 'mysql',
  connection: {
    host: 'localhost',
    user: 'xxxxxxx',
    password: 'xxxxxxx',
    database: 'icam',
  },
})

const Model = objection.Model

// Give the connection to objection.
Model.knex(knex)

import { CvAccessMapBaseModel } from './schema/objection/base/CvAccessMapBaseModel.js'
import { CvDeviceBaseModel } from './schema/objection/base/CvDeviceBaseModel'

const graphQlSchema = graphQlBuilder()
  .model(CvAccessMapBaseModel)
  .model(CvDeviceBaseModel)
  .build()

and the two models:

// file: CvAccessMapBaseModel.js
const { Model } = require('objection')

export class CvAccessMapBaseModel extends Model {

  // Table name is the only required property.
  static get tableName() {
    return 'cv_access_map'
  }

  static get jsonSchema () {
    return {
      type: 'object',
      required: [
      ],

      properties: {
        id: {type: 'integer'},            
        user_id: {type: 'integer'},            
        group_id: {type: 'integer'},            
        device_id: {type: 'integer'},            
        location_id: {type: 'integer'},            
        b_excluded: {type: 'boolean'}
      }
    }
  }

  // This object defines the relations to other models.
  static get relationMappings() {

    return {

      // Parent Relations    
            
      cvDevice: {
        relation: Model.BelongsToOneRelation,   // **** Fails to recognize this assignment when build() is called ****
        modelClass: __dirname + '/CvDeviceBaseModel',
        join: {
          from: 'cv_access_map.device_id',
          to: 'cv_device.id'
        }
      },
    }
  }
}

and

// file: CvDeviceBaseModel.js

import {Model} from 'objection'

export class CvDeviceBaseModel extends Model {

  // Table name is the only required property.
  static get tableName() {
    return 'cv_device'
  }

    static get jsonSchema () {
    return {
      type: 'object',
      required: [

        'client_id',
        'location_id',
        'device_uid',
        'name',
        'sort_order',
        'device_type',
        'user_id'
      ],

      properties: {
            
        id: {type: 'integer'},            
        client_id: {type: 'integer'},            
        location_id: {type: 'integer'},            
        device_uid: {type: 'string'},            
        imei: {type: 'string'},            
        name: {type: 'string'},            
        sort_order: {type: 'number'},            
        placed_on: {type: 'string'},            
        device_type: {type: 'string'},            
        user_id: {type: 'integer'}
      }
    }
  }

  // This object defines the relations to other models.
  static get relationMappings() {

    return {

      cvAccessMaps: {
        relation: Model.HasManyRelation,
        modelClass: __dirname + '/CvAccessMapBaseModel',
        join: {
          from: 'cv_access_map.device_id',
          to: 'cv_device.id'                
        }    
      },
    }
  }
}

Thanks in advance,
Adrian

Can someone help me setting up graphql with this lib?

I'm following the readme example with my already created model:

const Model = require('../model')

class File extends Model {
  static get tableName()
  {
    return 'files'
  }

  static jsonSchema()
  {
    return {
      type: 'object',
      require: ['name'],
      properties: {
        id: { type: 'integer' },
        name: { type: 'string' },
        path: { type: 'string' },
        description: { type: 'string' }
      }
    }
  }
}

module.exports = File

This is how I'm setting up the GraphQL:

const { builder } = require('objection-graphql')
const graphql = require('express-graphql')
const file = require('../models/file')

const schema = builder()
          .model(file)
          .build()

        app.use('/graphql', graphql({
          schema: schema,
          graphiql: true
        }))

When I run this on graphiQL:

{
  Files(orderBy: id) {
    name
  }
}

this is what I got:

{
  "errors": [
    {
      "message": "Enum type FilesPropertiesEnum must define one or more values.\n\nType Files must define one or more fields."
    }
  ]
}

What I'm doing wrong?

SchemaBuilder does not recognise inherited methods or properties

Hi,

I have a set of 'base' objection.js models that are generated directly from my SQL schema, in a 'models/base' folder. These are named as follows 'PersonBaseModel', 'MovieBaseModel', etc, and contain their jsonSchema in each.

In the 'models' parent folder, I have the derived classes that extend the 'base' models, which are basically empty, but they extend the 'base' models, and are named as follows 'PersonModel', 'MovieModel' etc. These models are also generated, but only once. If they already exist, they are not overwritten during regeneration of the models. This allows the user to modify the derived model, and provide custom functionality in eg. the 'PersonModel' , without fear of them being overwritten. This is pretty standard methodology for an ORM.

When I try to run the 'objection-graphql' Builder on eg. 'PersonModel', it tries to verify that the jsonSchema exists, and fails, because jsonSchema is in the parent 'PersonBaseModel'.

Is there any way to work around this? Or is there any work on 'objection-graphql' that is heading in this direction?

Usage with the latest Objection version: TypeError: Class constructor Model cannot be invoked without 'new'

I'm trying to use this package with the latest Objection js version. Unfortunately, I've got the following error while building my schema:

Usage with the latest Objection version: TypeError: Class constructor Model cannot be invoked without 'new'

Here is my stack trace:

at propertyNameToColumnName (..\node_modules\objection\lib\model\Model.js:1244:15)
< at Function.propertyNameToColumnName (..\node_modules\objection\lib\model\Model.js:518:40)
< at reducePrimitiveFields (..\node_modules\objection-graphql\lib\argFactories.js:149:55)
< at ..\node_modules\objection-graphql\lib\argFactories.js:30:12
< at ..\node_modules\objection-graphql\lib\SchemaBuilder.js:109:27
< at arrayReduce (..\node_modules\lodash\lodash.js:704:21)
< at Function.reduce (..\node_modules\lodash\lodash.js:9698:14)
< at SchemaBuilder._argsForModel ..\node_modules\objection-graphql\lib\SchemaBuilder.js:108:12)
< at ..\node_modules\objection-graphql\lib\SchemaBuilder.js:81:27

It looks like the problem is in this row:

output = func(output, field, propName, modelClass.propertyNameToColumnName(propName));

Returning single record only (postgres)

Hi,

I'm getting a weird bug - when querying a model that has 2 records with Graphql, it is returning the first record only, even though the database is definitely receiving the SQL query without any LIMIT conditions.

I have created a repository to replicate this issue and provide some more detail: https://github.com/paulbjensen/objection-graphql-postgres-issue

On a side note, I would like to say thank you for Objection.js. I used to be a Ruby on Rails developer a long time ago, and this is the closest ORM I've seen in the Node.js space to what ActiveRecord provided way back then.

How to retrieve DB context in a Mutation with `extendWithMutations`

Hi @timhuff ,

Regarding mutations, I am ditching my embedded code approach and going with your excellent extendWithMutations() approach, but I have a couple of usage questions, please:

  1. What is the best way to setup and call extendWithMutations() for mutations on multiple models? I have multiple mutations per model, and obviously multiple models with this need. Should I do:
schema = mainModule
  .builder()
  .model(Person)
  .extendWithMutations(mutationTypesPerson)
  .model(Book)
  .extendWithMutations(mutationTypesBook)
  .build();    
  1. How do I get access to the database context in the mutation code, without creating a new DB connection for each query? Obviously, I need to make database calls inside the mutation, but I'm too dumb to see how to do it :o

Do you perhaps have a simple example?

TIA,
Adrian

'builder.tableRefFor is not a function'

Objection: 0.7.12
Knex: 0.13.0
MySql: 2.12.0

When trying to execute a graphQL query per the example given in the readME I am getting an error at line 350 of SchemaBuilder.js.

` _filterForSelects(astNode, modelClass) {
const relations = modelClass.getRelations();
const selects = [];

for (let i = 0, l = astNode.selectionSet.selections.length; i < l; ++i) {
  const selectionNode = astNode.selectionSet.selections[i];
  const relation = relations[selectionNode.name.value];

  if (!relation) {
    selects.push(selectionNode.name.value);
  }
}

if (selects.length === 0) {
  return null;
}

return (builder) => {
  builder.select(selects.map(it => {
    if (modelClass.jsonSchema.properties[it]) {
      console.log(modelClass.jsonSchema.properties)
      return `${builder.tableRefFor(modelClass)}.${it}`;
    } else {
      return it;
    }
  }));
};

}
}`

'builder.tableRefFor is not a function'

Upon inspection that it is looking for this method off of the knex query builder but I do not see this listed inside the object. I went ahead and did a search in my entire project (including node_modules) and could not find this called anywhere. I was just wondering what this is supposed to do and why I can't seem to find it anywhere.

When I take this if logic out and just return the property mapping i get a correct return however since I am not sure what this does and I have not built any complex queries (I am trying to proof of concept this) I am hesitant to remove it.

Support "in-model" filters and arguments

It would be nice to have ability to define args and filters that should be exposed. So let's see the example.

By default it exposes lots default fileters and args. Also module allows to add custom filters through .argFactory() method.

So the first problem is that it's hard to remove not needed filters for certain fields. Also it's became rather complicated when you have lots of models defined in different files. So in order to add some filters or args for each model, you'll need to define all that stuff somewhere (probably in your model file), then collect it into one object in file where schema is built and pass it to .argFactory() method.

Instead of doing this it would be better if module will check for presence of some static properties in model class e.g.:

class MyModel extends Model {
  static get defaultFilters() {
    return {
      id: ['eq', 'isNull'],
      time: ['eq', 'gt', 'gte', 'lt', 'lte']
    };
  }
}

So when model will be generated it will try to get this property and if it exist it will only add filters for each field that are defined in that object.

The same approach could be implemented for additional filters and args. And as a result .argFactory() method will be under the hood of this module and users will just define all the stuff they need directly in model class.

So it will be something similar to @timhuff approach with mutations #43. But in this case there will be no need to call additional method like extendWithModelMutations, it would just check for some properties in modelClass and get all info from there.

Using generated schema with Apollo Express Server? [Solved - yes, it works]

Hi,

Is it possible to use the generated schema with the Apollo GraphQL Express server? (As per this example https://github.com/Akryum/apollo-server-example)

a direct call to graphql() is working fine:

const IwAddressBaseModel = require('./data/objection/base/IwAddressBaseModel'),
      IwRegionBaseModel = require('./data/objection/base/IwRegionBaseModel');

const graphQlSchema = graphQlBuilder()
                       .model(IwAddressBaseModel)
                       .model(IwRegionBaseModel)
                       .build();

//Execute a GraphQL query.
graphql(graphQlSchema, 
`{
  iwRegions {
    name
  }
}`).then(result => {
  console.warn(JSON.stringify(result, null, 2));
});

const GRAPHQL_PORT = 3001;
const SUBSCRIPTIONS_PATH = '/subscriptions';

var graphQLServer = express();

graphQLServer.use(cors());

graphQLServer.use(bodyParser.urlencoded({ extended: true }));

graphQLServer.use(bodyParser.json());  
            
graphQLServer.use('/graphql', graphqlExpress({
    graphQlSchema
}));

graphQLServer.use('/graphiql', graphiqlExpress({
    endpointURL: '/graphql'
}));


const server = createServer(graphQLServer);

server.listen(GRAPHQL_PORT, () => {
  console.log(`GraphiQL is now running on http://localhost:${GRAPHQL_PORT}/graphiql`)
//  console.log(`GraphQL Subscriptions server is now running on http://localhost:${GRAPHQL_PORT}/${SUBSCRIPTIONS_PATH}`)
});

with console.log showing:

{
  "data": {
    "iwRegions": [
      {
        "name": "Limpopo"
      },
      {
        "name": "Gauteng"
      },
      {
        "name": "Western Cape"
      },
      {
        "name": "Northwest Province"
      },
      {
        "name": "Mpumulanga"
      },
      {
        "name": "Northern Cape"
      },
      {
        "name": "Kwazulu Natal"
      },
      {
        "name": "Garden Route"
      },
      {
        "name": "Eastern Cape"
      }
    ]
  }
}

but when I try to use graphiQL with the same query, it returns an error message:

<body>
<pre>Error: Must provide schema<br> &nbsp; &nbsp;at invariant (C:\xampp\htdocs\iwallqsrv\node_modules\graphql\jsutils\invariant.js:19:11)<br> &nbsp; &nbsp;at Object.validate (C:\xampp\htdocs\iwallqsrv\node_modules\graphql\validation\validate.js:58:37)<br> &nbsp; &nbsp;at doRunQuery (C:\xampp\htdocs\iwallqsrv\node_modules\apollo-server-core\src\runQuery.ts:116:30)<br> &nbsp; &nbsp;at C:\xampp\htdocs\iwallqsrv\node_modules\apollo-server-core\src\runQuery.ts:59:39<br> &nbsp; &nbsp;at &lt;anonymous&gt;<br> &nbsp; &nbsp;at process._tickDomainCallback (internal/process/next_tick.js:228:7)</pre>
</body>

graphql latest version support

Hi
I encounter a problem using this package with the latest version of graphql js (0.9.2 in my case). The problem is in _resolverForModel: it looks like fieldASTs is not there anymore, so I've got an error "using [0] with undefined"

How to write mutation in Schema Defition Language

Hi, absolutely loving this code.

Being new to graphql and objection in general I’m struggling to sort out a way to write my mutations in Schema Defition Language. I’ve tried using the graphql-tools library but can’t get it to output schema without including the type defs.

Anyone found a way to use SDL with this library?

How to extend query with select

I tried

`postss {
      content
      liked_by_me
}`

onQuery(builder){
        // NOT WORK
        builder
        .select(['posts.*',
            Post.relatedQuery('liked_users').count().as('liked_count'),
            Post.relatedQuery('liked_users').select(raw('true')).where('uid', 1).as('liked_by_me')
        ])
}

but got undefined

remove the liked_by_me field works fine

liked_by_me also works fine in normal query

furthermore, I want to extend the liked_by_me with redis, so how to do that?
requery the redis after getting the actual data from db? then how to bind it to graphql?

any advice?

many thanks

Support `GraphQLInputObject` as argument

Seems like objection-graphql cannot handle objects as custom arguments...

{ Error: objection-graphql cannot handle argument value {"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"name","loc":{"start":28,"end":32}},"value":{"kind":"StringValue","value":"containerId","loc":{"start":33,"end":46}},"loc":{"start":28,"end":46}},{"kind":"ObjectField","name":{"kind":"Name","value":"value","loc":{"start":48,"end":53}},"value":{"kind":"StringValue","value":"asd","loc":{"start":54,"end":59}},"loc":{"start":48,"end":59}}],"loc":{"start":27,"end":60}}
    at SchemaBuilder._argValue (xxx\node_modules\.github.com\Vincit\objection-graphql\69e09be0192a19ae52585847b4e032641eec0675\node_modules\objection-graphql\lib\SchemaBuilder.js:370:13)
    at SchemaBuilder._filterForArgs (xxx\node_modules\.github.com\Vincit\objection-graphql\69e09be0192a19ae52585847b4e032641eec0675\node_modules\objection-graphql\lib\SchemaBuilder.js:346:26)

This is set in the custom argument builder:

args.fieldEq = {
    type: ParamInput,
    query: (query, value) => {
        query.whereRaw("data->>'"+value.name+"' = '" + value.value + "'")
    },
}
const ParamInput = new GraphQLInputObjectType({
    name: 'ParamInput',
    fields: {
        name: {type: GraphQLString},
        value: {type: GraphQLString},
    },
})

Error source

_argValue(value, variables) {
    if (value.kind === KIND_VARIABLE) {
      return variables[value.name.value];
    } else if ('value' in value) {
      return value.value;
    } else if (Array.isArray(value.values)) {
      return value.values.map(value => this._argValue(value, variables));
    } else {
      throw new Error(`objection-graphql cannot handle argument value ${JSON.stringify(value)}`);
    }
  }

@denizunal3

Inconsistent behavior for a complex query

I have a hierarchical query which looks like this:

roomJobSettings (style: "${style}"){       
            roomType,
            jobGroup {
              id,        
             //...      
              jobCategory {
                id,
                //...
              },    
              materialType {
                id,
               //...
                materials {
                  id,
                  //...
                  jobGroupMaterials {            
                    //..
                  },
                  materialStyles (style: "${style}") {
                    id, 
                   //...
                  }
                }
              }
            }
          }

For some reason this query result for materialStyles is inconsistent: sometiimes it retrieves something and sometimes it's empty. What can be the reason for this?

__typename being sent to SQL backend as part of a query?

Hi @koskimas,

I'm experiencing a problem with __typename appearing in the final query string being sent to the SQL engine, I'm hoping you can help here. Have you seen anything like this before, or could you point me in a direction that might help?

I'm using Apollo Server Express 1.2.0 and Apollo Client 2.0.2.

I have a simple query that works fine in GraphiQL, but not when sent via the client.

{ 
  iwRegions {
    country_id
    name
  }
}

and I'm getting an error back as follows:

message: "select `iw_region`.`country_id`, `iw_region`.`name`, `__typename` from `iw_region` - ER_BAD_FIELD_ERROR: Unknown column '__typename' in 'field list'"

I've tried raising issues on the GitHub for apollo-client, apollo-server and vue-apollo, but I've not had any response there :(

Any help you can give would be greatly appreciated!

Thank you
Adrian

Optimal usage of the db schema name in GraphQL schema

A few days ago I switched to a named schema for my DB tables. So right now I ended up with prefixing all table references in my models with the DB schema name. However, I don't want my GraphQL schema to be prefixed with the DB schema name. Is there any workaround for this case?

Is there any way to implement resolvers in the jsonSchema?

`
dbObj = {
name: 'john'
address: {
prop w spaces 1: '0',
prop w spaces 2: '1'
}
}

jsonSchema = {
name: {type: string },
addres: { type: object, properties {
propWSpaces1: {type: string, resolve: (parent) => parent['prop w spaces 1']}
propWSpaces2: {type: string, resolve: (parent) => parent['prop w spaces 2']}
}
}
}
`

This is what I am trying to accomplish, however the resolve is either not recognized when the GraphQl schema is generated or Im missing something important on implementing resolvers correctly while using objection-graphql.

any ideas?

Basic problem with standard Objection express-es6 example and objection-graphql

Hi, I am new to objection.js and objection-graphql and have tried creating a basic test for objection-graphql based on the standard objection.js es6 example. I played with this before and went through quite a few errors which manifest as Node.js stack dump errors some more obvious than others but have come unstuck at this one. I have made a repo with it in :-

https://github.com/AaronNGray/objection-express-es6-graphql-test

I am getting the following error :-

C:\Users\aaron\Tests\objection-tests\express-es6>node objection-graphql.js
test
(node:27828) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'movies' of undefined
    at graphql.then.result (C:\Users\aaron\Tests\objection-tests\express-es6\objection-graphql.js:29:25)
    at internalTickCallback (internal/process/next_tick.js:77:7)
    at process._tickCallback (internal/process/next_tick.js:47:5)
    at Function.Module.runMain (internal/modules/cjs/loader.js:777:11)
    at executeUserCode (internal/bootstrap/node.js:342:17)
    at startExecution (internal/bootstrap/node.js:276:5)
    at startup (internal/bootstrap/node.js:227:5)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:743:3)
(node:27828) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:27828) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Hopefully there is something stupid I have not seen.

select method of query not used -> how to extend query builder with objection-graphql

This is a continuation of #264.

This issue is how to add a virtual field with materialized path.
In vanilla objection, this can be done with an extended query builder and extended select method that adds an eager parent relation loading if the virtual field is requested. Then this data is picked up in the getter of the virtual field with a traverse call. This is a very elegant solution.

Now, objection-graphql does not seem to use the select method at all when doing a query, so then this solution above is not valid. Is there any other way and why should not only the requested fields from the graphql query be fetched?

Many thanks!

range: [] doesn't seem to work with variables in graphiQL

I'm trying to test a query in the GraphiQL client, but it doesn't recognize variables in the query. They are simply ignored. The OrderBy: works fine.

The query:

query getRegions ($start: Int, $end: Int) {
  iwRegions (range: [$start, $end], orderBy: name) {
    name
  }
}

with variables:

{
  "start": 0,
  "end": 4
}

returns:

{
  "data": {
    "iwRegions": [
      {
        "name": "Eastern Cape"
      },
      {
        "name": "Garden Route"
      },
      {
        "name": "Gauteng"
      },
      {
        "name": "Kwazulu Natal"
      },
      {
        "name": "Limpopo"
      },
      {
        "name": "Mpumulanga"
      },
      {
        "name": "Northern Cape"
      },
      {
        "name": "Northwest Province"
      },
      {
        "name": "Western Cape"
      }
    ]
  }
}

If I hard-code the values as follows:

query getRegions {
  iwRegions (range: [0, 4], orderBy: name) {
    name
  }
}

I get the correct response:

{
  "data": {
    "iwRegions": [
      {
        "name": "Eastern Cape"
      },
      {
        "name": "Garden Route"
      },
      {
        "name": "Gauteng"
      },
      {
        "name": "Kwazulu Natal"
      },
      {
        "name": "Limpopo"
      }
    ]
  }
}

Any ideas?

Thanks,
Adrian

$formatDatabaseJson doesn't work for a newer versions of this library

Hi
I'm trying to upgrade from version 0.1.2. I use snake_case for db objects naming and camelCase for my ORM models. I use the following code to parse from one format to another:

  $formatDatabaseJson(json) {
        json = super.$formatDatabaseJson(json)
        return mapKeys(json, (value, key) => snakeCase(key))
    }
    $parseDatabaseJson(json) {
        json = mapKeys(json, (value, key) => camelCase(key))
        return super.$parseDatabaseJson(json)
    }

My queries used to work for version 0.1.2, but now they fail and I think the reason is wrong parsing behavior as I've got wrong queries in my log:

select "area_param"."paramName", "area_param"."type", "area_param"."displayUnit", "area_param"."displayName" from "area_param"

Custom Error Handling

This isn't really an issue with the project but I figured I'd let you guys know what I've done with my fork because I think it would add value here as well. I added an API called setErrorHandler. It's a very small change. This enables me to specify a global error handler for all queries (basically I replace the unexpected errors with new Error("An unexpected error has occurred").

timhuff@9507f9c

As an aside, my project is not a large deviation from this one. There's no conflicts and it's backwards compatible. I've basically just augmented it with a few small functions. After working with it for a few months, I can confirm that they're extremely helpful. My original pull request was denied (thus the fork) due to a difference of opinion related to separation of concerns. I'd encourage you to consider merging, though, as I do feel that the changes I've made really brings the library to a new level. For example, I'm able to define all mutations and subscriptions inside the Objection model, itself. Organizationally, this is a pretty nice ability to have.

Relations not loading after mutation

I have a mutation defined as follows


const mutation = {
    description: 'Updates a Person',
    type: graphQlSchema.getType('Persons'),
    args: {
        id: {
            type: new GraphQLNonNull(GraphQLInt),
            description: 'Person ID',
        },
        first_name: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'First Name',
        },
        last_name: {
            type: new GraphQLNonNull(GraphQLString),
            description: 'Last Name',
        }
    },
    resolve: async (root, userData) => {
        const { id, first_name, last_name } = userData;
        return await Person.query().findById(id).patch({ first_name, last_name }).returning('*');
    },
};

Which I call from my app like

mutation($id: Int!, firstName: String!, lastName: String!) {
    updatePerson(id: $id, first_name: $firstName, last_name: $lastName) {
        id,
        first_name,
        last_name,
        pets {
                name,
                toys {
                        brand_name,
                        quantity
                }
        }
    }
}

The mutation part works fine, but when the data is returned from the server, it's missing the pets object and everything below it. Interestingly (maybe), pets actually comes back as null rather than being omitted completely, so it seems like maybe it tried to complete the relation, but nothing came back.

Any idea what could be going on here?

Allow for use of instance of Objection QueryBuilder

In implementing Mutations, I am trying to call insertAndFetchByID and updateAndFetchByID, to satisy the GraphQL requirements to perform a Mutation and retrieve the results in a single trip, but the QueryBuilder throws an error, saying that these operations are only available on an actual instance of the QueryBuilder, and as a result I have to use the standard insert and update methods.

I am adding the fields requested as update/insert (data, ['returning']) to the calls as required for passing them to Knex. Knex accepts them and performs the operation without complaining, but I still only get an Integer in return, indicating number of records updated.

I am guessing that objection-graphql is using Objection.js's static QueryBuilder methods in composing the calls to objection.js, and that is what is causing this?

Currently, the end result of this problem is that I have to jump through hoops to update my Apollo Store Cache after the mutations, and a full invalidation of the cache is required. Ouch.

Is there a way around this, to get the desired behaviour?

Thanks!

GraphQL query params and "OR" support

Hi
I am playing around this project for a while and I stuck with two problems right now:

  1. I want to use parameters for my queries, but they don't work for me for some reason. For instance, this query retrieves an empty set:
query getEnAirports($part:String){
  airports(iataLike: $part){
    id,
    name,    
    city,
    country,
    iata
  }
}

{
  "part": "%PE%"
}

while the same query with a hard coded value does the job:

query getEnAirports{
  airports(iataLike: "%PE%"){
    id,
    name,    
    city,
    country,
    iata
  }
}
  1. The second problem is "OR" support for queries. What's the right way to implement this? Should it be implemented as it is described in "Adding your own custom arguments" section with a custom suffix or there is another more convenient way?

Thanks.

Error when merging two schemas with graphql-tools mergeSchemas

I'm trying to merge two objection-graphql schemas with graphql-tools mergeSchemas function (aka schema stitching). Each schema works ok independently, however any query to the merged schema results in the following error:

"undefined passed as argument #2 for 'where' operation. Call skipUndefined() method to ignore the undefined values."

The error is the same for any query including even the basic ones like:

{
    companys {
      id, 
      name
    }
 }

I know that the error can reside somewhere in graphql-tools but what I see is that it actually delegating the query to the appropriate schema depending on type, i.e.:

 delegate: function (operation, fieldName, args, context, info) {
            var schema = typeRegistry.getSchemaByField(operation, fieldName);
            if (!schema) {
                throw new Error("Cannot find subschema for root field " + operation + "." + fieldName);
            }
            var fragmentReplacements = typeRegistry.fragmentReplacements;
            return delegateToSchema(schema, fragmentReplacements, operation, fieldName, args, context, info);
        }

Any ideas what how to fix this?

Ambiguous reference to the column when using join

Hi, I've got an error

Ambiguous reference to the column "id"

for the following GraphQL query (I use PostgreSQL):

{
      residentialComplexs {
        id,
        name,
        blocks {
          id, 
          name,
          address,
          layouts {
            id
          }
        }
      }
    }

Here is the query, which was generated and fails with the error
select "block_layout"."block_id" as "objectiontmpjoin0", "id" from "layout" inner join "block_layout" on "block_layout"."layout_id" = "layout"."id" where "block_layout"."block_id" in ($1, $2, $3, $4)

Looks like that explicit table prefix "layout" before "id" can solve the problem. It used to work for the previous version.

Error: relation type "HasManyRelation" is not supported

I have two models (Order and OrderItems) where Order has relationMappings defined as follows:

    static get relationMappings(){

        const OrderItem = require('./OrderItem');

        return {
            items: {
                relation: Model.HasManyRelation,
                modelClass: OrderItem,
                join: {
                    from: 'orders.id',
                    to: 'order_items.order_id'
                }
            }
        }
    }

When I use the GraphQL Builder, as follows:

const schema = builder()
  .model(Order, { fieldName: 'order', listFieldName: 'orders' })
  .model(OrderItem)
  .build();

I get the following error:

Error: relation type "HasManyRelation" is not supported
    at SchemaBuilder._relationField (/.../node_modules/objection-graphql/lib/SchemaBuilder.js:243:11)

Looking at the code in the _relationField method it seems that HasManyRelation should be supported.

Not 100% sure why this is happening, but it might be related to the fact that I have all my models defined in a separate NPM package so the instanceof objection.HasManyRelation check isn't referring to the same instance of objection that the models were built with?

Anyone have a small sample project that implements mutations?

Hi,

Does anyone perhaps have a small (but complete) example of an app source that demonstrates the use of Mutations (hopefully multiple Mutations).

My API server resides in a main file: server.js, and I want to import the Mutation definitions into this, and not define them directly in server.js. I will have several hundred mutations, and I need to be able to import them in a structured manner and not define them all in a single file.

Currently, if I try this, I cannot seem to get access to the Models inside the mutation, to be able to write to the DB. Even the tests say that the mutations are working, but they omit the all-important step of actually writing to the DB via Objection.

TIA

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.