Can't get my reactive variable from graphql local query - reactjs

I'm trying to setup a Apollo 3 local state with react. For an unknown reason my query does not work when I try to read a custom type (isAuthenticated query works well). Can you please help me to understand I literaly explored all the documentation and the link on internet about local state management.
Thank you
cache.js
import { InMemoryCache, makeVar, gql } from '#apollo/client'
export const isAuthenticatedVar = makeVar(!!localStorage.getItem('token'))
export const userVar = makeVar("")
export const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
isAuthenticated: {
read() {
return isAuthenticatedVar()
}
},
user: {
read() {
return userVar()
},
__typename: 'User',
}
}
}
}
})
export const IS_AUTHENTICATED = gql`
query IS_AUTHENTICATED {
isAuthenticated #client
}
`
export const GET_LOCAL_USER = gql`
query GET_LOCAL_USER {
user #client
}
`
typedej.js
import { gql } from '#apollo/client'
export const typeDef = gql`
extend type User {
id: ID!
name: String!
surname: String!
email: String!
password: String!
phone: String!
adress: Adress!
isAdmin: Boolean!
isCoach: Boolean!
isManager: Boolean!
}
extend type Query {
isAuthenticated: Boolean!
user: User!
}
`
export default typeDef
test.js
const login = (email, password) => {
const emailLower = email.toLowerCase()
client.query({
query: LOGIN,
variables: { email: emailLower, password: password }
})
.then(result => {
console.log(result)
localStorage.setItem('token', result.data.login.token)
localStorage.setItem('userId', result.data.login.user.id)
isAuthenticatedVar(true)
userVar({ ...result.data.login.user})
console.log('USER VAR SETUP')
///Console.log show that userVar contains an object with all User data
console.log(userVar())
var test
try{
//test return NULL object
test = client.readQuery({ query: GET_LOCAL_USER}).user
}catch(error) {
console.log(error)
}
console.log('FETCH QUERY userVar')
console.log(test)
navigate("/")
})
.catch(error => {
console.log(error)
})
}

Tried several ways but can't find how to resolve this. I don't understand why documentation don't explain how to use with Object Type

Related

Can't send data to graphQL database from next.js app

I am making a Reddit Clone. I have gotten to the point of implementation of SQL, so I am using graphQL and to make this easier Stepzen for endpoints. The following code is my Post Box for the reddit site along with graphQL, Mutations.ts and Queries.ts.
I have added Apollo Client and Toaster as well.
The problem seems to be occurring in my PostBox.tsx, It is coming from within my onSubmit function. As the Try/Catch runs, it triggers the error and catches it.
In the console my formData is a success and the console Log immediately inside the try works, but nothing else.
PostBox.tsx
import { useSession } from 'next-auth/react';
import React, { useState } from 'react';
import Avatar from './Avatar';
import { LinkIcon, PhotographIcon } from '#heroicons/react/outline';
import { useForm } from 'react-hook-form';
import { useMutation } from '#apollo/client';
import { ADD_POST, ADD_SUBREDDIT } from '../graphql/mutations';
import client from '../apollo-client';
import { GET_SUBREDDIT_BY_TOPIC } from '../graphql/queries';
import toast from 'react-hot-toast';
type FormData = {
postTitle: string;
postBody: string;
postImage: string;
subreddit: string;
};
function PostBox() {
const { data: session } = useSession();
// console.log(session);
//
const [addPost] = useMutation(ADD_POST);
const [addSubreddit] = useMutation(ADD_SUBREDDIT);
//
const [imageBoxOpen, setImageBoxOpen] = useState<boolean>(false);
const {
register,
setValue,
handleSubmit,
watch,
formState: { errors },
} = useForm<FormData>();
const onSubmit = handleSubmit(async (formData) => {
console.log(formData);
console.log('form data');
const notification = toast.loading('Creating new post...');
console.log(notification, 'notification');
try {
// query for subreddit topic
console.log('Test 1 Success');
//
// Error below: CL Test in line 42 is a success
const {
data: { getSubredditListByTopic },
} = await client.query({
query: GET_SUBREDDIT_BY_TOPIC,
variables: {
topic: formData.subreddit,
},
});
const subredditExists = getSubredditListByTopic.length > 0;
console.log('Test 2 Failed');
if (!subredditExists) {
// create subreddit
console.log('subreddit is new creating a new subreddit!');
console.log('Test 3 Failed');
const {
data: { insertSubreddit: newSubreddit },
} = await addSubreddit({
variables: {
topic: formData.subreddit,
},
});
console.log('Creating post...', formData);
const image = formData.postImage || '';
const {
data: { insertPost: newPost },
} = await addPost({
variables: {
body: formData.postBody,
image: image,
subreddit_id: newSubreddit.id,
title: formData.postTitle,
username: session?.user?.name,
},
});
console.log('New post added:', newPost);
} else {
// use existing
const image = formData.postImage || '';
const {
data: { insertPost: newPost },
} = await addPost({
variables: {
body: formData.postBody,
image: image,
subreddit_id: getSubredditListByTopic[0].id,
title: formData.postTitle,
username: session?.user?.name,
},
});
console.log('New post added', newPost);
}
setValue('postBody', '');
setValue('postImage', '');
setValue('postTitle', '');
setValue('subreddit', '');
toast.success('New Post Created!', {
id: notification,
});
} catch (error) {
toast.error('Whoops something went wrong!', {
id: notification,
});
}
});
queries.ts
import { gql } from '#apollo/client';
export const GET_SUBREDDIT_BY_TOPIC = gql`
query MyQuery($topic: String!) {
getSubredditListByTopic(topic: $topic) {
id
topic
created_at
}
}
`;
Mutations.ts
import { gql } from '#apollo/client';
export const ADD_POST = gql`
mutation MyMutation(
$body: String!
$image: String!
$subreddit_id: ID!
$title: String!
$username: String!
) {
insertPost(
body: $body
image: $image
subreddit_id: $subreddit_id
title: $title
username: $username
) {
body
created_at
id
image
subreddit_id
title
username
}
}
`;
export const ADD_SUBREDDIT = gql`
mutation MyMutation($topic: String!) {
insertSubreddit(topic: $topic) {
id
topic
created_at
}
}
`;
index.graphql ->There is more to this file but these are the only two I have changed, Supabase automatically built the rest
getSubredditList: [Subreddit]
#dbquery(
type: "postgresql"
schema: "public"
table: "subreddit"
configuration: "postgresql_config"
)
getSubredditListByTopic(topic: String!): [Subreddit]
#dbquery(
type: "postgresql"
schema: "public"
query: """
select * from "subreddit" where "topic" = $1
"""
configuration: "postgresql_config"
)
This is really upsetting me as I am not sure what is wrong. Thank you all for taking a look!

how to pass the variables dynamic in GraphQL?

what changes need to perform the code:
const data = client.mutate({
mutation: (Query)
});
let Query = gql`
mutation{
create(input:{
department:"Tester",
code:"Tester"
}
)
{
code,
details,
description,
}
}`
console.log(data, "data")
how to pass input dynamically? I have my input as:
var department = {
department:"Tester",
code:"Tester"
}
I haven't tested it, but it will work.
in react apollo-client
import { gql, useMutation } from '#apollo/client';
const Query = gql`
mutation Create($department: String!, $code: String!) {
create(department: $department, code: $code) {
code
details
description
}
}
`;
const [create] = useMutation(Query);
create({ variables: { department: department_value, code: code_value } });
other way
const departmentValue = "Tester"
const codeValue = "Tester"
client.mutate({
variables: { department: departmentValue, code:codeValue },
mutation: gql`
mutation Create($department: String!, $code: String!){
create(department: $department, code: $code){
code
details
description
}
}
`,
})
.then(result => { console.log(result) })
.catch(error => { console.log(error) });

PubSub does not work in Client page but works in Graphql playground - TypeError: data.getPosts is not iterable

I am trying to make a Social app using MongoDB, Express, React, Node, Graphql with Apollo , I am following a video from freecodecamp : Link to the video
I am using pubsub to achieve a realtime functionality, like when ever a new post has been added it should show up in the home page.
I might have missed the code of some file that's why
I am dropping a link to my github repo containing the whole project : Link to the github repo
Home.js:
import React, { useContext } from 'react';
import { useQuery } from '#apollo/react-hooks';
import { Grid, Transition } from 'semantic-ui-react';
import { AuthContext } from '../context/auth';
import PostCard from '../components/PostCard';
import PostForm from '../components/PostForm';
import { FETCH_POSTS_QUERY } from '../util/graphql';
function Home() {
const { user } = useContext(AuthContext);
const { data, loading, error } = useQuery(FETCH_POSTS_QUERY);
if(data) {
const { posts } = data || [];
return (
<Grid columns={3}>
<Grid.Row className="page-title">
<h1>Recent Posts</h1>
</Grid.Row>
<Grid.Row>
{user && (
<Grid.Column>
<PostForm />
</Grid.Column>
)}
{loading && <h1>Loading posts..</h1>}
{data && (
<Transition.Group>
{posts &&
posts.map((post) => (
<Grid.Column key={post.id} style={{ marginBottom: 20 }}>
<PostCard post={post} />
</Grid.Column>
))}
</Transition.Group>
)}
</Grid.Row>
</Grid>
);
}
if(error) {
return error.message;
}
}
export default Home;
I render all the posts in Home.js
Resolvers => posts.js :
const { AuthenticationError, UserInputError } = require('apollo-server');
const Post = require('../../models/Post');
const checkAuth = require('../../util/check-auth');
module.exports = {
Query: {
async getPosts() {
try {
const posts = await Post.find().sort({ createdAt: -1 });
return posts;
} catch (err) {
throw new Error(err);
}
},
async getPost(_, { postId }) {
try {
const post = await Post.findById(postId);
if (post) {
return post;
} else {
throw new Error('Post not found');
}
} catch (err) {
throw new Error(err);
}
}
},
Mutation: {
async createPost(_, { body }, context) {
const user = checkAuth(context);
if (body.trim() === '') {
throw new Error('Post body must not be empty');
}
const newPost = new Post({
body,
user: user.id,
username: user.username,
createdAt: new Date().toISOString()
});
const post = await newPost.save();
context.pubsub.publish('NEW_POST', {
newPost: post,
}).then(()=>{
console.log("working")
});
return post;
},
async deletePost(_, { postId }, context) {
const user = checkAuth(context);
try {
const post = await Post.findById(postId);
if (user.username === post.username) {
await post.delete();
return 'Post deleted successfully';
} else {
throw new AuthenticationError('Action not allowed');
}
} catch (err) {
throw new Error(err);
}
},
async likePost(_, { postId }, context) {
const { username } = checkAuth(context);
const post = await Post.findById(postId);
if (post) {
if (post.likes.find((like) => like.username === username)) {
// Post already likes, unlike it
post.likes = post.likes.filter((like) => like.username !== username);
} else {
// Not liked, like post
post.likes.push({
username,
createdAt: new Date().toISOString()
});
}
await post.save();
return post;
} else throw new UserInputError('Post not found');
}
},
Subscription: {
newPost: {
subscribe: (_, __, { pubsub }) => pubsub.asyncIterator('NEW_POST')
}
}
};
Resolvers => index.js :
const postsResolvers = require('./posts');
const usersResolvers = require('./users');
const commentsResolvers = require('./comments');
module.exports = {
Post: {
likeCount: (parent) => parent.likes.length,
commentCount: (parent) => parent.comments.length
},
Query: {
...postsResolvers.Query
},
Mutation: {
...usersResolvers.Mutation,
...postsResolvers.Mutation,
...commentsResolvers.Mutation
},
Subscription: {
...postsResolvers.Subscription
}
};
tyDefs.js :
const { gql } = require('apollo-server');
module.exports = gql`
type Post {
id: ID!
body: String!
createdAt: String!
username: String!
comments: [Comment]!
likes: [Like]!
likeCount: Int!
commentCount: Int!
}
type Comment {
id: ID!
createdAt: String!
username: String!
body: String!
}
type Like {
id: ID!
createdAt: String!
username: String!
}
type User {
id: ID!
email: String!
token: String!
username: String!
createdAt: String!
}
input RegisterInput {
username: String!
password: String!
confirmPassword: String!
email: String!
}
type Query {
getPosts: [Post]
getPost(postId: ID!): Post
}
type Mutation {
register(registerInput: RegisterInput): User!
login(username: String!, password: String!): User!
createPost(body: String!): Post!
deletePost(postId: ID!): String!
createComment(postId: String!, body: String!): Post!
deleteComment(postId: ID!, commentId: ID!): Post!
likePost(postId: ID!): Post!
}
type Subscription {
newPost(
id: ID!,
body: String!,
createdAt: String!,
username: String!,
likeCount: Int!,
commentCount: Int!): Post!
}
`;
This is the index.js file of my server (backend) :
const { ApolloServer, PubSub } = require('apollo-server');
const mongoose = require('mongoose');
const typeDefs = require('./graphql/typeDefs');
const resolvers = require('./graphql/resolvers');
const { MONGODB } = require('./config.js');
const pubsub = new PubSub();
const PORT = process.env.port || 5000;
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req, pubsub })
});
mongoose
.connect(MONGODB, { useUnifiedTopology: true , useNewUrlParser: true })
.then(() => {
console.log('MongoDB Connected');
return server.listen({ port: PORT });
})
.then((res) => {
console.log(`Server running at ${res.url}`);
})
.catch(err => {
console.error(err)
})
I am intialising the pubsub in the index.js file only,
This is the error I am getting :
TypeError: data.getPosts is not iterable 0.chunk.js:124321
Screen shot of the error :
This error is one problem the other is that whenever I click submit the same error occurs, but the post appears when I run the subscription in Graphql Playground not in the client page but when I refresh the page the post appears there.
In case you're using Apollo v3, and you're having some issues with caching while adding posts:
const [createPost, { error }] = useMutation(CREATE_POST_MUTATION, {
variables: values,
update(proxy, result) {
const data = proxy.readQuery({
query: FETCH_POSTS_QUERY,
});
proxy.writeQuery({
query: FETCH_POSTS_QUERY,
data: {
getPosts: [result.data.createPost, ...data.getPosts],
},
});
values.body = "";
},
onError(err) {
<== also add this so the page doesn't break
return err;
},
});

useMutation result returning weird structure/ not returning requested fields

What I am trying to do: using useMutation to submit an array of array to mongoDB.
What happened: it did successfully save in mongoDb and in the correct format BUT the 'result.data' of useMutation is not as expected, it's too weired to be used.
This is my array of array (of course, it started as an empty array and was pushed in as user adds attraction to the days in their itinerary).
let itinerary = [{placeIds: ["ChIJafBhcoVPqEcRb7rHEy3G0L8", "ChIJBRk3gUlOqEcR9oEBV-dqK5M"]},
{placeIds: ["ChIJx8Iw5VFOqEcRXUgfWxkmAaA", "ChIJSdWeck5OqEcReSTr3YfoSuE"]},
{placeIds: ["ChIJ1WCXcFJOqEcRBR_ICa3TemU"]}]
This is the graphql mutation:
const SUBMIT_ITINERARY = gql`
mutation submitItinerary(
$dayPlans: [DayPlanInput]
){
submitItinerary(
dayPlans: $dayPlans
){
id
dayPlans{
placeIds
}
createdAt
}
}
`
This is the apollo react hook useMutation:
const [submitItinerary] = useMutation(SUBMIT_ITINERARY, {
update(result){
console.log(result.data);
},
onError(err){
console.log(err)
},
variables: {
dayPlans: itinerary
}
})
in my graphql playground I got this
So I thought in my frontend I would get something similar but instead I got this:
Why is that the case? I want to get a structure similar to what I got in graphql playground.
More information that might be helpful:
Here is my itinerary schema:
const itinerarySchema = new Schema({
city: String,
username: String,
createdAt: String,
dayPlans: [],
user: {
type: Schema.Types.ObjectId,
ref: 'users',
}
});
my type definitions:
type DayPlan {
placeIds: [String]!
}
type Itinerary {
id: ID!
dayPlans: [DayPlan]!
username: String!
createdAt: String!
}
input DayPlanInput {
placeIds: [String]
}
input RegisterInput {
username: String!
password: String!
confirmPassword: String!
email: String!
}
type Query {
getUsers: [User]
}
type Mutation {
register(registerInput: RegisterInput): User!
login(username: String!, password: String!): User!
submitItinerary(dayPlans: [DayPlanInput] ): Itinerary!
}
and my submitItinerary resolver:
Mutation: {
async submitItinerary(_, {dayPlans}, context) {
const user = checkAuth(context);
//console.log(user);
if (dayPlans.length === 0){
throw new Error('Itinerary should not be empty');
}
const newItinerary = new Itinerary({
dayPlans,
user: user.id,
username: user.username,
createdAt: new Date().toISOString()
})
const submitted = await newItinerary.save()
return submitted;
}
}
The first variable of the update option in useMutation is the Apollo Cache itself not the mutation result. The mutation result can be found in the second parameter. Here you can find the API docs.
This should log the correct data:
const [submitItinerary] = useMutation(SUBMIT_ITINERARY, {
update(cache, result){
console.log(result.data);
},
onError(err){
console.log(err)
},
variables: {
dayPlans: itinerary
}
})
Also another way to access the mutation result is using the second element from the hook return value:
const [submitItinerary, { data, loading, error }] = useMutation(SUBMIT_ITINERARY)

Adding field to Graphcool email-password template mutation

I used the email-password template to setup my graphcool server with user authentication. I want to add a property for “name” when signing up a new user. I added name to the User model and updated the signup.graphql and signup.ts code as shown below.
I get an error when running the createUser mutation which says “name is not defined”. I’m not sure what the problem is. Any help is greatly appreciated!
signup.graphql
extend type Mutation {
signupUser(email: String!, password: String!, name: String): SignupUserPayload
}
signup.ts
interface EventData {
email: string
password: string
name: string
}
const SALT_ROUNDS = 10
export default async (event: FunctionEvent<EventData>) => {
console.log(event)
try {
const graphcool = fromEvent(event)
const api = graphcool.api('simple/v1')
const { email, password } = event.data
if (!validator.isEmail(email)) {
return { error: 'Not a valid email' }
}
// check if user exists already
const userExists: boolean = await getUser(api, email)
.then(r => r.User !== null)
if (userExists) {
return { error: 'Email already in use' }
}
// create password hash
const salt = bcrypt.genSaltSync(SALT_ROUNDS)
const hash = await bcrypt.hash(password, salt)
// create new user
const userId = await createGraphcoolUser(api, email, hash, name)
// generate node token for new User node
const token = await graphcool.generateNodeToken(userId, 'User')
return { data: { id: userId, token } }
} catch (e) {
console.log(e)
return { error: 'An unexpected error occured during signup.' }
}
}
async function getUser(api: GraphQLClient, email: string): Promise<{ User }> {
const query = `
query getUser($email: String!) {
User(email: $email) {
id
}
}
`
const variables = {
email,
}
return api.request<{ User }>(query, variables)
}
async function createGraphcoolUser(api: GraphQLClient, email: string, password: string, name: string): Promise<string> {
const mutation = `
mutation createGraphcoolUser($email: String!, $password: String!, $name: String) {
createUser(
email: $email,
password: $password,
name: $name
) {
id
}
}
`
const variables = {
email,
password: password,
name: name
}
return api.request<{ createUser: User }>(mutation, variables)
.then(r => r.createUser.id)
Found the answer. I was missing name from this line here..
const { email, password } = event.data
Changing to this solved it
const { email, password, name } = event.data

Resources