There is a graphql endpoint which I don't own but which provides a public endpoint. I'm hoping to introspect it using graphiql. I'm totally new to graphql, so I don't even know if this sort of thing is possible.
I have the graphiql example running locally and am modifying server.js to try to make it work. Poking around at other SO threads has gotten me this far...
var introspectionQuery = require('graphql/utilities').introspectionQuery;
var request = require('sync-request');
var url = 'http://endpoint.com/graphql';
var response = request('POST', url, { qs: { query: introspectionQuery } } );
var schema = JSON.parse(response.body.toString('utf-8'));
// herein lies the rub
schema = new GraphQLSchema(schema.data.__schema);
var app = express();
app.use(express.static(__dirname));
app.use('/graphql', graphqlHTTP(() => ({
schema: schema,
})));
app.listen(8080);
This code blows up in the GraphQLSchema constructor, trying to make a schema out of that introspection query. Clearly that's not quite the right approach?
What you want to build schema out of the introspection result is buildClientSchema:
var buildClientSchema = require('graphql/utilities').buildClientSchema;
var introspectionQuery = require('graphql/utilities').introspectionQuery;
var request = require('sync-request');
var response = request('POST', url, { qs: { query: introspectionQuery } });
// Assuming we're waiting for the above request to finish (await maybe)
var introspectionResult = JSON.parse(response.body.toString('utf-8'));
var schema = buildClientSchema(introspectionResult);
You could build the schema in two other ways: buildASTSchema and instantiating GraphQLSchema directly, which is what you're trying out. GraphQLSchema constructor takes in an object with GraphQLSchemaConfig type:
type GraphQLSchemaConfig = {
query: GraphQLObjectType;
mutation?: ?GraphQLObjectType;
subscription?: ?GraphQLObjectType;
types?: ?Array<GraphQLNamedType>;
directives?: ?Array<GraphQLDirective>;
};
And those two utility modules provide easier ways to build the schema from either from introspection query result or parsed IDL type definitions, respectively by using buildClientSchema or buildASTSchema. Refer to those modules in graphql-js/src/utilities directory for more information please.
I was trying this with a PHP GraphQL library. I hit lots of issues experimenting with the above around CORS (cross origin security stuff).
Then I discovered GraphIQL is available as a Chrome app. That resolved my need, so noting here in case useful to anyone else who comes across this issue. You don't need to do any coding to get GraphIQL working with a remote endpoint.
Related
I want to convert a GraphQL file to a Postman collection. I tried with a JavaScript library to do that (https://www.npmjs.com/package/graphql-to-postman).
But I'm getting the following error:
Unhandled Rejection (TypeError): Cannot set property
'includeDeprecatedFields' of undefined
function convert() {
var postmanJson = fileReader.result,
fileDownload = require('js-file-download');
const graphQlToPostman = require('graphql-to-postman');
const collection = graphQlToPostman.convert(postmanJson);
fileDownload(
JSON.stringify(collection),
'postman collection',
);
}
This is the function where I used the library.
To convert graphql to postman collection, first you need graphql schema. Graphql schema can be downloaded by running introspection query on the graphql server end point. Here is how to do it.
Install graphql-cli
npm i -g apollo
Download schema from the graphql server
apollo schema:download --endpoint=http://localhost:4000/graphql schema.json
Convert schema into graphql collection
const fs = require('fs')
const converter = require('graphql-to-postman')
const schema = fs.readFileSync('./schema.json')
converter.convert({
type: 'string',
data: schema.toString(),
}, {}, async function (error, result) {
if (error) {
log.error('Conversion failed')
} else {
const outputData = result.output[0].data
fs.writeFileSync('output-collection.json', JSON.stringify(outputData))
console.log('Conversion success');
}
})
I've built an easy-to-use command-line tool that allows you to automatically generate your Postman collection from your GraphQL endpoint. (https://www.npmjs.com/package/graphql-testkit)
It also comes with out-of-the-box support to control the maximum depth and add headers to all the requests in the collection.
Simply doing this, after replacing the endpoint, and adding or removing headers based on your requirement will auto-generate a Postman Collection with support for variables.
graphql-testkit \
--endpoint=https://api/spacex.land/graphql\
--header="Authorization:123,x-ws-system-id=10" \
--maxDepth=4
I need to call a mutation from a cron job running on the server. I found this SO post, but the accepted answer said there was no way to do it.
Then I found this on GitHub, from 2017:
graphql-server is a network wrapper for graphql core function. if you
don't want to use it over network, you can just run it standalone:
import scheme from './scheme';
const query = `{
me {
name
}
}`;
const result = graphql(scheme, query);
console.log(result);
function documentation can be found here
That looks pretty good! Is that the best practices approach in 2020?
Yes, if you have access to the GraphQLSchema object used by your GraphQL server, then the simplest approach is to just use the graphql function exported by the graphql module. Note that you should await the returned Promise to access the result:
async function run () {
const { data, errors } = await graphql(
schema,
query,
rootValue,
context,
variables
)
}
However, you can also make requests to the server itself over the network -- if the server is on the same host, you'd just use localhost or 127.0.0.1. In that case, you can use axios, request or any other HTTP client library in a Node.js script. You can also just use curl to make the request directly in a bash script or curl command.
I just found out about this approach as well. It's possible to create an Apollo client directly on the server.
export const getApolloServerClient = () =>
new ApolloClient({
ssrMode: true,
cache: new InMemoryCache(),
link: new SchemaLink({ schema }),
});
My goal is to run some kind of webhook, cloud function or say I want to perform some kind of action after each query success or mutation success in graphql.
Means I want to log each and every action performed by users (kind of history of when what was created and updated).
How can this be implemented using some kind of middleware between graphql and DB (say mongo for now)?
Means that middleware should be responsible to run the logging action each time a query or mutation is called from front-end.
Tech stack being used is- Node, express, graphQl, Redis etc.
Any suggestions would really be appreciated.
Thanks
The solution I came up with was calling a function manually each time a query or mutate.
If you're using Apollo, you can utilize the formatResponse and formatError options for logging, as outlined in the docs.
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: error => {
console.log(error);
return error;
},
formatResponse: response => {
console.log(response);
return response;
},
});
Using an extension can allow you to hook into different phases of the GraphQL request and allow more granular logging. A simple example:
const _ = require('lodash')
const { GraphQLExtension } = require('graphql-extensions')
module.exports = class LoggingExtension extends GraphQLExtension {
requestDidStart(options) {
logger.info('Operation: ' + options.operationName)
}
willSendResponse(o) {
const errors = _.get(o, 'graphqlResponse.errors', [])
for (const error of errors) {
logger.error(error)
}
}
}
There's a more involved example here. You can then add your extension like this:
const server = new ApolloServer({
typeDefs,
resolvers,
extensions: [() => new YourExtension()]
});
If you're using express-graphql to serve your endpoint, your options are a bit more limited. There's still a formatError option, but no formatResponse. There is a way to pass in an extensions array as well, but the API is different from Apollo's. You can take a look at the repo for more info.
For some reason, I had to build a client-side only GraphQL server, my schema is built as follow:
private buildSchema(): GraphQLSchema {
const allTypes: string = ...// my types
const allResolvers: IResolvers[] = ...// my resolvers
return makeExecutableSchema({
typeDefs: allTypes,
resolvers: allResolvers
});
}
The client is as follow:
this.client = new ApolloClient({
link: new SchemaLink({schema: this.buildSchema()}),
cache: new InMemoryCache({
addTypename: false
})
});
And everything works fine except that my queries are not defered. For instance if I run:
const gqlQuery: string = `
{
user {
name
slowResolver #defer {
text
}
}
}
`
const $result = this.apollo.getClient().watchQuery({
query: gql(gqlQuery)
});
The $result will be emited only when the whole query will be resolved (instead of user and then slowResolver as expected).
Any idea of what I missed in the workflow?
The #defer directive was actually removed from Apollo, although there's been some work done to reimplement it. Even if it's implemented, though, deferred queries would have to be handled outside of the execution context. In other words, executing the schema can return a deferred execution result, but something else (like Apollo server itself) has to handle how that response (both the initial payload, and the subsequent patches) are actually sent to the server over whatever transport.
If you're defining a schema client-side, unfortunately, it's not going to be possible to use the #defer directive.
A Client needs a specific JSON structure which I wanted to provide by an GraphQL Response.
Unfortunately I have to get rid of the top level "data" field and flatten the response for that client.
Is there a way to do this by a resolver?
From:
{
"data" : {
"myKey":
{...}
}
}
To:
{
"myKey":
{...}
}
Thanks!
It's technically possible by utilizing the formatResponse option passed in to ApolloServer's constructor:
const formatResponse = ({ data, errors }) => data
const server = new ApolloServer({ typeDefs, resolvers, formatResponse })
or to do that for a specific query (for example, status), you can do:
const formatResponse = res => {
if (res.data && res.data.status) return res.data
return res
}
However, I would highly advise against this sort of approach for two main reasons. One, it breaks the spec, which is going to make your API incompatible with most client libraries out there designed for explicitly working with GraphQL APIs. Two, it leaves you with either having to inject your errors (validation or otherwise) into your actual data somewhere, or leaving them out altogether.
It's hard to imagine a scenario where pulling the data out of the response shouldn't be done by the client application -- and if you're having a hard time with that on a particular framework, that sounds like a good follow up SO question!