How to add item into an list field? - graphql

I want to add members to chatRoom member list by passing memberId to graphql mutation function. But it pop up some errors. I have attached most code that relevant to this question as below. Please help me to figure it out. I guess create: {connect } might be the cause of this issue.
//Here is Mutation function
async function addMemberToChatRoom(parent, args, context, info) {
const member = await context.prisma.users({
where: {
id: args.memberId
}
});
const chatRoom = await context.prisma.updateChatRoom({
where: {
id: args.roomId
},
data: {
users: {
create: {
{ connect: { id: args.memberId } }
}
}
}
})
return chatRoom
}
//Here is prisma datamodel
type User {
id: ID! #id
name: String!
email: String! #unique
password: String!
}
type ChatRoom {
id: ID! #id
name: String!
users: [User]!
}
type Message {
id: ID! #id
content: String!
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
}
//Here is Schema.graphql
type Query {
info: String!
users: [User!]!
}
type Mutation {
signup(email: String!, password: String!, name: String!): AuthPayload
login(email: String!, password: String!): AuthPayload
createChatRoom(name: String!): ChatRoom
addMemberToChatRoom(roomId: String!, memberId: String!): ChatRoom
}
type AuthPayload {
token: String!
user: User!
}
type User {
id: ID!
name: String!
email: String!
}
type ChatRoom {
id: ID!
name: String!
users: [User!]
}
//Here is index.js
const { GraphQLServer } = require('graphql-yoga')
const { prisma } = require('./generated/prisma-client')
const Query = require('./resolvers/Query')
const Mutation = require('./resolvers/Mutation')
// const User = require('./resolvers/User')
const resolvers = {
Query,
Mutation
}
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
context: request => {
return {
...request,
prisma,
}
},
tracing: true,
})
server.start(() => console.log(`Server is running on http://localhost:4000`))
//Here is error
{
"data": {
"addMemberToChatRoom": null
},
"errors": [
{
"message": "Variable '$data' expected value of type 'ChatRoomUpdateInput!' but got: {\"users\":{\"create\":{\"id\":\"cjuzcf7lx75g60b953w50uwdc\"}}}. Reason: 'users.create[0].name' Expected non-null value, found null. (line 1, column 46):\nmutation ($where: ChatRoomWhereUniqueInput!, $data: ChatRoomUpdateInput!) {\n ^",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"addMemberToChatRoom"
]
}
]
}

I remove create and change it to
users:
{
connect: { id: args.memberId }
}
then it works.
async function addMemberToChatRoom(parent, args, context, info) {
const member = await context.prisma.users({
where: {
id: args.memberId
}
});
const chatRoom = await context.prisma.updateChatRoom({
where: {
id: args.roomId
},
data: {
users: {
connect: { id: args.memberId }
}
}
})
const returnedChatRoom = await context.prisma.chatRooms({
where: {
id: args.roomId
}
});
return returnedChatRoom
}

Related

Relationships with AwsCdk, DynamoDB and AppSync - Typescript and lambda functions

we are currently studying the stack: cdk, appsync and amplify to migrate our applications.
In our initial tests, we were able to upload a graphql api with only appsync wit relationships and it was very smooth, nice and fast.
When testing to build with cdk, we are having difficulties to create the relationships.
Here my code:
Schema
type Person {
id: ID!
name: String!
}
input PersonInput {
id: ID!
name: String!
}
input UpdatePersonInput {
id: ID!
name: String
}
type Client {
id: ID!
type: String!
personId: String
# Person: PersonConnection
Person: Person #connection(fields: ["personId"])
}
input ClientInput {
id: ID!
type: String!
personId: String!
}
input UpdateClientInput {
id: ID!
type: String
personId: String
}
My function
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
async function list() {
const params = {
TableName: process.env.CLIENT_TABLE,
}
try {
const data = await docClient.scan(params).promise()
return data.Items
} catch (err) {
console.log('DynamoDB error: ', err)
return null
}
}
export default list;
My table
const clientTable = new dynamodb.Table(scope, 'ClientTable', {
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
partitionKey: {
name: 'id',
type: dynamodb.AttributeType.STRING,
},
});
clientTable.addGlobalSecondaryIndex({
indexName: 'client-by-person-id',
partitionKey: {
name: 'personId',
type: dynamodb.AttributeType.STRING
},
sortKey: {
name: 'createdAt',
type: dynamodb.AttributeType.STRING
}
})
My query
query MyQuery {
listClients {
id
personId
type
Person {
name
}
}
}
However, my return to Person connection is null
"listClients": [
{
"id": "1",
"personId": "1",
"type": "PJ",
"Person": null
}
]
I would appreciate it if could point out our mistake
Solution of the problem based on the response of the Thorsten.
First, add resolver to the Person field in Client
export const clientResolvers = [{ typeName: "Client", fieldName: "Person" },...]
clientResolvers.map(((resolver: clientTypeResolver) => dataSource2.createResolver(resolver)))
Map function to the Person field in its lambda function
type AppSyncEvent = {
...
source: {personId: string,}
...
}
exports.handler = async (event:AppSyncEvent) => {
switch (event.info.fieldName) {
...
case "Person":
return await getPerson(event.source.personId);
}
}```
Function to solve the person field
async function getPerson(personId: string) {
console.log("CONTEXT\n" + JSON.stringify(personId, null, 2))
// console.log(context.source)
const params = {
TableName: process.env.PERSON_TABLE,
Key: { id: personId }
}
try {
const { Item } = await docClient.get(params).promise()
console.log("DATA\n" + JSON.stringify(Item, null, 2))
return Item
} catch (err) {
console.log('DynamoDB error: ', err)
}

"Variable '$data' expected value of type 'VoteCreateInput"

When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
When I am trying to do the "vote" mutation, getting the below error. My other mutations are working fine.
"data": null,
"errors": [
{
"message": "Variable '$data' expected value of type 'VoteCreateInput!' but
got: {\"user\":{\"connect\":
{\"id\":\"ck1j3nzi68oef090830r8wd6b\"}},\"link\":{\"connect\":
{\"id\":\"ck1j58loj8x570908njwe4eu7\"}}}. Reason: 'User' Expected non-null
value, found null. (line 1, column 11):\nmutation ($data:
VoteCreateInput!) {\n ^",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"vote"
]
}
]
Mutation
async function vote(parent, args, context, info) {
// 1
const userId = getUserId(context)
// 2
const linkExists = await context.prisma.$exists.vote({
user: { id: userId },
link: { id: args.linkId },
})
if (linkExists) {
throw new Error(`Already voted for link: ${args.linkId}`)
}
// 3
return context.prisma.createVote({
user: { connect: { id: userId } },
link: { connect: { id: args.linkId } },
})
}
datamodel.schema
type Link {
id: ID! #id
createdAt: DateTime! #createdAt
description: String!
url: String!
postedBy: User
votes: [Vote!]!
}
type User {
id: ID! #id
name: String!
email: String #unique
password: String!
links: [Link!]!
votes: [Vote!]!
}
type Vote {
id: ID! #id
link: Link!
user: User!
}
schema.graphql
type Mutation {
vote(linkId: ID!): Vote!
}
type Link {
id: ID!
description: String!
url: String!
postedBy: User
votes: [Vote!]!
}
There was a glitch in prisma database which was not updating with the change in data model.
I have created a new instance of the db and now its working fine.

Querying NOT NULL GraphQL with Prisma

Schema:
type TrackUser {
id: ID! #unique
createdAt: DateTime!
user: User #note there is no `!`
}
type User {
id: ID! #unique
name: String! #unique
}
I want to get Alls TrackUser where User is not null. What would be the query?
This would be a possible query:
query c {
trackUsers(where: { NOT: [{ user: null }] }) {
name
}
}
Here you can see how it looks in the Playground. I added a name to Trackuser in the datamodel in order to be able to create it from that side without a user.
this works, but I guess it is just a hack..
query TrackUsersQuery($orderBy: TrackUserOrderByInput!, $where: TrackUserWhereInput, $first: Int, $skip: Int) {
trackUsers(where: $where, orderBy: $orderBy, first: $first, skip: $skip) {
id
createdAt
user {
id
name
}
}
}
variables = {
where: {
user: {
name_contains: ''
}
}
}
UPDATE:
For Prisma2, here you have the possibilities:
For products that have no invoice, you can use the following:
const data = await prisma.product.findMany({
where: {
invoices: {
none: {
id: undefined,
},
},
},
})
And for Invoices that do not have a product associated:
const data = await prisma.invoice.findMany({
where: {
productId: null,
},
})
more details here: https://github.com/prisma/prisma/discussions/3461

Query.products is defined in resolvers but not in schema

Hi I defined rootQuery in Customer schema and then in Product schema I extended query. I wrote resolvers for product schema but then I got following error: Error: Query.products defined in resolvers, but not in schema.
When I move product queries to customer query definition it works.
I dont understand why I'm getting this error. Do I need implement some rootQuery and insert it into typeDefs array and then extend queries in Customer and Product ?
Customer schema
import CustomerPhoto from "./customerPhoto";
const Customer = `
type Customer {
customerID: ID!
firstname: String
lastname: String
phone: String
email: String
CustomerPhoto: CustomerPhoto
}
input CustomerInput {
firstname: String!
lastname: String!
phone: String!
email: String!
}
type Query {
customers(cursor: Int!):[Customer]
customer(id: Int!): Customer
}
type Mutation {
createCustomer(photo: String!, input: CustomerInput): Customer
updateCustomer(customerID: ID!, photo: String, input: CustomerInput): Customer
deleteCustomer(customerID: ID!): Customer
}
`;
export default [Customer, CustomerPhoto];
Product Schema
import ProductPhoto from "./productPhoto";
const Product = `
type Product {
productID: ID!
name: String!
description: String!
pricewithoutdph: Float!
pricewithdph: Float!
barcode: Int!
ProductPhoto: ProductPhoto
}
extend type Query {
products: [Product]
product(productID: ID!): Product
}
`;
export default [Product, ProductPhoto]
Here Im importing both schemas. Is there something missing ?
const schema = makeExecutableSchema({
typeDefs: [...Customer,...Product],
resolvers: merge(CustomerResolvers, ProductResolvers),
logger: {
log: e => {
console.log("schemaError", e);
}
},
resolverValidationOptions: {
requireResolversForNonScalar: true
}
});
Product Resolvers
const ProductResolvers = {
Query: {
products: (_, { cursor }) => {
return models.Product.findAndCountAll({
include: {
model: models.ProductPhoto,
attributes: ["productPhotoID", "photo"],
required: true
},
offset: cursor,
limit: 10,
attributes: ["productID", "name", "description", "pricewithoutdph", "pricewithdph", "barcode"]
}).then(response => {
return response.rows;
});
}
};
export default ProductResolvers;
Customer Resolvers
const CustomerResolvers = {
Query: {
customers: (_, {cursor}) => {
return models.Customer.findAndCountAll({
include: {
model: models.CustomerPhoto,
attributes: ["customerPhotoID", "photo"],
required: true
},
offset: cursor,
limit: 10,
attributes: ["customerID", "firstname", "lastname", "phone", "email"]
}).then(response => {
return response.rows;
});
}
......
}
};

How to pass the values of a connection to a subscription?

I'm trying to return a subscription which consists of the following:
const postsSubscription = gql`
subscription postAdded {
postAdded {
id
title
description
author{
name
}
}
}
`
What happens is that Author is type of User, and I just pass an authorId. That means that I don't have the author name when I create the Post:
createPost: async (root, req, { posts }) => {
const Item = {
id: uuid.v4(),
authorId: '565dbdc0-36f2-4bba-be67-c126d0c71fff',
...req
}
await posts.create({ Item })
pubsub.publish('postAdded', { postAdded: Item })
return Item
},
Here is the Author resolver:
Post: {
author: async({ authorId }, req, { users }) => {
const Key = { id: authorId }
const { Item } = await users.get({ Key })
return Item
}
}
Here is the schema:
type Post {
id: ID
title: String
description: String
author: User #relation(name: "PostAuthor")
}
type User {
id: ID
name: String
email: String
password: String
posts: [Post] #relation(name: "UserPosts")
}
type PostPayload {
post: Post
}
type CreateUserPayload {
user: User
}
type Query {
allPosts: [Post]
allUsers: [User]
post(id: ID!): Post
user(id: ID!): User
}
type Mutation {
createPost(input: CreatePostInput!): PostPayload
updatePost(input : UpdatePostInput!): PostPayload
createUser(input : CreateUserInput!): CreateUserPayload
}
type Subscription {
postAdded: Post
}
input CreatePostInput {
title: String!
description: String!
}
input UpdatePostInput {
id: ID!
title: String!,
description: String!
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
So, my question is, how to pass all required fields (including the connections) to the subscription?
I made it work, but not as I wanted.
1) I had to remove this part of the code:
Post: {
author: async({ authorId }, req, { users }) => {
const Key = { id: authorId }
const { Item } = await users.get({ Key })
return Item
}
}
and added this function to the createPost function itself:
createPost: async (root, { input }, { posts, users }) => {
const Key = { id: '3b1884b8-9ee7-4d9d-ab2f-ff32bcd69b9a' }
const user = await users.get({ Key })
const Item = {
id: uuid.v4(),
author: user.Item,
...input
}
await posts.create({ Item })
await pubsub.publish(POST_ADDED_TOPIC, { [POST_ADDED_TOPIC]: Item })
return { post: Item }
}
So this is kinda fixed. But, if you know how to fix this, using the first approach (Post: author thingy) I'll appreciate.

Resources