Running GraphQL as Azure Function app says POST body missing, invalid Content-Type, or JSON object has no keys - graphql

I am getting this message POST body missing, invalid Content-Type, or JSON object has no keys when using the Test/Run options in the code editor of Azure.
Here is my Typescript code that works locally fine but not when running through the Azure portal. Where should I make the changes to make it work on the Azure portal?
import { ApolloServer, gql } from "apollo-server-azure-functions"
const quizzes = [
{
id: '1',
question: 'Hobbies',
correctAnswer: 'Technology'
}
];
const typeDefs = gql`
type Quiz {
id: String!
question: String!
correctAnswer: String!
}
type Query {
quizzes: [Quiz!]!
quiz(id: String!): Quiz!
}
type Mutation {
createQuestionInput(id: ID!, question: String!, correctAnswer: String!): Quiz
}
`
const resolvers = {
Query : {
quizzes: async (parent, args, context, info) => {
return quizzes;
},
quiz: async (parent, args, context, info) => {
return quizzes.find((quiz) => quiz.id == args.id);
}
},
Mutation: {
createQuestionInput: async(parent, args, context, info) => {
quizzes.push({id: args.id,
question: args.question,
correctAnswer: args.correctAnswer});
return quizzes.find((quiz)=> quiz.id ==args.id);
}
}
}
const server = new ApolloServer({ typeDefs, resolvers})
export default server.createHandler();
function.json
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post",
"options"
]
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
],
"scriptFile": "../dist/graphql-sumesh-fn/index.js"
}

Related

GraphQL: POST body missing, invalid Content-Type, or JSON object has no keys

I am trying to create a mutation but I keep getting a POST body is missing. I can log the data before urql and codegen hooks are called and I can see the data in the variables on the error, but chrome keeps imploding with a "POST body missing" error and the server's resolver is never hit.
I am using Urql with Codegen on a React Client and using Apollo-server on an Express API.
Here is my code:
Mutation Definition (for codegen)
mutation UserLogin($email: String!, $password: String!) {
login(email: $email, password: $password) {
errors {
email {
error
isValid
}
password {
error
isValid
}
}
token
}
}
codegen outputs:
export const UserLoginDocument = gql`
mutation UserLogin($email: String!, $password: String!) {
login(email: $email, password: $password) {
errors {
email {
isValid
error
}
password {
isValid
error
}
}
token
}
}
`;
export function useUserLoginMutation() {
return Urql.useMutation<UserLoginMutation, UserLoginMutationVariables>(UserLoginDocument);
};
Using this hook fails, so I tried using urql directly:
useMutation Hook (same error)
const [, executeMutation] = useMutation(`
mutation($email: String!, $password: String!) {
login(email: $email, password: $password) {
errors {
email {
error
isValid
}
password {
error
isValid
}
}
token
}
}
`);
I have confirmed that I can execute the query with a raw fetch:
async function fetchGraphQL(text: any, variables: any, token: string = '') {
const response = await fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
Authorization: `bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: text,
variables,
}),
});
return await response.json();
}
However, attempts to generate a hook via codegen or just using urql's useMutation hook are resulting in:
{
...
"source": {
"body": "mutation ($email: String! $password: String!) { login(email: $email password: $password) { errors { email { error isValid } password { error isValid } } token } }",
...
},
"variables": {
"email": "...",
"password": "..."
},
"kind": "mutation",
"context": {
"url": "http://localhost:4000/graphql",
"preferGetMethod": false,
"suspense": false,
"requestPolicy": "cache-first",
"meta": {
"cacheOutcome": "miss"
}
}
},
"error": {
"name": "CombinedError",
"message": "[Network] Bad Request",
"graphQLErrors": [],
"networkError": {},
"response": {}
}
}
I have another, very simple mutation that works fine:
mutation SetActiveChild($childId: String!) {
setActiveChild(childId: $childId)
}
Here are my typeDefs and resolvers:
typeDefs
export const typeDefs = gql`
...
type Mutation {
"user login"
login(email: String!, password: String!): LoginResponse
}
...
`
resolvers
export const resolvers = {
Query: {
...
},
Mutation: {
login: ({}, user: UserRegister) => {
return AuthGraph.login(user);
},
...
},
};
I'm pretty new to GraphQL, many thanks in advance to help me understand what I've done wrong.
Add following thing to apolloServer configuration
// This middleware should be added before calling `applyMiddleware`.
app.use(graphqlUploadExpress());
Reference: https://www.apollographql.com/docs/apollo-server/data/file-uploads/
I ran into this issue and turned out that in my request Header my Content-Type was being set as text/html; charset=utf-8 Once I changed that to application/json The problem got solved

Are fields with list types forbidden in GraphQL schema stitching selection sets?

I have an array of entities that look like this:
const aEntities = [
{
id: 1,
name: 'Test',
oneToManyRelation: [
{
id: 2
},
{
id: 3
}
],
oneToOneRelation: {
id: 1
}
}
];
The entities are represented by the type AType. I want to make an extension of this type in a separate subschema and prove that it is possible to add fields that derive their values from the contents of oneToOneRelation and oneToManyRelation respectively.
The following schema, implementing a derived field based on oneToOneRelation, works fine:
const aSchema = makeExecutableSchema({
resolvers: {
Query: {
aEntities: () => aEntities
}
},
schemaTransforms: [stitchingDirectivesValidator],
typeDefs: gql`
${allStitchingDirectivesTypeDefs}
type AType {
id: ID!
name: String!
oneToOneRelation: AEmbeddedType!
}
type AEmbeddedType {
id: ID!
}
type Query {
aEntities: [AType!]!
}
`
});
const bSchema = makeExecutableSchema({
resolvers: {
AType: {
oneToOneId: ({ oneToOneRelation }) => oneToOneRelation.id
},
Query: {
aEntities_fromBSchema: (_, { keys }) => keys,
}
},
schemaTransforms: [stitchingDirectivesValidator],
typeDefs: gql`
${allStitchingDirectivesTypeDefs}
type AType #key(selectionSet: "{ oneToOneRelation { id } }") {
oneToOneId: String!
}
scalar Key
type Query {
aEntities_fromBSchema(keys: [Key!]!): [AType!]! #merge
}
`
})
const schema = stitchSchemas({
subschemaConfigTransforms: [stitchingDirectivesTransformer],
subschemas: [
{
schema: aSchema
},
{
schema: bSchema,
}
]
})
But once I add oneToManyRelation { id } to the selectionSet i run into problems:
const aSchema = makeExecutableSchema({
resolvers: {
Query: {
aEntities: () => aEntities
}
},
schemaTransforms: [stitchingDirectivesValidator],
typeDefs: gql`
${allStitchingDirectivesTypeDefs}
type AType {
id: ID!
name: String!
oneToManyRelation: [AEmbeddedType!]!
oneToOneRelation: AEmbeddedType!
}
type AEmbeddedType {
id: ID!
}
type Query {
aEntities: [AType!]!
}
`
});
const bSchema = makeExecutableSchema({
resolvers: {
AType: {
oneToManyIds: ({ oneToManyRelation }) => oneToManyRelation.map(({ id }) => id),
oneToOneId: ({ oneToOneRelation }) => oneToOneRelation.id
},
Query: {
aEntities_fromBSchema: (_, { keys }) => keys,
}
},
schemaTransforms: [stitchingDirectivesValidator],
typeDefs: gql`
${allStitchingDirectivesTypeDefs}
type AType #key(selectionSet: "{ oneToOneRelation { id }, oneToManyRelation { id } }") {
oneToOneId: String!
oneToManyIds: [String!]!
}
scalar Key
type Query {
aEntities_fromBSchema(keys: [Key!]!): [AType!]! #merge
}
`
})
I get the following error:
oneToManyRelation.map is not a function
And when I log the keys parameter in the aEntities_fromBSchema resolver it seems that oneToManyRelation haven't been resolved to be an array at all, but rather an (empty) object:
[
{
oneToOneRelation: [Object: null prototype] { id: '1' },
oneToManyRelation: [Object: null prototype] { id: undefined },
__typename: 'AType'
}
]
Is referencing list types in key selection sets simply forbidden as of graphql-tools v 7.0.2? It looks like I actually can circumvent the issue by using a subschema merge config defined outside of the SDL (without batching, instead using the args and selectionSet config parameters), but for validation/gateway reasons I'd prefer to have all my subschemas contain all of their type merging instructions as SDL directives.
Nb. This is a simplified representation of a real world problem.
Nb2. In the real world application one of my subschemas is a remote GraphQL application that I don't control, hence the need for some advanced tailoring in the stitching layer.
Edit: Simply adding the following to the merge options on the subschema config seems to solve the problem. Someone know of a good reason why this doesn't seem to be reproducible with SDL directives? (Or a good way to do so?)
// AType
{
argsFromKeys: (keys) => ({ keys }),
fieldName: 'aEntities_fromBSchema',
key: ({ oneToOneRelation, oneToManyRelation }) => ({ oneToManyRelation, oneToOneRelation }),
selectionSet: '{ oneToOneRelation { id }, oneToManyRelation { id } }'
}
You have likely found a bug! Please open an issue on the GitHub repo so we can track it. :)

GraphQL Stitching - Why would fields from subschemas return null?

I'm attempting to stitch together two GraphQL schemas, one from contentful and one from neo4j.
Each subschema appears to be interrogated during queries across the combined schema but "foreign" fields always come back as null.
I just can't figure this one out.
Sample Query:
query {
//Request data exclusively from the neo4j schema
Product(id:"475e006f-b9cf-4f40-8712-271ceb46d14b"){
id,
name,
weight
},
//This is a contentful schema query which should return weight from neo4j
product(id:"[contentful-native-id]"){
id,
weight,
}
}
Result:
"data": {
"Product": [
{
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"name": "Test product name",
"weight": 14.9
}
],
"product": {
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b",
"weight": null //This shouldn't be null! :(
}
}
Logging:
//First query being executed against neo4j database
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .id , .name , .weight } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
//Triggered by the second query correctly trying to resolve weight from neo4j
neo4j-graphql-js MATCH (`product`:`Product` {id:$id}) RETURN `product` { .weight , .id } AS `product`
neo4j-graphql-js {
"offset": 0,
"first": -1,
"id": "475e006f-b9cf-4f40-8712-271ceb46d14b"
}
This seems to suggest something is working, but the result of weight never makes it to the final output.
ApolloServer doesn't report any errors via didEncounterErrors()
Stitching:
const gatewaySchema = stitchSchemas({
subschemas: [{
schema: neoSchema,
merge: {
Product: {
selectionSet: '{id}',
fieldName: 'Product',
args: ({
id
}) => ({
id
}),
}
}
},
{
schema: contentfulSchema,
merge: {
}
}
],
})
Schemas:
const executor = async ({
document,
variables,
context
}) => {
const query = print(document);
//console.log(query);
const fetchResult = await fetch('https://graphql.contentful.com/content/v1/spaces/[SPACE]', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer [AUTHTOKEN]`,
},
body: JSON.stringify({
query,
variables
})
});
return fetchResult.json();
};
const contentfulSchema = wrapSchema({
schema: await introspectSchema(executor),
executor: executor
});
const driver = neo4j.driver(
process.env.NEO4J_URI || 'bolt://localhost:7687',
neo4j.auth.basic(
process.env.NEO4J_USER,
process.env.NEO4J_PASS
), {
encrypted: process.env.NEO4J_ENCRYPTED ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF',
}
)
const neoSchema = makeAugmentedSchema({
typeDefs: typeDefs,
});
Server:
const server = new ApolloServer({
schema: gatewaySchema,
context: ({ req }) => {
return {
driver,
req
};
},
plugins:[
myPlugin
]
});
Any insight or ideas much appreciated!
This appears to be down to the fact that stitchSchemas is NOT supported in ApolloServer...
Does Apollo Server work with GraphQL Tools stitchSchemas?

GraphQL client not reading certain query variables [duplicate]

I have a component that passes a string (userToFetch) it as a variable parameter in a parameterized query. The component looks like this:
// pages/index.jsx
import React from 'react';
import { useQuery } from '#apollo/react-hooks';
import gql from 'graphql-tag';
const GET_USERS = gql`
query users ($limit: Int!, $username: String!) {
users (limit: $limit, where: { username: $username }) {
username
firstName
}
}
`;
const Home = () => {
const userToFetch = 'jonsnow';
const {
loading,
error,
data,
} = useQuery(
GET_USERS,
{
variables: { limit: 2, username: userToFetch },
notifyOnNetworkStatusChange: true,
},
);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {JSON.stringify(error)}</p>;
}
return (
<div>
<ul>
{data.users.map(user => {
return <li>{user.username} {user.firstName}</li>;
})}
</ul>
</div>
);
};
export default Home;
And this is how I have configured my Apollo client:
// /apollo-client.js
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import withApollo from 'next-with-apollo';
import { createHttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
const GRAPHQL_URL = 'https://dev.schandillia.com/graphql';
const link = createHttpLink({
fetch, // Switches between unfetch & node-fetch for client & server.
uri: GRAPHQL_URL
});
// Export a HOC from next-with-apollo
// Docs: https://www.npmjs.com/package/next-with-apollo
export default withApollo(
// You can get headers and ctx (context) from the callback params
// e.g. ({ headers, ctx, initialState })
({ initialState, ctx }) => {
console.log('initialState', initialState);
console.log('ctx', ctx);
return new ApolloClient({
link: link,
cache: new InMemoryCache()
// rehydrate the cache using the initial data passed from the server:
.restore(initialState || {})
})
}
);
The database is a collection of following users:
"users": [
{
"username": "negger",
"firstName": "Arnold",
"lastName": "Schwarzenegger"
},
{
"username": "jonsnow",
"firstName": "Jon",
"lastName": "Snow"
},
{
"username": "tonystark",
"firstName": "Tony",
"lastName": "Stark"
}
]
}
Now, although this should work (it does when I run the query in my graphql playground at https://dev.schandillia.com/graphql), the code runs as if the where clause didn't exist! It just returns all results as if the query being run were:
users {
_id
username
firstName
}
In order to reproduce the issue, visit https://www.schandillia.com. The page ought to display a list with only one element consisting of a matching username-firstName value: jonsnow Jon but it returns two entries, negger Arnold and jonsnow Jon (respecing limit but completely ignoring where). Now, run the same query with jonsnow as a where parameter in https://dev.schandillia.com/graphql:
{
users(where: { username: "jonsnow" }) {
_id
username
firstName
}
}
And the results would be exactly as expected:
{
"data": {
"users": [
{
"_id": "5d9f261678a32159e61018fc",
"username": "jonsnow",
"firstName": "Jon",
}
]
}
}
What am I overlooking?
P.S.: The repo is up for reference at https://github.com/amitschandillia/proost/tree/master/apollo-nextjs.
UPDATE: In order to track down the root cause, I tried logging some values in apollo-client.js:
console.log('initialState', initialState);
Strangely, the output shows the right query, along with the variables being passed, but wrong results:
...
ROOT_QUERY.users({"limit":2,"where":{"username":"jonsnow"}}).0:
firstName: "Arnold"
username: "negger"
__typename: "UsersPermissionsUser"
...
UPDATE: Here's a screenshot of results in my Apollo Client Developer Tools:
The schema generated by Strapi gives the where attribute a Type JSON and hence you have to pass the entire where part in the query variable as JSON since the variables are not getting injected.
# Write your query or mutation here
query users($where: JSON) {
users(where: $where) {
username
firstName
}
}
And the variables would look like:
{"where": {"username": "jonsnow"}}

GraphQL how to mutate data

I have a basic schema for mutating some data which looks like
const schema = new graphql.GraphQLSchema({
mutation: new graphql.GraphQLObjectType({
name: 'Remove',
fields: {
removeUser: {
type: userType,
args: {
id: { type: graphql.GraphQLString }
},
resolve(_, args) {
const removedData = data[args.id];
delete data[args.id];
return removedData;
},
},
},
})
});
Looking around google I cant find a clear example of the example query which needs to be sent to mutate.
I have tried
POST -
localhost:3000/graphql?query={removeUser(id:"1"){id, name}}
This fails with error:
{
"errors": [
{
"message": "Cannot query field \"removeUser\" on type \"Query\".",
"locations": [
{
"line": 1,
"column": 2
}
]
}
]
}
In order to post requests from the front-end application it is recommended to use apollo-client package. Say i wanted to validate a user login information:
import gql from 'graphql-tag';
import ApolloClient, {createNetworkInterface} from 'apollo-client';
client = new ApolloClient({
networkInterface: createNetworkInterface('http://localhost:3000/graphql')
});
remove(){
client.mutate({
mutation: gql`
mutation remove(
$id: String!
) {
removeUser(
id: $id
){
id,
name
}
}
`,
variables: {
id: "1"
}
}).then((graphQLResult)=> {
const { errors, data } = graphQLResult;
if(!errors && data){
console.log('removed successfully ' + data.id + ' ' + data.name);
}else{
console.log('failed to remove');
}
})
}
More information about apollo-client can be found here
Have you tried using graphiql to query and mutate your schema?
If you'd like to create a POST request manually you might wanna try to struct it in the right form:
?query=mutation{removeUser(id:"1"){id, name}}
(Haven't tried POSTing myself, let me know if you succeeded, i structured this out of the url when using graphiql)
You have to explicitly label your mutation as such, i.e.
mutation {
removeUser(id: "1"){
id,
name
}
}
In GraphQL, if you leave out the mutation keyword, it's just a shorthand for sending a query, i.e. the execution engine will interpret it as
query {
removeUser(id: "1"){
id,
name
}
}
cf. Section 2.3 of the GraphQL Specification
const client = require("../common/gqlClient")();
const {
createContestParticipants,
} = require("../common/queriesAndMutations");
const gql = require("graphql-tag");
const createPartpantGql = async (predictObj) => {
try {
let resp = await client.mutate({
mutation: gql(createContestParticipants),
variables: {
input: {
...predictObj,
},
},
});
let contestParticipantResp = resp.data.createContestParticipants;
return {
success: true,
data: contestParticipantResp,
};
} catch (err) {
console.log(err.message)
console.error(`Error creating the contest`);
return {
success: false,
message: JSON.stringify(err.message),
};
}
};

Resources