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

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!

Related

Can't get my reactive variable from graphql local query

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

Next-Auth with firebase Authentication

just wanna have my custom credential provider which authenticate the entered username and password with Firebase Authentication on sign in page
pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import { getDatabase } from "firebase/database"
import { DB } from "../../../constants/firebase"
import { FirebaseAdapter } from "#next-auth/firebase-adapter"
import * as firestoreFunctions from "firebase/firestore"
import CredentialsProvider from "next-auth/providers/credentials"
export default NextAuth({
session: {
strategy: "database",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {
label: "Username",
type: "text",
placeholder: "somebody#gmail.com",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const database = getDatabase()
console.log(database)
const user = {
id: 1,
usename: "j",
password: "123456789",
}
if (
credentials?.username === user.usename &&
credentials.password === "123456789"
) {
return user
}
return null
},
}),
],
adapter: FirebaseAdapter({
db: DB,
...firestoreFunctions,
}),
// pages: {
// signIn: "/auth/signin",
// signOut: "/auth/signout",
// error: "/auth/error", // Error code passed in query string as ?error=
// verifyRequest: "/auth/verify-request", // (used for check email message)
// newUser: "/auth/new-user", // New users will be directed here on first sign in (leave the property out if not of interest)
// },
callbacks: {
async jwt({ token, user }) {
if (user) {
token.email = user.email
}
return token
},
async session({ session, token, user }) {
if (token) {
session.user!.email = token.email
}
return session
},
redirect({ url, baseUrl }) {
if (url.startsWith(baseUrl)) return url
else if (url.startsWith("/"))
return new URL(url, baseUrl).toString()
return baseUrl
},
},
})
firebase.ts
import { initializeApp, getApp, getApps } from "firebase/app"
import { getAnalytics } from "firebase/analytics"
import { getFirestore } from "#firebase/firestore"
import { getStorage } from "#firebase/storage"
import getFirebaseObject from "./firebaseConfig"
const app = !getApps.length ? initializeApp(getFirebaseObject()) : getApp()
const DB = getFirestore(app)
const storages = getStorage()
const analytics = getAnalytics(app)
export { app, DB, analytics, storages }
as you see
const user = {
id: 1,
usename: "j",
password: "123456789",
}
in fact except of these static data wanna search and get right user info from the Firebase
I know there are a some other way of doing this but I like working with next-auth for last change wanna make sure there's a spot of light in this was ;)
i found this public repository where the author does something similar to what you want to achieve, which is create a custom token with your database credentials.
May be this repository can help you. It has a few errors, but it gave me a general idea about what to do, as I had a similar case.
try {
if (user !== null) {
await customTokenSignIn(user.id, user.email);
(await getUser(user.id)) ??
(await createUser(toReqUser(user, account)));
const data = await getUser(user.id);
setResUser(user, data as ResUser);
return true;
}
return false;
} catch (e) {
console.error(e);
return false;
}
const customTokenSignIn = async (id: string, email: string) => {
const hash = toHash(id);
const customToken = await adminAuth.createCustomToken(hash);
await auth.signInWithCustomToken(customToken).then((res) => {
res.user?.updateEmail(email);
});
await adminAuth.setCustomUserClaims(hash, { sid: id });
await createUserToken({ id: id, firebaseUid: hash });
};

GraphQL / Relay - optimisticResponse isn't propagating as I had hoped

In the mutation below I'm updating the avatarId for a person and I would like the new imageUrl to propagate everywhere it's needed, namely the <UserAvatar /> component which can be in a lot of components on the same page.
I know this new URL before ever hitting the UpdatePersonMutation so I'm not sure what I'm doing wrong.
Do I need to involve the store and / or use optimisticUpdater, and / or use subscriptions to get a new avatar image to show up everywhere instantly?
import { commitMutation } from 'react-relay';
import graphql from 'babel-plugin-relay/macro';
import modernEnvironment from '../Environment';
const mutation = graphql`
mutation UpdatePersonMutation($input: UpdatePersonInput!) {
updatePerson(input: $input) {
imageByAvatarId {
id
imageUrl // I know this before I ever reach this mutation
}
person {
id
avatarId
}
}
}
`;
const commit = (payload, callback) => {
const input = {
id: payload.id,
personPatch: {
avatarId: payload.avatarId,
},
};
return commitMutation(modernEnvironment, {
mutation,
variables: {
input,
},
optimisticResponse: {
updatePerson: {
imageByAvatarId: {
id: payload.imageId,
imageUrl: payload.imageUrl,
},
person: {
id: payload.id,
avatarId: payload.avatarId,
},
},
},
onCompleted: response => {
callback(response);
},
onError: error => {
console.log(error);
},
});
};
export default commit;

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;
},
});

Resources