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

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.

Related

Cast to ObjectId failed for value \"{ proposser: '618e49a68e18cd48286de4b5' }\" (type Object) at path \"_id\" for model \"Proposal\"

I am getting this error in graphql playground (image below ) .
I have checked for the validity of objectId in the resolver as well.
// model
const ProposalSchema = new Schema({
cover
Letter: {
type: String,
},
budget: {
type: String,
},
proposals: {
type: mongoose.Schema.Types.ObjectId,
},
_id: {
type: mongoose.Schema.Types.ObjectId,
},
});
//resolver
also checked If the argument is valid using mongoose.isValidObjectId(proposser) it returns true
Query: {
proposals(_, args) {
const { proposser } = args;
return Proposal.findById({
proposser,
});
},
},
// schema
const typeDefs = gql`
type Proposal {
_id: ID!
coverLetter: String
budget: String
proposser: ID!
}
`;
const Proposal = mongoose.model("Proposal", ProposalSchema);
I was using wrong method in resolvers .
findById was being used for field non Id field.
async proposals(_, args) {
const { proposser } = args;
const userProposals = await Proposal.find({
proposser,
});
try {
const result = userProposals;
return result ? result : [];
} catch (err) {
console.log(err);
}
},

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

GraphQL error: Variable $customerAccessToken of type String! was provided invalid value

I'm attempting to recover a customer using the customerAccessToken that is given when the user logs in to Shopify.
Using Apollo, here's my code:
this.apollo.mutate({
mutation: getCustomerFromToken,
variables: {
input: {
customerAccessToken: '217b9a6952c28eb4db376487a6301294' // Also tried btoa('217b9a6952c28eb4db376487a6301294')
}
},
})
Here's my GraphQL query:
query getCustomerFromToken($customerAccessToken: String!) {
customer(customerAccessToken: $customerAccessToken) {
id
addresses(first: 5) {
edges {
node {
address1
address2
company
city
province
zip
country
phone
}
}
}
orders(first: 200) {
edges {
cursor
node {
id
totalPriceV2 {
amount
currencyCode
}
processedAt
orderNumber
}
}
}
}
}
Here's the login GraphQL code I'm using to retrieve the accessToken from Shopify:
mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
customerAccessTokenCreate(input: $input) {
customerAccessToken {
accessToken
expiresAt
}
customerUserErrors {
code
field
message
}
}
}
My problem was two fold.
I was using a mutation on a query end point
Queries don't use input in the payload
const payload = {
customerAccessToken: "..."
}
// NOT
const payload = {
input: {
customerAccessToken: "..."
}
}

throw a descriptive error with graphql and apollo

Consider the following class:
// entity/Account.ts
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, Index, CreateDateColumn, UpdateDateColumn } from 'typeorm'
import { Field, Int, ObjectType } from 'type-graphql'
#ObjectType()
#Entity()
export class Account extends BaseEntity {
#Field(() => Int)
#PrimaryGeneratedColumn()
id: number
#Field()
#Column({ length: 50, unique: true })
#Index({ unique: true })
accountIdentifier: string
#Field({ nullable: true })
#Column({ length: 100 })
name?: string
}
With it's corresponding resolver:
// AccountResolver.ts
#Resolver()
export class AccountResolver {
#Mutation(() => Account)
async addAccount(#Arg('options', () => AccountInput) options: AccountInput) {
try {
// if (!options.accountIdentifier) {
// throw new Error(`Failed adding account: the accountIdentifier is missing`)
// }
return await Account.create(options).save()
} catch (error) {
if (error.message.includes('Cannot insert duplicate key')) {
throw new Error(
`Failed adding account: the account already exists. ${error}`
)
} else {
throw new Error(`Failed adding account: ${error}`)
}
}
}
}
Jest test file
// AccountResolver.test.ts
describe('the addAccount Mutation', () => {
it('should throw an error when the accountIdentifier is missing', async () => {
await expect(
client.mutate({
mutation: gql`
mutation {
addAccount(
options: {
name: "James Bond"
userName: "James.Bond#contoso.com"
}
) {
accountIdentifier
}
}
`,
})
).rejects.toThrowError('the accountIdentifier is missing')
})
The field accountIdentifier is mandatory and should throw a descriptive error message when it's missing in the request. However, the error thrown is:
"Network error: Response not successful: Received status code 400"
What is the correct way to modify the error message? I looked at type-graphql with the class-validators and made sure that validate: true is set but it doesn't give a descriptive error.
EDIT
After checking the graphql playground, it does show the correct error message by default. The only question remaining is how write the jest test so it can read this message:
{
"error": {
"errors": [
{
"message": "Field AccountInput.accountIdentifier of required type String! was not provided.",
Thank you for any help you could give me.
The ApolloError returned by your client wraps both the errors returned in the response and any network errors encountered while executing the request. The former is accessible under the graphQLErrors property, the latter under the networkError property. Instea dof using toThrowError, you should use toMatchObject instead:
const expectedError = {
graphQLErrors: [{ message: 'the accountIdentifier is missing' }]
}
await expect(client.mutate(...)).rejects.toMatchObject(expectedError)
However, I would suggest avoiding using Apollo Client for testing. Instead, you can execute operations directly against your schema.
import { buildSchema } from 'type-graphql'
import { graphql } from 'graphql'
const schema = await buildSchema({
resolvers: [...],
})
const query = '{ someField }'
const context = {}
const variables = {}
const { data, errors } = await graphql(schema, query, {}, context, variables)

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