Apollo GraphQL: Call a Mutation from the Server? - graphql

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 }),
});

Related

Nuxt Apollo with dynamic headers for a session based authentication

Apollo is not storing the header from the query dynamically.
pages/index.vue
methods: {
fetchCars() {
const token = Cookies.get('XSRF-TOKEN')
console.log(token) // 🟢 Token is shown in console
this.$apollo.query({
query: gql`
query {
cars {
uuid
name
}
}
`,
headers: {
'X-XSRF-TOKEN': token, // â­• Fetch without header
},
})
},
},
Is there a way to set the header value new for every Apollo request?
I have a separate Frontend and Backend. For the Frontend I am using Nuxt.js with Apollo. I want to have a session based communication with my server. For this reason I need to send the CSRF-Token with every Request.
Now the problem: On the first load of the page there is no Cookie set on the browser. I do a GET-Request on every initialization of my Nuxt application.
plugins/csrf.js
fetch('http://127.0.0.1:8000/api/csrf-cookie', {
credentials: 'include',
})
Now I have a valid Cookie set on my side and want to communicate with the GraphQL Server but my header is not set dynamically in the query. Does anyone know how I can solve this?
My Laravel Backend is throwing now a 419 Token Mismatch Exception because I did not send a CSRF-Token with my request.
Link to the repository: https://github.com/SuddenlyRust/session-based-auth
[SOLVED] Working solution: https://github.com/SuddenlyRust/session-based-auth/commit/de8fb9c18b00e58655f154f8d0c95a677d9b685b Thanks to the help of kofh in the Nuxt Apollo discord channel 🎉
In order to accomplish this, we need to access the code that gets run every time a fetch happens. This code lives inside your Apollo client's HttpLink. While the #nuxtjs/apollo module gives us many options, we can't quite configure this at such a high level.
Step 1: Creating a client plugin
As noted in the setup section of the Apollo module's docs, we can supply a path to a plugin that will define a clientConfig:
// nuxt.config.js
{
apollo: {
clientConfigs: {
default: '~/plugins/apollo-client.js'
}
}
}
This plugin should export a function which receives the nuxt context. It should return the configuration to be passed to the vue-cli-plugin-apollo's createApolloClient utility. You don't need to worry about that file, but it is how #nuxtjs/apollo creates the client internally.
Step 2: Creating the custom httpLink
In createApolloClient's options, we see we can disable defaultHttpLink and instead supply our own link. link needs to be the output of Apollo's official createHttpLink utility, docs for which can be found here. The option we're most interested in is the fetch option which as the docs state, is
a fetch compatible API for making a request
This boils down to meaning a function that takes uri and options parameters and returns a Promise that represents the network interaction.
Step 3: Creating the custom fetch method
As stated above, we need a function that takes uri and options and returns a promise. This function will be a simple passthrough to the standard fetch method (you may need to add isomorphic-fetch to your dependencies and import it here depending on your setup).
We'll extract your cookie the same as you did in your question, and then set it as a header. The fetch function should look like this:
(uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
Putting it all together
Ultimately, your ~/plugins/apollo-client.js file should look something like this:
import { createHttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-fetch'
export default function(context) {
return {
defaultHttpLink: false,
link: createHttpLink({
uri: '/graphql',
credentials: 'include',
fetch: (uri, options) => {
const token = Cookies.get('XSRF-TOKEN')
options.headers['X-XSRF-TOKEN'] = token
return fetch(uri, options)
}
})
}
}

Log Query/Mutation actions to database for Auditing

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.

Apollo client: Can #defer be used with client side resolvers?

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.

Context cleanup with apollo graphql server

I can create a context function in ApolloServer that will be executed before each request.
How can I have a cleanup function (after request execution)?
not sure if this is still relevant but I found this medium article addressing this exact question.
You can use this snippet for when you want code to run after all the resolvers have run:
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
...
},
plugins: [
{
requestDidStart: () => ({
willSendResponse: response => {
// this will run after every request
if (response.context.db) {
response.context.db.close();
}
}
})
}
]
});
I looked in the docs and this plugin system is nowhere to be found. I haven't found anything to discourage use like this but it does not seem to be an official solution.
I tried it in my project and it does seem to work.
Not sure why you would like to have context cleanup function? The context is set before as the middleware for each request therefore if you would like to have context set differently for another request you just inject the logic to the middleware. Maybe i just do not understand your use case for this. It would be helpful if you can clarify why you would like to apply it? The context is set per request, therefore you can inject enmpty object for some requests based on the req. If you need to clear up information from req object after graphql middleware you can do for example another middleware where you will set req.user (if you have authenticated user there) to null.

Inspecting a remote graphql endpoint with graphiql

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.

Resources