GraphQL execution slow and incorrect - graphql

I have built my first GraphQL server in NodeJS to query a MySQL database using Sequelize and Apollo.
SQL commands run fast (as normal) but GraphQL queries are timing out, and returning incomplete data. So there are two issues I am facing:
Why is my GraphQL API querying so slowly?
How do I ensure I get all thedata that I need?
I have created a Stackblitz for this, and built a new test MySQL database. Unfortunately the stackblitz doesnt seem to be able to connect to a DB (either my real one or the test one, both work on my local version with the same code) but the structure is visible.
The GraphQL query I am trying to run is this:
query UnitTopicActivity($unitTopicActivityId: ID!) {
unitTopicActivity(id: $unitTopicActivityId) {
activity_id
activity_specific_id
id
topic_id
topic {
name
description
id
image
}
activity {
... on Working {
name
}
... on Experiment {
name
}
... on PhetLab {
name
}
... on VideoNote {
name
}
}
}
}
With variables
{
"unitTopicActivityId": 192
}
The output I expect is:
{
"data": {
"unitTopicActivity": {
"activity_id": 5,
"activity_specific_id": 44,
"id": "193",
"topic_id": 73,
"topic": {
"name": "Complex Energy Problems",
"description": "This is topic you will be required to use all you know of energy and work to solve some complex problems.",
"id": "73",
"image": ""
},
"activity": [ // not sure about what this should look like to be honest as I havent been able to generate it yet but this best guess.
{
"working": {
"name": "Working Activity"
}
},
{
"experiment": {
"name": "Experiment Activity"
}
}
]
}
}
}
What I get is (This single query takes 5.6 seconds):
{
"data": {
"unitTopicActivity": {
"activity_id": 5,
"activity_specific_id": 44,
"id": "193",
"topic_id": 73,
"topic": {
"name": "Complex Energy Problems",
"description": "This is topic you will be required to use all you know of energy and work to solve some complex problems.",
"id": "73",
"image": ""
},
"activity": null
}
}
}
If I try to run the query that does this for everyone of the items then I get some data but then a timeout in the code as it hits the execution limit in my database.js.
Edit - Some other info about similar querys:
This is a simple query and runs fast enough (631ms non local db for 165 rows), as expected:
query UnitTopicActivities {
unitTopicActivities {
activity_id
activity_specific_id
id
topic_id
}
}
If I add in the topic (thus a subquery to another table) then it gets about 50% of the stuff in the topic table the first time its queried and then is faster the next time:
query UnitTopicActivities {
unitTopicActivities {
activity_id
activity_specific_id
id
topic_id
topic {
id
name
description
image
}
}
}
Finally adding in the activity section produces the same results as originally provided, activity is null and it doesnt get through all the data before it times out.
end edit
I have tried to look up the issue elsewhere, and note that some people use another tool which stores data to speed up the process, but im not sure this is the solution. I dont think it should be this slow when the SQL command does it in under 0.1 seconds consistently and so expect there are other underlying issues.
Some things im not sure will help but I had been asked for in an unrelated context:
SHOW CREATE TABLE units_topics;
localhost/aM78gzT8nB/ https://remotemysql.com/phpmyadmin/db_sql.php?db=aM78gzT8nB
Your SQL query has been executed successfully.
SHOW CREATE TABLE units_topics
units_topics CREATE TABLE `units_topics` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` text NOT NULL,
 `description` text NOT NULL,
 `image` text NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=74 DEFAULT CHARSET=latin1
SHOW TABLE STATUS WHERE name LIKE "units_topics";
localhost/aM78gzT8nB/ https://remotemysql.com/phpmyadmin/db_sql.php?db=aM78gzT8nB
Your SQL query has been executed successfully.
SHOW TABLE STATUS WHERE name LIKE "units_topics"
units_topics MyISAM 10 Dynamic 55 158 8700 281474976710655 2048 0 74 2022-12-01 11:00:21 2022-12-01 11:00:21
NULL
latin1_swedish_ci
NULL
The code is as follows:
server.js (launches an apollo server, this is the node launch file)
const apollo = require('#apollo/server');
const apolloStandalone = require('#apollo/server/standalone');
const gqlTools = require('#graphql-tools/merge');
const playGround = require('apollo-server-core');
const schema = [require('./schema/minimumSchema')];
const typeDefs = gqlTools.mergeTypeDefs(...[schema.map((a) => a.typeDefs)]);
const resolvers = gqlTools.mergeResolvers(...[schema.map((a) => a.resolvers)]);
// server function (async bug?)
async function startApolloServer(typeDefs, resolvers) {
// create an apollo serve rinstance...
const server = new apollo.ApolloServer({
typeDefs,
resolvers,
plugins: [playGround.ApolloServerPluginLandingPageGraphQLPlayground()],
});
const { url } = await apolloStandalone.startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
}
startApolloServer(typeDefs, resolvers);
database.js - this links to the database. Username, password etc left in as itsa temporary test server.
const Sequelize = require('sequelize');
const graphql = require('graphql/type');
var db = {};
const Date = new graphql.GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize(value) {
return value.getTime(); // Convert outgoing Date to integer for JSON
},
parseValue(value) {
return new Date(value); // Convert incoming integer to Date
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
// Convert hard-coded AST string to integer and then to Date
return new Date(parseInt(ast.value, 10));
}
// Invalid hard-coded value (not an integer)
return '1';
},
});
const sequelize = new Sequelize.Sequelize(
'aM78gzT8nB',
'aM78gzT8nB',
'RtPJ3fI68F',
{
host: 'remotemysql.com',
port: '3306',
dialect: 'mysql',
dialectModule: require('mysql2'),
logging: false,
pool: {
max: 2,
min: 0,
acquire: 30000,
idle: 20000,
},
}
);
let models = [
require('./models/a_experiment'),
require('./models/a_flashcards'),
require('./models/a_phetlabs'),
require('./models/a_videonotes'),
require('./models/a_working'),
require('./models/units_topics'),
require('./models/units_topics_activities'),
];
models.forEach((model) => {
const seqModel = model(sequelize, Sequelize.Sequelize);
db[seqModel.name] = seqModel;
});
Object.keys(db).forEach((key) => {
if ('associate' in db[key]) {
db[key].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize.Sequelize;
module.exports = db;
module.exports.Date = Date;
minimumSchema.js is the file I have defined the types and resolvers in:
const db = require('../database');
const typeDefs = `
type Experiment {
id: ID!
points_max: Int
name: String
introduction: String
diagram_link: String
}
type Flashcard {
id: ID!
topic_id: Int
side_a: String
side_b: String
topic: Topic
}
type PhetLab {
id: ID!
name: String
description: String
embed_code: String
sim_link: String
points_max: Int
}
type VideoNote {
id: ID!
points_max: Int
name: String
link: String
length: Int
}
type Working {
id: ID!
points_max: Int
name: String
description: String
}
type Topic {
id: ID!
name: String
description: String
image: String
}
union Activity = Working | Experiment | PhetLab | VideoNote
type Query {
unitTopicActivities(query: String): [UnitTopicActivity]
unitTopicActivity(id: ID!): UnitTopicActivity
}
type UnitTopicActivity {
id: ID!
topic_id: Int
activity_id: Int
activity_specific_id: Int
topic: Topic
activity: [Activity]
}
`;
const resolvers = {
Query: {
unitTopicActivities: async (obj, args, context, info) =>
db.units_topics_activities.findAll(),
unitTopicActivity: async (obj, args, context, info) =>
db.units_topics_activities.findOne({ where: { id: args.id } }),
},
UnitTopicActivity: {
topic: async (obj, args, context, info) =>
db.units_topics.findOne({ where: { id: obj.topic_id } }),
activity: async (obj, args, context, info) => {
switch (obj.activity_id) {
case 1:
db.a_videonotes.findByPk(obj.activity_specific_id);
break;
case 2:
db.a_experiment.findByPk(obj.activity_specific_id);
break;
case 3:
db.a_phetlabs.findByPk(obj.activity_specific_id);
break;
case 5:
db.a_working.findByPk(obj.activity_specific_id);
break;
}
},
},
Flashcard: {
topic: async (obj, args, context, info) =>
db.units_topics.findOne({ where: { id: obj.topic_id } }),
},
};
module.exports.resolvers = resolvers;
module.exports.typeDefs = typeDefs;
Then the models - these are the files sequelize uses so it knows what the database looks like:
The first five, experiment, working, flashcards, phetlabds and videonotes just store the activity names, the descriptions and the points required. Some other basic info and they are all a little different but basically all server the same purpose and arent usually queried on their own.
a_experiment.js
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('a_experiment', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
points_max: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
introduction: {
type: DataTypes.TEXT,
allowNull: false
},
diagram_link: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
sequelize,
tableName: 'a_experiment',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
a_flashcards.js
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('a_flashcards', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
topic_id: {
type: DataTypes.INTEGER,
allowNull: false
},
side_a: {
type: DataTypes.TEXT,
allowNull: false
},
side_b: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
sequelize,
tableName: 'a_flashcards',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
a_phetlabs.js
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('a_phetlabs', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false
},
embed_code: {
type: DataTypes.TEXT,
allowNull: false
},
sim_link: {
type: DataTypes.TEXT,
allowNull: false
},
points_max: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
tableName: 'a_phetlabs',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
a_videonotes.js
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('a_videonotes', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
points_max: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
link: {
type: DataTypes.TEXT,
allowNull: false
},
length: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
tableName: 'a_videonotes',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
a_working.js
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('a_working', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
points_max: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 100
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
sequelize,
tableName: 'a_working',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
units_topics.js - This lists the topics, with their names, descriptions etc. Again this is unlikely the be queries on its own:
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('units_topics', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false
},
image: {
type: DataTypes.TEXT,
allowNull: false
}
}, {
sequelize,
tableName: 'units_topics',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
units_topics_activities.js - This is the table that is likely to be queried, and pulls together the topics and the activities from any of the five different activity databases. Its a link between the topicId and the activityIds.
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('units_topics_activities', {
id: {
autoIncrement: true,
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true
},
topic_id: {
type: DataTypes.INTEGER,
allowNull: false
},
activity_id: {
type: DataTypes.INTEGER,
allowNull: false
},
activity_specific_id: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
sequelize,
tableName: 'units_topics_activities',
timestamps: false,
indexes: [
{
name: "PRIMARY",
unique: true,
using: "BTREE",
fields: [
{ name: "id" },
]
},
]
});
};
Finally there the init-models.js file which simply initialises the database structures in sequelize:
var DataTypes = require('sequelize').DataTypes;
var _a_experiment = require('./a_experiment');
var _a_flashcards = require('./a_flashcards');
var _a_phetlabs = require('./a_phetlabs');
var _a_videonotes = require('./a_videonotes');
var _a_working = require('./a_working');
var _units_topics = require('./units_topics');
var _units_topics_activities = require('./units_topics_activities');
function initModels(sequelize) {
var a_experiment = _a_experiment(sequelize, DataTypes);
var a_flashcards = _a_flashcards(sequelize, DataTypes);
var a_phetlabs = _a_phetlabs(sequelize, DataTypes);
var a_videonotes = _a_videonotes(sequelize, DataTypes);
var a_working = _a_working(sequelize, DataTypes);
var units_topics = _units_topics(sequelize, DataTypes);
var units_topics_activities = _units_topics_activities(sequelize, DataTypes);
return {
a_experiment,
a_flashcards,
a_phetlabs,
a_videonotes,
a_working,
units_topics,
units_topics_activities,
};
}
module.exports = initModels;
module.exports.initModels = initModels;
module.exports.default = initModels;

Related

model.hasMany called with something that's not a subclass of Sequelize.Model

I am getting the below error whenever I tried to call any call from serverless framework lambda
[offline] _____ HANDLER RESOLVED _____
offline: Failure: product.hasMany called with something that's not a subclass of Sequelize.Model
Error: product.hasMany called with something that's not a subclass of Sequelize.Model
at Function.hasMany (C:\Users\Kiran\Documents\Projects\Rentals-Backend\node_modules\sequelize\lib\associations\mixin.js:18:13)
at Function.Product.associate (C:\Users\Kiran\Documents\Projects\Rentals-Backend\entity\product.js:21:17)
IMPORTANT
Below code is the answer for the above error. You might missed any steps. So you can refer and fix. Thanks Anatoly who helped me to solve the problem.
Product model:
const { STRING, BOOLEAN, INTEGER } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const Product = sequelize.define("product", {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
name: { type: STRING },
description: { type: STRING, allowNull: true },
purchase_price: { type: STRING },
tax: { type: STRING },
sale_price: { type: STRING },
categoryId: { type: STRING },
status: { type: BOOLEAN, defaultValue: 0 },
created_on: { type: INTEGER, allowNull: true },
updated_on: { type: INTEGER, allowNull: true },
}, {
timestamps: false,
freezeTableName: true,
})
Product.associate = function (models) {
Product.hasMany(models.product_image, { as: "images" });
Product.belongsTo(models.product_category, { as: "category", foreignKey: 'categoryId' });
};
return Product;
}
Image model:
const { STRING, BOOLEAN, INTEGER } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const ProductImage = sequelize.define("product_image", {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
productId: { type: INTEGER },
fileName: { type: STRING },
url: { type: STRING },
position: { type: INTEGER },
isDefault: { type: BOOLEAN, defaultValue: 0 },
shopId: { type: STRING },
status: { type: BOOLEAN, defaultValue: 0 },
created_on: { type: INTEGER, allowNull: true },
updated_on: { type: INTEGER, allowNull: true },
}, {
timestamps: false,
freezeTableName: true,
})
return ProductImage;
}
Category model:
const { STRING, BOOLEAN, INTEGER } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const ProductCategory = sequelize.define("product_category", {
id: { type: INTEGER, primaryKey: true, autoIncrement: true },
name: { type: STRING },
description: { type: STRING, allowNull: true },
status: { type: BOOLEAN, defaultValue: 0 },
created_on: { type: INTEGER, allowNull: true },
updated_on: { type: INTEGER, allowNull: true },
}, {
timestamps: false,
freezeTableName: true,
});
return ProductCategory;
}
This is the config file where we initialize sequelize
Config file
const Sequelize = require('sequelize')
const fs = require('fs')
const path = require('path')
const db = {}
const models = path.join(__dirname, '..', 'entity')
var basename = path.basename(module.filename)
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASSWORD,
{
dialect: 'mysql',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
logging: false
}
)
fs
.readdirSync(models)
.filter(function (file) {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js')
})
.forEach(function (file) {
var model = require(path.join(models, file))(
sequelize,
Sequelize.DataTypes
);
db[model.name] = model;
})
Object.keys(db).forEach(function (modelName) {
if (db[modelName].associate) {
db[modelName].associate(db)
}
})
db.Sequelize = Sequelize
db.sequelize = sequelize
module.exports = db
Here we are calling product details.
Calling function
const db = require('../config/sequelize-config');
exports.getProductById = (query, username, shopId) => {
return new Promise((resolve, reject) => {
db.product.findOne({
where: {
id: query.id
},
attributes: ['id', 'name', 'description', ['purchase_price', 'purchasePrice'], 'tax', ['sale_price', 'salePrice']],
include: [{
model: db.product_image,
as: 'images',
where: {
status: 1
},
required: false,
attributes: ['id', 'fileName', 'position', 'url']
},
{
model: db.product_category,
as: 'category',
required: false,
attributes: ['id', 'name']
}]
}).then(product => {
if (product) {
resolve({ [KEY_STATUS]: 1, [KEY_MESSAGE]: "Product details fetched successfully", [KEY_DATA]: product });
} else {
reject({ [KEY_STATUS]: 0, [KEY_MESSAGE]: "Product details fetch failed" });
}
}).catch(error => {
reject({ [KEY_STATUS]: 0, [KEY_MESSAGE]: "Product details fetch failed", [KEY_ERROR]: error.message });
});
})
}
To avoid cross-reference errors and similar ones I recommend converting model definitions to functions and registering models and associations in the same one module, see this answer and the question

How to create custom resolvers for Gatsby page queries?

I have a Gatsby application pulling data from Sanity.
This is Sanity's schema for the course.js:
import video from './video'
export default {
// Computer name
name: `courses`,
// Visible title
title: `Courses`,
type: `document`,
fields: [
{
name: `title`,
title: `Course title`,
type: `string`,
description: `Name of the course`
},
{
name: `slug`,
title: `slug`,
type: `slug`,
options: {
source: `title`,
maxLength: 100,
}
},
{
name: `price`,
title: `Price`,
type: `number`
},
{
name: `thumbnail`,
title: `Thumbnail`,
type: `image`,
options: {
hotspot: true,
}
},
{
name: `playlist`,
title: `Playlist`,
type: `array`,
of: [
{
title: `Video`,
name: `video`,
type: `video`,
}
]
},
]
}
And this is Sanity's schema for video.js:
export default {
// Computer name
name: `video`,
// Visible title
title: `Video`,
type: `object`,
fields: [
{ name: `title`, type: `string`, title: `Video title` },
{ name: `url`, type: `url`, title: `URL` },
{ name: `public`, title: `public`, type: `boolean`, initialValue: false }
]
}
This Gatsby page query:
{
allSanityCourses {
nodes {
title
price
playlist {
url
title
public
}
}
}
}
Results in:
{
"data": {
"allSanityCourses": {
"nodes": [
{
"title": "Master VS Code",
"price": 149,
"playlist": [
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Introduction",
"public": true
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Philosophy",
"public": false
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Tech and Tools",
"public": false
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Integration",
"public": true
},
{
"url": "https://www.youtube.com/watch?v=PRz1Nv9GUzs",
"title": "Extensions",
"public": false
}
]
}
]
}
},
"extensions": {}
}
To prevent the url field from being injected into the React component this Gatsby page query runs on (because these urls are paid for), I need to remove it, if the public field is set to false.
I've tried inserting this into gastby-node.js:
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions
const typeDefs = [
schema.buildObjectType({
name: "SanityCourses",
fields: {
playlist: {
type: "[SanityVideo]",
url: {
type: "String",
resolve: (source) => "nope"
},
},
},
interfaces: ["Node"],
}),
]
createTypes(typeDefs)
}
And:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
SanityCourses: {
playlist: {
type: "[SanityVideo]",
url: {
type: "String",
resolve(source, args, context, info) {
return "nope"
},
}
},
},
}
createResolvers(resolvers)
}
But neither seems to work. The url field returns the url as always. The resolvers don't even seem to fire (I've tried putting console.log()'s in them).
Any help on how to remove the url field if the public field is set to false, or general direction to go in would be very appreciated.
Ditch the attempt in createSchemaCustomization since you don't need to customize the schema here (though I believe there is a way to achieve what you want using it, it is not expected that the data in it is sourced from existing nodes, and this undeclared dependency can create caching issues).
Then update your createResolvers function to something like this:
exports.createResolvers = ({ createResolvers }) => {
createResolvers({
SanityVideo: {
safeUrl: {
type: "String",
resolve: (source, args, context, info) => source.public ? source.url : null
},
},
})
}
I don't believe resolvers can replace schema-originated nodes (fields), hence using safeUrl instead of url
The type you are adding a field to is SanityVideo, and it doesn't matter what the parent node is—this will apply to all instances of SanityVideo in your data

schema type for an unknown object in graphql

I have the following object coming back from db:
{
"total_rows": 200,
"bookmark": "g1AAAABteJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVCJDUv3___-zMpjc7D8wgEEiCx71eSwgJQ1A6j-GtiwA6MscCg",
"rows": [
{
"id": "51a1ff51b3b4719d05e40ac4bb0d0566",
"objects": {
"0": {
"type": "ipv4-addr",
"value": "192.168.1.10",
"resolves_to_refs": "2"
},
"1": {
"type": "network-traffic"
}
}
],
"counts": {
"created_by_ref": {
"0203a7e6-b174-4af9-812d-ab889816e868": 1,
"0250789a-14c3-4751-b4a0-c017af82b8f1": 1,
"03c63db6-2a84-4627-88be-a83208d524e6": 1,
"05cba3da-11ff-4a7a-aae9-0b1614cd5300": 1,
"fc825d33-26ea-4563-9478-2e1887b87112": 1
},
"file.hashes.MD5": {
"UNDEFINED": 200
},
"file.name": {
"UNDEFINED": 200
},
"ipv4_addr.value": {
"127.0.0.1": 200,
"192.168.1.10": 200
},
"last_observed": {
"1583503380000": 5,
"1583589780000": 9,
"1585749840000": 12
}
},
"num_of_rows": 10
}
I am trying to fit in a graphql schema to the above. I have the following which works paryially:
const graphql = require("graphql");
const { GraphQLObjectType, GraphQLString, GraphQLSchema, GraphQLInt, GraphQLList } = graphql;
const SearchResultType = new GraphQLObjectType({
name: "SearchResult",
fields: ()=>({
total_rows: { type: GraphQLInt },
bookmark: { type: GraphQLString },
//rows: { type: new GraphQLList(GraphQLInt) },
num_of_rows: { type: GraphQLInt }
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
searchResult:{
type: SearchResultType,
args: { id: { type: GraphQLString } },
resolve(parentValue: any, args: any) {
console.log(args)
return resultMock;
}
}
}
});
module.exports = new GraphQLSchema({
query: RootQuery,
});
The above works for those data types which are defined. However there are some objects in the mockResult above like:
"objects": {
"0": {
"type": "ipv4-addr",
"value": "192.168.1.10",
"resolves_to_refs": "2"
},
"1": {
"type": "network-traffic"
}
or
"counts": {
"created_by_ref": {
"0203a7e6-b174-4af9-812d-ab889816e868": 1,
"0250789a-14c3-4751-b4a0-c017af82b8f1": 1,
"03c63db6-2a84-4627-88be-a83208d524e6": 1,
"05cba3da-11ff-4a7a-aae9-0b1614cd5300": 1,
"fc825d33-26ea-4563-9478-2e1887b87112": 1
So as you see these objects keys are random or at least not guessable until we receive them. Is there any way I can define a sth like this: rows: { type: new GraphQLList(any random object we do not know ) }, as a type in schema below:
const SearchResultType = new GraphQLObjectType({
name: "SearchResult",
fields: ()=>({
total_rows: { type: GraphQLInt },
bookmark: { type: GraphQLString },
rows: { type: new GraphQLList(any random object we do not know ) },
num_of_rows: { type: GraphQLInt }
})
});
You can use the GraphQL JSON Scalar (for example from this implementation). I would not recommend doing this though (in fact years ago I did a talk "GraphQL JSON Scalar considered harmful"). Instead, you might want to transform map-like objects into lists of key-value pairs.
So for example for your counts object you could do the following:
type CreatedByRef {
key: ID
count: Int
}
Object.keys(counts.created_by_ref).map(key => ({
key,
count: counts.created_by_ref[key],
}));
This will change the shape of the result but preserve all the properties of GraphQL.

GraphQL + Sequalize + existing database - "message": "parent.getPages is not a function" for one model not the other

GraphQL Query
Morning all,
I have a nice setup GraphQL -> Sequalize -> Existing DB (generated by a Laravel application). I've built this schema out:
type App {
id: ID!
userId: ID
user: User
pages: [Page]
experiments: [Experiment]
uri: String!
createdAt: String!
updatedAt: String!
}
type Page {
id: ID!
userId: ID
user: User
appId: ID!
app: App!
uri: String!
createdAt: String!
updatedAt: String!
}
type Experiment {
id: ID!
title: String!
appId: ID!
app: App!
createdAt: String!
updatedAt: String!
}
Based on the existing data. Querying an apps experiments works just great:
query {
app(id: 6) {
id
title
experiments {
id
}
}
}
{
"data": {
"app": {
"id": "6",
"title": "C-Map Embark: Boating",
"experiments": [
{
"id": "1"
}
]
}
}
}
But querying pages I get this:
query {
app(id: 6) {
id
title
pages {
id
}
}
}
{
"errors": [
{
"message": "parent.getPages is not a function",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"app",
"pages"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: parent.getPages is not a function",
...
The db columns are the same, as are the resolvers:
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
const Page = sequelize.define(
"page",
{
id: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
...
createdAt: {
type: DataTypes.DATE,
allowNull: true
},
updatedAt: {
type: DataTypes.DATE,
allowNull: true
}
},
{
tableName: "pages",
underscored: true
}
);
Page.associate = models => {
Page.belongsTo(models.app);
};
return Page;
};
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
const Experiment = sequelize.define(
"experiment",
{
id: {
type: DataTypes.INTEGER(10).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
...
createdAt: {
type: DataTypes.DATE,
allowNull: true
},
updatedAt: {
type: DataTypes.DATE,
allowNull: true
}
},
{
tableName: "experiments",
underscored: true
}
);
Experiment.associate = models => {
Experiment.belongsTo(models.app);
};
return Experiment;
};
Have you come across this before?

Unknown column 'getContent.movie_id' in 'field list

my whole schema
const Films = new GraphQLObjectType({
name: 'films',
interfaces: () => [MovieStream],
fields: () => ({
movie_id: {
type: GraphQLString,
},
id:{
type: GraphQLID
},
name: {
type: GraphQLString,
},
})
})
Films._typeConfig = {
sqlTable: "films",
uniqueKey: 'id',
}
const MovieStream = new GraphQLInterfaceType({
name: 'MovieStream',
fields: () => ({
id: {
type: GraphQLID,
},
movie_id: {
type: GraphQLString,
},
})
})
MovieStream._typeConfig = {
sqlTable: "movie_streams",
uniqueKey: 'id'
}
const QueryRoot = new GraphQLObjectType({
name: 'Query',
fields: () => ({
getContentList:{
type: new GraphQLList(Films),
args: {
id: {
type: GraphQLInt
},
permalink: {
type: GraphQLString
},
language: {
type: GraphQLString
},
content_types_id: {
type: GraphQLString
},
oauth_token:{
type: GraphQLString
}
},
resolve: (parent, args, context, resolveInfo) => {
return joinMonster.default(resolveInfo,{}, sql => {
return FilmDb.query(sql).then(function(result) {
return result[0];
});
} ,{dialect: 'mysql'});
},
}
})
})
module.exports = new GraphQLSchema({
query: QueryRoot
})
I have again modified my code still got the error
{
"errors": [
{
"message": "Unknown column 'getContent.movie_id' in 'field list'",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getContentList"
]
}
],
"data": {
"getContentList": null
}
}
My previous post Is it possible to fetch data from multiple tables using GraphQLList
Please check and tell me where i am wrong??? I have already add the field still it does not access the field of that object type

Resources