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

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

Related

How do I see the name of a logged in user?

I'm trying to access the current logged in user's name, and get it working in the Playground right now for apollo graphql. I'm using Prisma and I'm a beginner.
This is my schema.graphql:
type Query {
info: String!
userlist: [User!]!
me: User
}
type Mutation {
signup(email: String!, password: String!, name: String!): AuthPayload
login(email: String!, password: String!): AuthPayload
}
type AuthPayload {
token: String
user: User
}
type User {
id: ID!
name: String!
email: String!
}
Here is my src/resolvers/Query.js:
const { getUserId } = require("../utils");
function userlist(parent, args, context) {
return context.prisma.user.findMany();
}
function me(parent, args, context) {
const id = getUserId(context);
return context.prisma.user({ id });
}
module.exports = {
userlist,
me,
};
How do I test this in the playground? If I log in a user with this query:
mutation {
login(email: "alice#prisma.io", password: "graphql") {
token
user {
email
}
}
}
Then when I try to display "me" I get this:
query {
me {
id
name
}
}
Result:
{
"errors": [
{
"message": "Cannot read properties of undefined (reading 'authorization')",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"me"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read properties of undefined (reading 'authorization')",
" at getUserId (/Users/username/example-3/server/src/utils.js:10:36)",
" at me (/Users/username/example-3/server/src/resolvers/Query.js:8:14)",
// i deleted a bunch of stuff here, let me know if you would like to see it and I'll add it back
]
}
}
}
],
"data": {
"me": null
}
}
This is the utils.js:
const jwt = require("jsonwebtoken");
const APP_SECRET = "GraphQL-is-aw3some";
function getTokenPayload(token) {
return jwt.verify(token, APP_SECRET);
}
function getUserId(req, authToken) {
if (req) {
const authHeader = req.headers.authorization;
if (authHeader) {
const token = authHeader.replace("Bearer ", "");
if (!token) {
throw new Error("No token found");
}
const { userId } = getTokenPayload(token);
return userId;
}
} else if (authToken) {
const { userId } = getTokenPayload(authToken);
return userId;
}
throw new Error("Not authenticated");
}
module.exports = {
APP_SECRET,
getUserId,
};
Do I need to pass in the auth token somehow? Any idea why getUserId isn't working? I think I have something wrong, because the code I was using from an example has getUserId(req), but another solution I saw has getUserId(context), so is that what is wrong here? How do I change my Query to use req instead of context? I'm not understanding them enough to make this change.
You answered your own question:
Do I need to pass in the auth token somehow?
Yes. You're getting an error because you're not passing any headers from the playground so req.headers is undefined.
At the bottom left of the playground there's an area where you can define query variables and headers. Define one called "authorization" and put a valid token in the contents.
You should have a login method somewhere that creates a valid token, use that.

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

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"
}

GraphQL always return null

I created a resolver and graphql file about user creation.
The data type seems to fit, and I have asynchronous processing, but the result always return null. I don't know why.
Resolver (createAccount.js)
import { prisma, generateToken, changePhoneNumber } from "../../../utils";
import bcrypt from "bcryptjs";
export default {
Mutation: {
createAccount: async (_, args) => {
const {
username,
password,
password2,
email,
phoneNum,
bio,
avatarUrl,
} = args;
if (password !== password2) {
throw new Error("two password aren't same each other");
}
const encryptPw = await bcrypt.hash(password, 10);
const newPhoneNumber = await changePhoneNumber(phoneNum, "+82");
const user = await prisma.user.create({
data: {
username,
email,
password: encryptPw,
phoneNum: newPhoneNumber,
bio,
avatarUrl,
},
});
const token = generateToken(user.id);
return { token, user };
},
},
};
GraphQL file (createAccount.graphql)
type Mutation {
createAccount(
username: String!
email: String!
password: String!
password2: String!
phoneNum: String!
bio: String
avatarUrl: String
): AuthPayload!
}
type AuthPayload {
token: String
user: User
}
utils.js
import { PrismaClient } from "#prisma/client";
import jwt from "jsonwebtoken";
export const prisma = new PrismaClient();
export const changePhoneNumber = (phoneNum, locationNum) => {
var leftStr = locationNum;
var rightStr = phoneNum.slice(1, phoneNum.length);
var newStr = leftStr + rightStr;
return newStr;
};
export const generateToken = (id) => jwt.sign({ id }, process.env.JWT_SECRET);
models.graphql
type User {
id: ID!
avatarUrl: String
username: String!
email: String!
password: String!
phoneNum: String!
emailSecret: String
phoneSecret: String
bio: String
rooms: [Room]
createdAt: String
messages: [Message]
sender: [User]
receiver: [User]
}
I read the answers to other similar questions, but most said that I should fit the data type or asynchronous processing.
(Why does a GraphQL query return null?) But my code used code for asynchronous processing, and I guess I matched the data type. Why does this code always return null?
Also, in addition to this Mutation, all other Query, Mutation and Subscriptions return nulls
Based on the error, it looks like GraphQL doesn't think you've provided a resolver for the createAccount field at all. The issue is with how you're merging your resolvers. This is your code:
const allTypes = fileLoader(path.join(__dirname, "api/**/*.graphql"));
const allResolvers = fileLoader(path.join(__dirname, "api/**/*.js"));
const schema = makeExecutableSchema({
typeDefs: mergeTypes(allTypes),
resolvers: mergeResolvers(allResolvers),
});
This is the resulting value of allResolvers:
[ { Query: { TestQL: [Function: TestQL] } },
{ Mutation: { addCategory: [Function: addCategory] } },
{ Mutation: { deleteCategory: [Function: deleteCategory] } },
{ Mutation: { editCategory: [Function: editCategory] } },
{ Mutation: { newMessage: [Function: newMessage] } },
{ Subscription: { subMessage: [Object] } },
[Function: _default],
{ Mutation: { createRoom: [Function: createRoom] } },
[Function: _default],
{ Query: { getRooms: [Function: getRooms] } },
{ Mutation: { createAccount: [Function: createAccount] } },
{ Mutation: { deleteUser: [Function: deleteUser] } },
{ Mutation: { editProfile: [Function: editProfile] } },
{ Query: { findEmail: [Function: findEmail] } },
{ Mutation: { login: [Function: login] } },
{ Mutation: { requestEmailSecret: [Function: requestEmailSecret] } },
{ Mutation: { resetPassword: [Function: resetPassword] } },
{ Query: { searchUser: [Function: searchUser] } },
{ Query: { seeProfile: [Function: seeProfile] } } ]
Two of your modules export a function instead of an object:
export default () => {...}
As a result, what's returned by mergeResolvers ends up being a function, not an object. So you're not providing a resolver map at all to makeExecutableSchema. You need to fix the default exports for those two modules for the resolvers to be merged correctly.

Apollo Client Mutation get an error 400 with React Component

Mutation gql tag:
// AppAdminLoinInput is wrong spell don't focus it.
const LOGIN_MUTATION = gql`
mutation AppAdminLoinInput($email: String!, $password: String!) {
appAdminLogin(email: $email, password: $password) {
name
email
token {
accessToken
refreshToken
}
}
}
`;
Mutation Function onSubmit
onSubmitLogin = async (e, appAdminLogin) => {
const { email, password } = this.state;
e.preventDefault();
if (!validator.isEmail(email)) {
console.log("Not email format");
return;
}
if (password.length < 8) {
return;
}
appAdminLogin({
variables: {
email,
password
}
});
};
When onSubmitLogin do i get an error 400 (Bad request)
What am i do wrong?
sorry about my english skill xD

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