GraphQL: fulfill query from JSON file source - json

I've just started messing about with GraphQL, and I'd like a resolver that uses a JSON file on disk as the data source. What I've got so far causes GraphQL to return null.
How do I do this and why doesn't the approach below work?
var schema = buildSchema(`
type Experiment {
id: String
trainData: String
goldData: String
gitCommit: String
employee: String
datetime: String
}
type Query {
# Metadata for an individual experiment
experiment: Experiment
}
schema {
query: Query
}`);
var root = {
experiment: () => {
fs.readFile('./data/experimentExample.json', 'utf8', function(err, data) {
if (err) throw err;
console.log(data);
return JSON.parse(data);
});
}
};
const app = express();
app.use('/graphql', graphqlHTTP({
rootValue: root,
schema: schema,
graphiql: true
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

The callback function you're passing to readFile runs asynchronously, which means returning a value from it doesn't do anything -- the function the readFile call is inside is done executing and has returned a value (null) by the time your callback is done.
As a rule of thumb, when dealing with GraphQL, you should stay away from callbacks -- your resolvers should always return a value or a Promise that will eventually resolve to a value.
Luckily, fs has an asynchronous method for reading files, so you can just do:
const root = {
experiment: () => {
const file = fs.readFileSync('./data/experimentExample.json', 'utf8')
return JSON.parse(file)
}
};
// or even cleaner:
const root = {
experiment: () => JSON.parse(fs.readFileSync('./data/experimentExample.json', 'utf8'))
};
As an additional example, here's how you would do that with a Promise:
// using Node 8's new promisify for our example
const readFileAsync = require('util').promisify(fs.readFile)
const root = {
experiment: () => readFileAsync('./data/experimentExample.json', {encoding: 'utf8'})
.then(data => JSON.parse(data))
};
// Or with async/await:
const root = {
experiment: async () => JSON.parse(await readFileAsync('./data/experimentExample.json', {encoding: 'utf8'}))
};
Of course there's no need to promisify readFile since you already have an async method available, but this gives you an idea of how to work with Promises, which GraphQL is happy to work with.

Related

How to return a string (or JSON object) from a TypeScript function?

The following TypeScript function will retrieve data from a web API and display it to the console from within the function, but when I try to return the data object (a string) I get an error. I am trying to return the JSON data object by return data; but the compiler thinks I am not returning anything. Please, what am I doing wrong?
async function main(url: string, query: string): Promise<string> {
const axios = require('axios');
axios.get(url, {params: {"query": query}})
.then(function (response: any) {
let data = JSON.stringify(response.data);
console.log("data: " + data);
return data;
})
.catch(function (error: any) {
console.log(error);
return "error";
});
}
var url: string = 'https://api.subquery.network/sq/AcalaNetwork/karura-tokens';
var query = "{accountBalances (first: 5) {nodes{id accountId tokenId total}}}"
var res = main(url, query).catch(e => console.error(e)).finally(() => process.exit(0))
console.log("res:" + res);
Here is the output:
src/index.ts:1:50 - error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
1 async function main(url: string, query: string): Promise<string> {
In my example I am trying to return a string, but I am happy returning either a string or a JSON object. I just can't get either one to work.
One possible solution (a bit hacky) is to return the promise from your function, declare your res variable in the global scope and, in the promise callback, assign the value the promise resolves to res.
But the actual assignment will only happen after the promise resolves, so you might set a timeout to use the variable outside the promise callback.
For example:
function get_sq(url: string, query: string): Promise<any>{
const axios = require('axios');
return axios.get(url, {params: {"query": query}});
}
var url: string = 'https://api.subquery.network/sq/AcalaNetwork/karura-tokens';
var query = "{accountBalances (first: 5) {nodes{id accountId tokenId total}}}"
var res;
get_sq(url, query).then(function (response: any) {
res = JSON.stringify(response.data);
}).catch(function (error: any) {
res = error;
});
setTimeout(() => {console.log("res:" + res);}, 3000);
Another possibility (the more robust one) is to keep inside the promise callback and do what you want to do with res there.

Javascript - Return json from fetch in an Object

I'm trying to make an application to get the recipes from https://edamam.com and I'm using fetch and Request object.
I need to make 3 request, and i thought that most beautiful way for do it is make an Object and a method that return the data in JSON.
I declarated into constructor a variable called this.dataJson, and i want to save there the data in JSON from the response. For that purpose i use this.
The problem is that i have a undefined variable.
.then( data => {this.dataJson=data;
console.log(data)} )
This is all my code.
class Recipe{
constructor(url){
this.url=url;
this.dataJson;
this.response;
}
getJson(){
var obj;
fetch(new Request(this.url,{method: 'GET'}))
.then( response => response.json())
.then( data => {this.dataJson=data;
console.log(data)} )
.catch( e => console.error( 'Something went wrong' ) );
}
getData(){
console.log("NO UNDFEIND"+this.dataJson);
}
}
const pa= new Recipe('https://api.edamam.com/search?...');
pa.getJson();
pa.getData();
I'm new studying OOP in JS and more new in Fetch requests...
If you guys can help me... Thanks very much!
Here's a solution using async-await (and a placeholder API):
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
// the async keyword ensures that this function returns
// a Promise object -> we can use .then() later (1)
async getJson() {
try {
const response = await fetch(new Request(this.url, {
method: 'GET'
}))
const json = await response.json()
this.dataJson = json
} catch (e) {
console.error('Something went wrong', e)
}
}
getData() {
console.log("NO UNDFEIND:", this.dataJson);
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// 1 - here we can use the "then", as pa.getJson() returns
// a Promise object
pa.getJson()
.then(() => {
pa.getData()
});
If we want to stay closer to your code, then:
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
getJson() {
// var obj; // not needed
// the "fetch" always returns a Promise object
return fetch(new Request(this.url, { // return the fetch!
method: 'GET'
}))
.then(response => response.json())
.then(data => {
this.dataJson = data;
// console.log(data) // not needed
})
.catch(e => console.error('Something went wrong'));
}
getData() {
console.log("NO UNDFEIND:", this.dataJson); // different syntax here
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// using "then", because the "fetch" returned a Promise object
pa.getJson()
.then(() => {
pa.getData();
});
The problem with your original code is that you initiate the request (pa.getJson()) and then immediately (on the next line) you want to read the data (pa.getData()). pa.getData() is called synchronously (so it happens in milliseconds), but the request is asynchronous - the data needs time to arrive (probably hundreds of milliseconds) - so, it's not there when you try to read it (it simply hasn't arrived yet).
To avoid this you have to use a technique to handle this asynchronous nature of the request:
use a callback function (blee - so last decade)
use a Promise object with then() (much better) or async-await (yeee!)
and call the pa.getData() when the response has arrived (inside the callback function, in the then() or after awaiting the result).

discord.js/node.js make code wait until sql query returns result

I am working on a discord.js bot, and I'm storing a bunch of information on various servers in a database. The problem is, that the code doesn't wait for the database to return the results. In the current situation, I'm trying to check if the server specific prefix checks out.
I tried using async and await at various places, but those didn't work. If I could, I'd rather not use .then(), because I don't really want to put all the commands inside a .then().
const { Client, Attachment, RichEmbed } = require('discord.js');
const client = new Client();
const mysql = require("mysql");
const config = require("./config.json")
var con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'botdb'
})
client.on("ready", () => {
console.log("I'm ready")
})
client.on("message", message => {
if (message.author.bot) return;
if (message.channel.type === 'dm') return;
let msg = message.content.split(" ");
let command = msg[0];
let prefix;
con.query(`SELECT * FROM serversettings WHERE ServerID = ${message.guild.id}`, (err, rows) => {
if (err) throw err;
prefix = rows[0].Prefix;
console.log(prefix)
})
console.log(`Prefix: ${prefix}, Command: ${command}`)
if (command === `${prefix}examplecommand`) {
//Do something
}
//Other code that uses prefix and command
}
It should log the prefix first, and then the Prefix: ${prefix}, Command: ${command} part, but it does it the other way around, so the examplecommand doesn't work.
Your result is caused by the fact that what's outside your query callback is executed immediately after the call. Keep in mind the mysql module is callback-based.
Possible Solutions
Place the code inside the callback so it's executed when the query is completed.
Wrap the query in a promise and await it.
function getGuild(guildID) {
return new Promise((resolve, reject) => {
con.query(`SELECT * FROM serversettings WHERE ServerID = '${guildID}', (err, rows) => {
if (err) return reject(err);
resolve(rows);
});
});
}
const [guild] = await getGuild(message.guild.id) // destructuring 'rows' array
.catch(console.error);
console.log(guild.prefix);
Use a Promise-based version of a MySQL wrapper, like promise-mysql. You could use it the same way as the code above, without worrying about coding your own Promises.
const [guild] = await con.query(`SELECT * FROM serversettings WHERE serverID = '${message.guild.id}'`)
.catch(console.error);
console.log(guild.prefix);

From ES2018 async/await to ES2015 Promises . ... timeout

I am trying to convert an ES2018 async function into an ES2015 (ES6) function, but I get a timeout, guess my ES2015 version is wrong...but where?
ES2018 version
async function connectGoogleAPI () {
// Create a new JWT client using the key file downloaded from the Google Developer Console
const client = await google.auth.getClient({
keyFile: path.join(__dirname, 'service-key.json'),
scopes: 'https://www.googleapis.com/auth/drive.readonly'
});
// Obtain a new drive client, making sure you pass along the auth client
const drive = google.drive({
version: 'v2',
auth: client
});
// Make an authorized request to list Drive files.
const res = await drive.files.list();
console.log(res.data);
return res.data;
}
ES2015 version w/Promise
function connectGoogleAPI () {
return new Promise((resolve, reject) => {
const authClient = google.auth.getClient({
keyFile: path.join(__dirname, 'service-key.json'),
scopes: 'https://www.googleapis.com/auth/drive.readonly'
});
google.drive({
version: 'v2',
auth: authClient
}), (err, response) => {
if(err) {
reject(err);
} else {
resolve(response);
}
}
});
}
You haven't translated the await of getClient. Remember, await = then (roughly). You're also falling prey to the promise creation anti-pattern: When you already have a promise (from getClient), you almost never need to use new Promise. Just use then.
Here's an example with each await converted into a then, using the chain for the subsequent operations:
function connectGoogleAPI () {
// Create a new JWT client using the key file downloaded from the Google Developer Console
return google.auth.getClient({
keyFile: path.join(__dirname, 'service-key.json'),
scopes: 'https://www.googleapis.com/auth/drive.readonly'
}).then(client => {
// Obtain a new drive client, making sure you pass along the auth client
const drive = google.drive({
version: 'v2',
auth: client
});
// Make an authorized request to list Drive files.
return drive.files.list();
}).then(res => {
console.log(res.data);
return res.data;
});
}
That last part can be just
}).then(res => res.data);
...if you remove the console.log. (Or we could abuse the comma operator.)
Notes:
Each await needs to become a then handler (there were two in the original, awaiting getClient and drive.files.list)
In a then handler, if you have to wait for another promise (such as the one from drive.files.list) you typically return it from the handler, and then use another handler to handle that result (which is why I have return drive.files.list() and then a separate handler for converting res to res.data)
Re that second point: Sometimes nesting is appropriate, such as when you need to combine the result with some intermediate value you only have with in your then handler. (For instance, if we wanted to combine res.data with client.) But generally, prefer not to nest.

node js mysql rest api - queue http requests - persist data

What is the best architectural design for following scenario?
I want to build a CRUD-microservice using node, express and mysql. The CREATE portion is quite complex due to a large piece of json with many relational properties on each http POST request. The request.body is looking something like this:
{
key1: string <-- saved as foreign_key
key2: {...}
key3: int
key4: [ <-- saved as n:m with corresponding table
{...},
{...},
]
...
...
keyXYZ: ...
key46: int <-- saved as foreign_key
key47: string
}
The module which does all query-operations looks like this:
persistData = async (data, dbConnection) => {
const idSomething1 = await fetchOrCreateSomething1(data.key1).catch (err => console.log(err));
const idSomething2 = await fetchOrCreateSomething2(data.key46).catch (err => console.log(err));
const idSomething3 = await fetchOrCreateSomething3(data.keyXYZ).catch (err => console.log(err));
const idManyThings = await fetchOrCreateManyThings(idSomething1, idSomething2, idSomething3, data.moreStuff...).catch (err => console.log(err));
}
All fetchOrCreateSomethingX = async () => {} functions are async to let the main function persistData wait for an newly created or retrieved record id.
This is wrapped inside an exported constructor function:
function DataHandler(data, res) {
db.getConnection()
.then((dbConnection) => {
persistData(data, dbConnection)
.then(() => {
dbConnection.release();
});
});
}
module.exports = DataHandler;
The endpoint does the following:
const createFunc = (req, res) => {
new DataHandler(req.body, res);
};
app.post("/create", createFunc);
I know that especially the last part does not work because the new DataHandler-object is overwritten as soon as the endpoint gets hit again. If the persisting process hasn't finished before the endpoint gets hit again the data from the first request is lost. I also know that express won't be able to send back responses which isn't ideal. If the new DataHandler instead would be stored to a new variable or const than at least both processes would run. But the main problem is that the data gets shuffled as the persistData() is running in parallel not encapsulated from each other.
I can't find any example or best practice how to design this well. Any hint or resource would be great!
Is a queuing system like the kue library the way to go?