Flatten Apollo GraphQL Response (Data Field) - graphql

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!

Related

How to work with both mocked graphql API and an externally served GraphQL endpoint

I'm hoping to hear some inputs from the experts here.
I'm currently working on NextJS project and my graphql is running on mocked data which is setup in another repo.
and now that the backend is built by other devs were slowly moving away from mocked data to the real ones.
They've given me an endpoint to the backend where I'm supposed to be querying data.
So the goal is to make both mocked graphql data and the real data in backend work side by side at least until we fully removed mocked data.
So far saw 2 ways of doing it, but I was looking for a way where I could still use hooks like useQuery and useMutation
Way #1
require('isomorphic-fetch');
fetch('https://graphql.api....', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `
query {
popularBrands ( storefront:"bax-shop.nl", limit:10, page:1){
totalCount
items{id
logo
name
image
}
}
}`
}),
})
.then(res => res.json())
.then(res => console.log(res.data));
Way #2
const client = new ApolloClient({
uri: 'https://api.spacex.land/graphql/',
cache: new InMemoryCache()
});
async function test () {
const { data: Data } = await client.query({
query: gql`
query GetLaunches {
launchesPast(limit: 10) {
id
mission_name
launch_date_local
launch_site {
site_name_long
}
links {
article_link
video_link
mission_patch
}
rocket {
rocket_name
}
}
}
`
});
console.log(Data)
}
Pseudo code:
Query the real data first
check if its empty, if it is, query the mock data.
If both are empty, then it's really an empty result set.
You can write a wrapper around the hooks you use that does this for you so you don't have to repeat yourself in every component. When you're ready to remove the mocked data you just remove the check for the second. data set.
This is a common technique when switching to a new database.

Use Apollo server as a pass through of a query from client

I have a use case where I have apollo-server-express running with a React based apollo-client. I have an external graphql-datasource for some queries. Currently, I've configured apollo-datasource-graphql to be used as a data source for my apollo-server-express. However, this requires duplication of work on the resolver in Apollo as well as the resolver on my external graphql system.
Is there a way for me to pass queries made in the client through the Apollo Server and to the external graphql data source?
Maybe you could access the GraphQL AST from the fourth resolver argument (resolveInfo) and pass it into a GraphQL client?
Here is some prototype code:
import { print } from 'graphql/language/printer';
function forwardOperationResolver(root, args, context, resolveInfo) {
return fetch('https://remote.host/graphql', {
method: 'POST',
body: JSON.stringify({
query: print(resolveInfo.operation),
variables: resolverInfo.variableValues,
}),
})
.then(response => response.json())
.then(response => {
if (response.errors) {
// Handle errors
}
return response.data;
});
}
Downside: This breaks a few things that usually work in GraphQL like partial results and error locations...

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.

How to send a GraphQL Query from Node.js to Prisma

I just learnt how to create a GraphlQL server using graphql-yoga and prisma-binding based on the HowToGraphQL tutorial.
Question: The only way to query the database so far was to use the Prisma Playground webpage that was started by running the command graphql playground.
Is it possible to perform the same query from a Node.js script? I came across the Apollo client but it seems to be meant for use from a frontend layer like React, Vue, Angular.
This is absolutely possible, in the end the Prisma API is just plain HTTP where you put the query into the body of a POST request.
You therefore can use fetch or prisma-binding inside your Node script as well.
Check out this tutorial to learn more: https://www.prisma.io/docs/tutorials/access-prisma-from-scripts/access-prisma-from-a-node-script-using-prisma-bindings-vbadiyyee9
This might also be helpful as it explains how to use fetch to query the API: https://github.com/nikolasburk/gse/tree/master/3-Use-Prisma-GraphQL-API-from-Code
This is what using fetch looks like:
const fetch = require('node-fetch')
const endpoint = '__YOUR_PRISMA_ENDPOINT__'
const query = `
query {
users {
id
name
posts {
id
title
}
}
}
`
fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: query })
})
.then(response => response.json())
.then(result => console.log(JSON.stringify(result)))
If you want to use a lightweight wrapper around fetch that saves you from writing boilerplate, be sure to check out graphql-request.
And here is how you use Prisma bindings:
const { Prisma } = require('prisma-binding')
const prisma = new Prisma({
typeDefs: 'prisma.graphql',
endpoint: '__YOUR_PRISMA_ENDPOINT__'
})
// send `users` query
prisma.query.users({}, `{ id name }`)
.then(users => console.log(users))
.then(() =>
// send `createUser` mutation
prisma.mutation.createUser(
{
data: { name: `Sarah` },
},
`{ id name }`,
),
)
.then(newUser => {
console.log(newUser)
return newUser
})
.then(newUser =>
// send `user` query
prisma.query.user(
{
where: { id: newUser.id },
},
`{ name }`,
),
)
.then(user => console.log(user))
Since you are using Prisma and want to query it from a NodeJS script, I think you might have overlooked the option to generate a client from your Prisma definitions.
It takes care of handling create/read/update/delete/upsert methods depending on your datamodel.
Also, you can worry less about keeping your models and queries/mutations in sync since it is generated using the Prisma CLI (prisma generate).
I find it to save a lot of coding time compared to using raw GrahQL queries, which I save for more complicated queries/mutations.
Check their official documentation for more details.
Also, note that using the Prisma client is the recommended way of using Prisma in prisma-binding resository, unless:
Unless you explicitly want to use schema delegation
which I can't tell you much about.
I did not know of the prisma-binding package untill I read your question.
EDIT:
Here is another link that puts them both in perspective

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