Can't use #cypher in GraphQL schema when using ApolloWebserver - node.js

I want to query a field on a node using the #cypher directive in my GraphQL schema.
However when I query the field I get Resolve function for \"Link.x\" returned undefined.
My schema with the directive on x from Link is the following
scalar URI
interface IDisplayable{
"Minimal data necessary for the object to appear on screen"
id: ID!
label: String
story: URI
}
interface ILink{
"""
A link must know to what nodes it is connected to
"""
x: Node! #cypher(statement: "MATCH (this)-[:X_NODE]->(n:Node) RETURN n")
y: Node!
"""
if optional=true then sequence MAY be used to define a set of options
"""
optional: Boolean
}
interface INode{
synchronous: Boolean
unreliable: Boolean
}
type Node implements INode & IDisplayable{
id: ID!
label: String!
story: URI
synchronous: Boolean
unreliable: Boolean
}
type Link implements ILink & IDisplayable{
id: ID!
label: String!
x: Node! #cypher(statement: "MATCH (this)-[:X_NODE]->(n:Node) RETURN n")
y: Node!
story: URI
optional: Boolean
}
When querying for a a link and its x property I get undefined. With the custom resolver that I wrote for y however it works. Of course I could leave the hand written resolvers but its a lot of code that is not necessary.
This is index.js:
require( 'dotenv' ).config();
const express = require( 'express' );
const { ApolloServer } = require( 'apollo-server-express' );
const neo4j = require( 'neo4j-driver' );
const cors = require( 'cors' );
const { makeAugmentedSchema } = require( 'neo4j-graphql-js' );
const typeDefs = require( './graphql-schema' );
const resolvers = require( './resolvers' );
const app = express();
app.use( cors() );
const URI = `bolt://${ process.env.DB_HOST }:${ process.env.DB_PORT }`;
const driver = neo4j.driver(
URI,
neo4j.auth.basic( process.env.DB_USER, process.env.DB_PW ),
);
const schema = makeAugmentedSchema( { typeDefs, resolvers } );
const server = new ApolloServer( {
context: { driver },
schema,
formatError: ( err ) => {
return {
message: err.message,
code: err.extensions.code,
success: false,
stack: err.path,
};
},
} );
const port = process.env.PORT;
const path = process.env.ENDPOINT;
server.applyMiddleware( { app, path } );
app.listen( { port, path }, () => {
console.log( `Server listening at http://localhost:${ port }${ path }` );
} );
With "graphql-schema.js" being
const fs = require( 'fs' );
const path = require( 'path' );
const schema = './schemas/schema.graphql';
const encoding = 'utf-8';
let typeDefs = '';
typeDefs += fs.readFileSync( path.join( __dirname, schema ) )
.toString( encoding );
module.exports = typeDefs;
Thanks for any tips

I found out that, if I write a custom resolver for a query, the directives provided by Apollo do not work.
However I realized that I can let Apollo create the queries that I needed so I just deleted my custom implementations, which works for me.
So in my resolvers I had to remove implementations for queries that would fetch fields annotated with a #cypher query, then I could put the directive into my schema and they worked fine.

Related

Cannot GET /[object%20Object] when calling axios.get()

When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...
You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.
The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.

Mongoose not resolving callback queries?

I have been working on this project for 2 years now, and I'm thinking this was caused by the recent update, but am wondering if there are any kind, intelligent, Mongoose/NoSQL DBA, souls out there who would do the awesome service of helping me either track-down, and/or resolve this issue.
So, as you can see below, this is a simple mongoose find query over express to MongoDB. This is rather evident, at a high-level, and for most devs, the interactions will be natural, as any Mongo, Express, Node Stack using Mongoose.
The is issue is that, when I send this query, disregarding environment (a production project), it does not resolve.
The "data" seems to get lost somewhere, and therefore, the query simply never resolves.
It's a simple setup, really a test endpoint, so help out, run it through, and send some feedback.
Greatly Appreciated!
Model.js
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate');
const Schema = mongoose.Schema;
const TestSchema = new Schema({
data: {
type: String,
unique: false,
required: true
},
}, {
timestamps: true
});
TestSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('Test', TestSchema);
Constructor.js
class Constructor {
constructor() {}
getAll() {
return TestSchema.find({}, function (err, tests) {})
}
}
module.exports = Constructor
db.js
let mongoose = require('mongoose')
// Connect to db
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true }, err => {
if (err)
return console.log("Cannot connect to DB")
connectionCallback()
console.log("DB Connected")
});
let connectionCallback = () => {}
module.exports.onConnect = cb => {
connectionCallback = cb
}
App.js
const express = require('express');
const app = express();
const ip = require('ip');
let db = require('./db')
const router = express.Router();
const port = 8888;
const http = require('http').createServer(app);
let ipAddress = 'localhost'; // only works to the local host
try {
// will enable the server to be accessed from the network
ipAddress = ip.address();
} catch( err ){
console.err( err );
}
http.listen(port, ipAddress,
() => {
let message = [
`Server is running at ${ipAddress}:${port}`,
];
console.log( ...message )
});
db.onConnect(() => {
let Constructor = require("./pathTo/Constructor")
let construct = new Constructor()
app.use('/api', router.get('/test', function(req, res) {construct.getAll()}))
})
Your problem is with the constructor.js getAll function, as you are returning also and passed a callback also, the promise will never be resolved. You should either resolve the promise or return the response from the callback.
Resolve Promise:
class Constructor {
constructor() {}
async getAll() {
return await TestSchema.find({})
}
}
module.exports = Constructor
Return from callback:
class Constructor {
constructor() {}
getAll() {
TestSchema.find({}, function (err, tests){
return tests.
})
}
}
module.exports = Constructor
I ended up just scaling the project for production. I put the connectionCallback in a class and called it with the createConnection mongoose function.
Looks like this:
mongoose.Promise = global.Promise;
const url = 'mongodb://localhost/db'
const connection = mongoose.createConnection(url, options);
//load models
require('/models').connectionCallback();
modules.export = connectionInstance;
Please note, I am no longer using express!

ObjectionJS - Group models in a data layer file

I have a NodeJS app running fastify with fastify-objectionjs.
For tidiness, I'd like to group all models in a single file called _main.js, where I export an array of the models inside the models folder.
Since the fastify-objectionjs registration requires an array of models, I thought I could just import the array from my _main.js and feed it as it is to the registration function.
But ObjectionJS is telling me that The supplied models are invalid.
/app.js (node entry point)
const fastify = require('fastify')({
logger: true
})
const knexConfig = require('./knexfile')
const dataLayer = require('./models/_main')
fastify.register(require('fastify-objectionjs'), {
knexConfig: knexConfig,
models: dataLayer
})
// Also tried:
// fastify.register(require('fastify-objectionjs'), {
// knexConfig: knexConfig,
// models: [dataLayer]
// })
/models/_main.js
const User = require('./user.model')
var dataLayer = [User]
module.exports = dataLayer
// Also tried without var:
// module.exports = {
// dataLayer: [
// User
// ]
// }
/models/user.model.js
const Knex = require('knex')
const connection = require('../knexfile')
const { Model } = require('objection')
const knexConnection = Knex(connection)
Model.knex(knexConnection)
class User extends Model {
static get tableName () {
return 'users'
}
}
module.exports = { User }
I can't seem to find a problem in the file flow, but if I create the models array on the fly, the app starts smoothly:
/app.js (node entry point)
const fastify = require('fastify')({
logger: true
})
const knexConfig = require('./knexfile')
const User = require('./models/user.model') // changed
fastify.register(require('fastify-objectionjs'), {
knexConfig: knexConfig,
models: [User] // changed
})
Any idea why this isn't working?
Thanks in advance for your time.
Found the gotcha, I just needed to use destructuring in the require of User, like this:
/models/_main.js
// BAD
// const User = require('./user.model')
// GOOD
const { User } = require('./user.model')
module.exports = [User]
Works like a charm.
Useful question that explains the difference:
Curly brackets (braces) in node require statement

buildSchema is not a function graphql npm

The official website of graphql refers to this graphql npm to create the helloworld .
var { graphql, buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
hello: String
}
`);
var root = { hello: () => 'Hello world!' };
graphql(schema, '{ hello }', root).then((response) => {
console.log(response);
});
When i run this file with node cmd , I got :
var schema = buildSchema(`
^
TypeError: buildSchema is not a function
at Object. (
I guess the offcial website of graphql is not up-to-date with this repository , so , i tried to use GraphQLSchema instead of buildSchema
var schema = GraphQLSchema(`
...
`)
I go another error:
TypeError: Cannot call a class as a function
Thus , i called new GraphQLSchema(... instead of GraphQLSchema(...
I got another error :
Error: Must provide configuration object.
So , what i did as next step, I explored the object and i filter functions contains schema in its name
const graphql = require('graphql');
const functionsContainsSchema= Object.keys(graphql).filter(e => e.toLowerCase().includes('schema') && typeof graphql[e] ==='function');
console.log(
functionsContainsSchema
)
/* STDOUT
[ 'GraphQLSchema',
'buildClientSchema',
'buildASTSchema',
'extendSchema',
'printSchema' ]
*/
What is the new version of buildSchema and what is its signature ?

Get model from mongoose db

I'm currently looking into building a small REST based service to which I can POST some data into a mongoose db and GET the data back.
Here's my main.js file:
var http = require("http");
var DAO = require("./DAO");
var express = require("express");
var util = require('util');
var app = express();
app.use(express.bodyParser());
app.post('/postIsles',function(req,res){
DAO[req.method](req.body);
res.send("body" + req.body.name);
});
app.get('/getIsles',function(req,res){
var isleVar = DAO[req.method](req);
res.send(isleVar);
});
app.listen("3000");
console.log("\nApp available at http://127.0.0.1:3000\n");
And DAO.js:
var mongoose = require('mongoose');
//Connect to database
mongoose.connect( 'mongodb://127.0.0.1:27017/library_database' );
//Schemas
var Isle = new mongoose.Schema({
name: String,
description: String,
lastStocked: Date
});
//Models
var IsleModel = mongoose.model( 'Isle', Isle );
function POST(request) {
var name = request.name;
var description = request.description;
var lastStocked = request.lastStocked;
console.log("POST REQ +" + request);
var isle = new IsleModel({
name: name,
description: description,
lastStocked: lastStocked
});
isle.save( function( err ) {
if( !err ) {
return console.log( 'created' );
} else {
return console.log( err );
}
});
}
function GET(request) {
return IsleModel.find( function( err, islesT ) {
if( !err ) {
console.log("isles :"+islesT);
return islesT;
} else {
return console.log( err );
}
});
}
exports.POST = POST;
exports.GET = GET;
When I try to run the GET, I get the following error:
TypeError: Converting circular structure to JSON
at Object.stringify (native)
I'm a bit unsure how to overcome this.
Remember when using Node.js: any operation that involves IO will be asynchronous.
Model#find is an asynchronous method, so isleVar is not set to the result you're expecting. Your result will only be available inside of the anonymous function that you pass into IsleModel.find
To fix your GET method, you'll need to modify your code to take into account the asynchronicity of the DB request and only send the response once your app has had a chance to retrieve data.
Below, is an example of one possible solution to fix /getIsles:
In main.js, modify your get route to pass in res (so it can be handled asynchronously)
app.get('/getIsles',function(req,res){
return DAO[req.method](req, res);
});
In DAO.js, have response send the data inside of your callback to IsleModel.find
function GET(request, response) {
IsleModel.find( function( err, islesT ) {
if( !err ) {
console.log("isles :"+islesT);
response.send(islesT);
} else {
return console.log( err );
}
});
}

Resources