Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GraphQL: fulfill query from JSON file source

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'); 
like image 609
conner.xyz Avatar asked Sep 10 '17 03:09

conner.xyz


1 Answers

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.

like image 87
Daniel Rearden Avatar answered Oct 06 '22 01:10

Daniel Rearden