GraphQL and Apollo - Multiple Mutations - reactjs

I'm working on a React app that is using Auth0, Hasura/PostgreSQL, GraphQL and Apollo and I'm very green so I obviously need some help. Below is what I'm trying to achieve:
A user submits the form to create a new team. The record is added to 'teams' table and now I need to return that Id so I can create a row inside 'teamstaff' table.
Table Structures:
Users
Id
Name
auth0_id
Teams
Id
Name
Created_By
Teamstaff
Id
User_Id
Team_Id
Role_Id
import gql from "graphql-tag";
const insertTeam = gql `
mutation ($name: String!, $gender: String!, $birth_year: Int!, $created_by: String!) {
insert_teams(objects: {name: $name, gender: $gender, birth_year: $birth_year, created_by: $created_by}) {
affected_rows
returning {
id
name
gender
birth_year
created_by
}
}
}
`;
export default insertTeam;
I'm able to add a new team to the DB but I need help on getting the Id from that newly created team so I can create the initial record in the 'teamstaff' table. Also, is there a better way of structuring my tables? Each user can be assigned to multiple teams and different roles for each team.

I'm not sure how you're performing this mutation, but I'll assume that you're using Apollo's Mutation Component. This is an example of how you could get the ID of the recently added record:
import React from "react";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
const INSERT_TEAM = gql `
mutation InsertTeam($name: String!, $gender: String!, $birthYear: Int!, $createdBy: String!) {
insertTeam(objects: {name: $name, gender: $gender, birthYear: $birthYear, createdBy: $createdBy}) {
affectedRows
returning {
id
name
gender
birthYear
createdBy
}
}
}
`;
const onInsertTeamCompleted = ({ insertTeam }) => {
console.log(insertTeam.returning.id); // Here you have your ID
}
const Screen = () => (
<Mutation mutation={INSERT_TEAM} onCompleted={onInsertTeamCompleted}>
{(insertTeam, { data }) => { // Through data, you can also access data.insertTeam results.
const onInserTeam = () => {
insertTeam({ variables: {name: "Mary", gender: "Female", birthYear: 1990} });
};
return (
<button onClick={onInserTeam}>Insert demo team</button>
);
})}
</Mutation>
)
export default Screen;
As you might have noticed, I've updated your query to follow the naming conventions for attributes (yep, using camelCase), but this change has no impact on the final result.
If you've not read it already, the Mutation Component section in Apollo docs talks about a lot of important things like updating the cache and handling errors, so it's a must-read! 😉

I know this question is old, but Hasura will handle this for you (at this point).
You just need to supply one side of the relationship with the User_Id field
https://docs.hasura.io/1.0/graphql/manual/mutations/insert.html#insert-an-object-along-with-its-related-objects-through-relationships
mutation($name: String!, $gender: String!, $birth_year: Int!, $created_by: String, $user_id: String!) {
insert_teams(
objects: {
TeamStaff: {
data: {
User_Id: $user_id
}
},
name: $name,
gender: $gender,
birth_year: $birth_year,
created_by: $created_by }
) {
affected_rows
returning {
id
name
gender
birth_year
created_by
}
}
}

Related

use graphql query and mutation in same file

const QUERIES = gql`
query {
getGrades {
grade_info
id
}
getSubjects {
id
subject_info
}
getSchools {
school_name
id
}
}
`;
const MUTATIONS = gql`
mutation {
createTeacher(
first_name: ${firstName}
last_name: ${lastName}
phone: "${number}
email: ${email}
subjectRef: ["6287323efe0b204eee241cc5"]
gradeRef: ["62872b8b0023e0dcc9c5a703"]
schoolRef: "62ab59edde044d104f10e5a9"
) {
id
first_name
last_name
phone
email
email_verified
approved
number_verified
}
}
`;
const { loading, error, data } = useQuery(QUERIES);
const [mutateFunction, { data, loading, error }] = useMutation(MUTATIONS);
Here is my graphql query using in react .
But my data variable conflicting in query and mutation
How to handle the situation ?
Please take a look .
If am changing data to something else it is not working.
You are using a destructuring assignment in order to extract the fields data, error, loading from the response of useQuery and useMutation.
The destructuring assingment operator allows renaming the variables (please find better variable names than I used as an example ;-) )
Example:
const { loading: loadingQueries, error: errorQueries, data: dataQueries } = useQuery(QUERIES);
//use
console.log(loadingQueries);
The same can be applied for useMutation.

Nested graphql query

I am pretty new with graphql , I don't know how to apply this filter.
following is schema
const GET_TICKETS = gql`
query Ticket($filter: String) {
tickets(filter: $filter) {
id
title
firstname
lastname
gender
contactnumber
email
address
pincode
location
emergency_type
priority
descriptionof_assistance
date_created
status
work_items {
id
status
service
volunteers {
volunteer_id
volunteer_name
status_of_acceptance
}
}
}
}
`;
I want to fetch all tickets where workitems contain given volunteer
fetch tickets where volunteer in workitems.volunteers ....something like this
When you use useQuery There is an optional second parameter or useLazyQuery on the function trigger.
Example from the official documentation.
useQuery
const { loading, error, data } = useQuery(GET_DOG_PHOTO, {
variables: { breed },
});
So on your case it should be
const { loading, error, data } = useQuery(GET_TICKETS, {
variables: { filter: "the filter value" },
});
and on useLazyQuery will look like this:
const [getTickets, { loading, error, data }] = useLazyQuery(GET_TICKETS);
//..... somewhere else
getTickets({variables: {filter: "filter value"}})
reference: https://www.apollographql.com/docs/react/data/queries/

Apollo Graphql query in React with input values

I'm having trouble to make a query in React with Apollo. I know how to do it in the playground and basic queries in React but I can't figure out how to pass dynamically the values for an input inside the query. The query should be like this. This works on playground.
getCityByName(name: "London", country: "GB", config:{units:metric, lang: sp}) {
id
name
weather {
summary {
description
}
temperature {
actual
}
}
}
The units and lang are enum. In React I could only pass the name and country dinamically but not the config options, I only get errors and I've tried so many different syntax.
The only way I can is hardcoding like this:
const GET_DATA = gql`
query getCity($name: String!, $country: String) {
getCityByName(
name: $name
country: $country
config: { units: metric, lang: sp }
) {
id
name
country
weather {
summary {
description
}
temperature {
actual
}
}
}
}
`;
const { loading, data, error } = useQuery(GET_DATA, {
variables: {
name: 'London',
country: 'GB',
},
});
How can I make units and lang dynamic?

getting different data from db than in GraphiQL

I'm new to graphQL and mongoDB and I'm trying to make it work in my project. The problem is with data from query that in GraphiQL is completely different than data from the same query inside my client side. Here's my setup of schema:
const graphql = require('graphql');
const _ = require('lodash');
const Item = require('../models/item');
const {
GraphQLObjectType,
GraphQLString,
GraphQLSchema,
GraphQLID,
GraphQLInt,
GraphQLList
} = graphql;
const ItemType = new GraphQLObjectType({
name: 'Item',
fields: () => ({
name: {
type: GraphQLString
},
id: {
type: GraphQLID
},
description: {
type: GraphQLString
},
price: {
type: GraphQLInt
},
image: {
type: GraphQLString
},
category: {
type: GraphQLString
}
})
});
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
item: {
type: ItemType,
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
// code to get data from db / other source
return Item.findById(args.id);
}
},
items: {
type: new GraphQLList(ItemType),
resolve(parent, args) {
return Item.find({})
}
}
}
});
When im doing a query from graphiQL of all the itemes and data i'm receiving is the "right one". It looks like this:
When i'm doing the same exact query from the front-end like that:
import { gql } from "apollo-boost";
const getItemsQuery = gql`
{
items {
name
id
description
price
image
category
}
}
`;
export { getItemsQuery };
The data looks like this:
It looks like it is repeating first item over and over and i can't see why. DB is also showing right items. My server side code can be found here: https://github.com/KamilStaszewski/shoppy/tree/adding_graphQL/server
From the docs:
The InMemoryCache normalizes your data before saving it to the store by splitting the result into individual objects, creating a unique identifier for each object, and storing those objects in a flattened data structure. By default, InMemoryCache will attempt to use the commonly found primary keys of id and _id for the unique identifier if they exist along with __typename on an object.
In other words, Apollo will use both __typename and id to create a cache key for each Item you fetch. This key is used to fetch the appropriate item from the cache. The problem is that your items are returning null for their id. This results in each item being written with the same key. As a result, when your query result is returned from the cache, it looks up the same key for each item in your items array.
To fix this issue, you need to ensure that your API returns a value for id. I haven't worked with mongoose that much, but I think since mongoose adds an id field for you automatically based on the _id, it should be sufficient to just remove the id from your mongoose model (not your GraphQL type). Alternatively, you could try adding a resolve function to your id field in the GraphQL type:
resolve: (item) => item._id.toString()

`updater` not working with Relay Modern because `ConnectionHandler.getConnection()` returns `undefined`

I'm using Relay Modern for my app and am trying to update the cache after a mutation using the updater and optimisticUpdater but it doesn't quite work.
Basically, I have a Link type with a votes connection - here's the relevant part of my schema:
type Link implements Node {
createdAt: DateTime!
description: String!
id: ID!
postedBy(filter: UserFilter): User
url: String!
votes(filter: VoteFilter, orderBy: VoteOrderBy, skip: Int, after: String, before: String, first: Int, last: Int): VoteConnection
}
type Vote implements Node {
createdAt: DateTime!
id: ID!
link(filter: LinkFilter): Link!
updatedAt: DateTime!
user(filter: UserFilter): User!
}
# A connection to a list of items.
type VoteConnection {
# Information to aid in pagination.
pageInfo: PageInfo
# A list of edges.
edges: [VoteEdge]
# Count of filtered result set without considering pagination arguments
count: Int!
}
# An edge in a connection.
type VoteEdge {
# The item at the end of the edge.
node: Vote
# A cursor for use in pagination.
cursor: String
}
Here's the code for my Link component request the votes in a fragment:
class Link extends Component {
render() {
const userId = localStorage.getItem(GC_USER_ID)
return (
<div>
{userId && <div onClick={() => this._voteForLink()}>â–²</div>}
<div>{this.props.link.description} ({this.props.link.url})</div>
<div>{this.props.link.votes.edges.length} votes | by {this.props.link.postedBy ? this.props.link.postedBy.name : 'Unknown'} {this.props.link.createdAt}</div>
</div>
)
}
_voteForLink = () => {
const userId = localStorage.getItem(GC_USER_ID)
const linkId = this.props.link.id
CreateVoteMutation(userId, linkId, this.props.viewer.id)
}
}
export default createFragmentContainer(Link, graphql`
fragment Link_viewer on Viewer {
id
}
fragment Link_link on Link {
id
description
url
createdAt
postedBy {
id
name
}
votes(last: 1000, orderBy: createdAt_DESC) #connection(key: "Link_votes", filters: []) {
edges {
node {
id
user {
id
}
}
}
}
}
`)
Finally, this is the CreateVoteMutation with the updater:
const mutation = graphql`
mutation CreateVoteMutation($input: CreateVoteInput!) {
createVote(input: $input) {
vote {
id
link {
id
}
user {
id
}
}
}
}
`
export default (userId, linkId, viewerId) => {
const variables = {
input: {
userId,
linkId,
clientMutationId: ""
},
}
commitMutation(
environment,
{
mutation,
variables,
updater: (proxyStore) => {
const createVoteField = proxyStore.getRootField('createVote')
const newVote = createVoteField.getLinkedRecord('vote')
const viewerProxy = proxyStore.get(viewerId)
const connection = ConnectionHandler.getConnection(viewerProxy, 'Link_votes')
// `connection` is undefined, so the `newVote` doesn't get inserted
if (connection) {
ConnectionHandler.insertEdgeAfter(connection, newVote)
}
},
onError: err => console.error(err),
},
)
}
The call to ConnectionHandler.getConnection(viewerProxy, 'Link_votes') only returns undefined, so the newVote doesn't actually get inserted.
Does anyone see what I'm doing wrong?
Problem:
When you're getting your connection:
const connection = ConnectionHandler.getConnection(viewerProxy, 'Link_votes')
you're trying to get the connection 'Link_votes' on the ViewerProxy. However what you want to be doing is getting the connection on the link.
Solution:
First you would need to get the id of the link that your adding the vote to.
const linkId = newVote.getLinkedRecord('link').getValue('id');
Then you want to get the Link Proxy so that you can then get the correct connection.
const linkProxy = proxyStore.get(LinkId)
Now that you have the Link Proxy that represents the link that you wanted the connection for, you can now get that connection.
const connection = ConnectionHandler.getConnection(linkProxy, 'Link_votes')
Sweet so now you've got the connection. Which solves the issue you're having.
However there is another problem, the way you go on to add the vote is wrong first you need to create an Edge out of it, and then add the edge.
First we need to create an edge
const voteEdge = createEdge(proxyStore, connection, newVote, 'VoteEdge');
Now that we have the voteEdge we can append it to the connection.
ConnectionHandler.insertEdgeAfter(connection, voteEdge).
Now it should all work. However you probably shouldn't be using the updater function for this kind of action. You should be using the RANGE_ADD configuration https://facebook.github.io/relay/docs/mutations.html#range-add and change how your server responds to that mutation.

Resources