How to pass nested variables to the GraphQL query in Apollo? - graphql

Trying to pass nested variables to the GraphQL query but my server gets only top-level variables (shopId), everything else is null.
I tried:
#1
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice(
$shopId: String!
$address1: String
$zip: String
$city: String
$countryCode: String
) {
calculatePackagePrice(
where: {
shopId: $shopId
destination: {
address1: $address1
zip: $zip
city: $city
countryCode: $countryCode
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
)
And #2:
export function CALCULATE_PACKAGE_PRICE({ shopId, destination }) {
return gql`
query CalculatePackagePrice {
calculatePackagePrice(
where: {
shopId: "${shopId}"
destination: {
address1: "${destination.address1}"
zip: "${destination.zip}
city: "${destination.city}"
countryCode: "${destination.countryCode}"
}
}
) {
name
price
userErrors {
field
message
}
}
}
`
}
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE({
shopId: shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
})
)
It works just fine when I hardcoded variables content to the queries. What I'm doing wrong?

Here is a helpful snippet from graphql docs,
All declared variables must be either scalars, enums, or input object types. So if you want to pass a complex object into a field, you need to know what input type that matches on the server.
You're correctly passing in the variables as strings, but then trying (perhaps successfully, but I've never seen the syntax before) to create the object in the gql template string. Instead, create an input type for destination and where.
input WhereInput {
shopId: String!
destination: DestinationInput!
}
input DestinationInput {
address1: String!
zip: String!
city: String!
countryCode: String!
}
then change the query on the client (and update the server definition),
const CALCULATE_PACKAGE_PRICE = gql`
query CalculatePackagePrice($where: WhereInput!) {
calculatePackagePrice(where: $where) {
name
price
userErrors {
field
message
}
}
}
`
then pass the variables like,
const [calculatePackagePrice, { loading, data }] = useLazyQuery(
CALCULATE_PACKAGE_PRICE,
{
variables: {
where: {
shopId,
destination: {
address1: "Example 123",
zip: "123",
city: "Test",
countryCode: "US",
},
},
}
}
)

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

Prisma 2 query to return records only that are associated with ALL of the provided tag IDs

I have tables Principles and Tags. And there is a many-to-many relation between them (joined implicitly).
Without using prisma.raw, how can I run the following query?
SELECT p.id, p.title, p.description, p.createdAt, p.modifiedAt
FROM principle p
WHERE EXISTS (SELECT NULL
FROM _PrincipleToTag pt
WHERE pt.B IN (${tagIds.join(',')})
AND pt.A = p.id
GROUP BY pt.A
HAVING COUNT(DISTINCT pt.B) = ${tagIds.length})
How can I update this Prisma 2 query such that the principles returned are only principles that are associated with ALL of the provided tagIds?
export const principles = ({ tagIds }) => {
const payload = {
where: {
//TODO filter based on tagIds
},
}
return db.principle.findMany(payload)
}
The docs mention contains and in and every, but I can't find examples of what I'm trying to do.
I'm using RedwoodJs, Prisma 2, Apollo, GraphQL.
Update in response to comment: here is the SDL:
input CreatePrincipleInput {
title: String!
description: String
}
input CreatePrincipleWithTagsInput {
title: String!
description: String
tagIdsJson: String
}
input CreateTagInput {
title: String!
description: String
}
# A date string, such as 2007-12-03, compliant with the `full-date` format
# outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for
# representation of dates and times using the Gregorian calendar.
scalar Date
# A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the
# `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO
# 8601 standard for representation of dates and times using the Gregorian calendar.
scalar DateTime
type Mutation {
createPrinciple(input: CreatePrincipleInput!): Principle
createPrincipleWithTags(input: CreatePrincipleWithTagsInput!): Principle
updatePrinciple(id: Int!, input: UpdatePrincipleInput!): Principle!
deletePrinciple(id: Int!): Principle!
createTag(input: CreateTagInput!): Tag!
updateTag(id: Int!, input: UpdateTagInput!): Tag!
deleteTag(id: Int!): Tag!
}
type Principle {
id: Int!
title: String!
description: String!
tags: [Tag]
createdAt: DateTime!
modifiedAt: DateTime!
}
type Query {
redwood: Redwood
principles(searchQuery: String, tagIds: [Int]): [Principle!]!
tags: [Tag!]!
tagsByLabel(searchTerm: String): [TagCount!]!
tag(id: Int!): Tag!
}
type Redwood {
version: String
}
type Tag {
id: Int!
title: String!
principles: [Principle]
description: String
createdAt: DateTime!
modifiedAt: DateTime!
}
type TagCount {
id: Int!
title: String!
count: Int!
principles: [Principle]
description: String
createdAt: DateTime!
modifiedAt: DateTime!
}
# A time string at UTC, such as 10:15:30Z, compliant with the `full-time` format
# outlined in section 5.6 of the RFC 3339profile of the ISO 8601 standard for
# representation of dates and times using the Gregorian calendar.
scalar Time
input UpdatePrincipleInput {
title: String
description: String
}
input UpdateTagInput {
title: String
description: String
}
It doesn't look like you are using prisma 2. Prisma 2 uses models (not types) and has arrays classified like Principles[] vs [Principles]. Maybe Redwood does the conversion(Never used it).
I created your model in Prisma 2 and used the following command to get a single principle that has the two tags associated with it. Keep in mind the IDs in there are from my test dataset. Hopefully, you can modify this to your code. If not, please create a sandbox/playground with minimal code for us to test.
export const principles = async ({ searchQuery, tagIds }) => {
const payload = {
where: {
OR: [
{ title: { contains: searchQuery } },
{ description: { contains: searchQuery } },
],
userId: userIdFromSession,
},
}
if (tagIds.length) {
const whereAnd = []
tagIds.forEach((tagId) => {
whereAnd.push({
tags: { some: { id: tagId } },
})
})
payload.where.AND = whereAnd
}
const result = await db.principle.findMany(payload)
return result
}
You could try something like this
export const principles = ({ searchQuery, tagIds }) => {
const payload = {
where: {
OR: [
{ title: { contains: searchQuery } },
{ description: { contains: searchQuery } },
],
// using the `in` operator like this
tagId: { in: tagIds },
userId: userIdFromSession,
},
}
console.log('db.principle.findMany(payload)', payload)
return db.principle.findMany(payload)
}
That should do the trick!
I had to resort to using AND for something similar - hope this helps!
const tagIds = [9,6];
where: {
// ...
AND: tagIds.map(tagId => ({
tags: {
some: {
id: {
equals: tagId,
},
},
},
})),
}

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

How to write graphql query wiith custom objects

The server side of graphql is with nodejs and express. This is the schema for graphql. It has one query which accepts DateT object having from and to dates.
var schema = buildSchema(`
type Query {
courseWithDate(
timeFilter: DateT
): Course
},
type Course {
...
from: String
to: String
},
type DateT{
from : String
to : String
}
`);
and this is how I am getting courses
I am able to run the application with this url
localhost:4000/graphql
This is the query I am using
query courseWithDate($from: dateFrom, $to: dateTo) {
courseWithDate(timeFilter: {
from: "${dateFrom}"
to: "${dateTo}"
}) {
title
...
}
}
with these parameters
{
"from": "2019-10-10","to":"2019-10-10"
}
Exception message I get is related to the input type I am trying to pass.
{
"errors": [
{
"message": "The type of Query.courseWithDate(timeFilter:) must be Input Type but got: DateT.",
"locations": [
{
"line": 6,
"column": 25
}
]
}
]
}
I'm not sure, but probably this style looks more like best practice
type Course {
id: Int
title: String
author: String
from: String
to: String
description: String
topic: String
url: String
}
input DateInput {
dateFrom: String!
dateTo: String!
}
type Query {
courseWithDate(input: DateInput!, name: String!): Course
}
And Query on client side should be:
{
courseWithDate(input: {
dateFrom: "${dateFrom}"
dateTo: "${dateTo}"
}
name: "${name}")
{
id
name
}
}

How to mutate nested object using graphql

I have a mongoose schema like the one below:
import mongoose from 'mongoose'
const ProjectSchema = new mongoose.Schema({
name: {
type: String
},
owner: {
type: String
},
member: {
type: String
},
updatedDate: {
type: Date
},
description: {
type: String
},
folder: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Folder'
},
dataSources: [{
name: {
type: String
},
updatedDate: {
type: Date
},
}],
propjectHistory: [{
no: {
type: Number
},
member: { // is this reference or just a string?
type: String
},
action: {
type: String
},
updatedDate: {
type: Date
},
}]
})
const Project = mongoose.model('Project', ProjectSchema)
And I integrated with graphql using graffiti and graffiti-mongoose.
However, the Graphiql documentation shows that I only have the ones below:
addProject(input: addProjectInput!):
name: String
owner: String
member: String
updatedDate: Date
description: String
folder: ID
clientMutationId: String!
I could successfully add project with a mutation query only using those parameters, but it seems that I cannot even send mutation query with projectHistory and dataSource, which are embedded inside project schema.
However, I can access projectHistory and dataSource when I send find queries.
I can't find any documentation about the problem.
sample mutation query without nested ones works.
mutation {
addProject(input:{
clientMutationId: "1"
name: "testproject",
owner: "keonwoo",
member: "keonwoo",
updatedDate: "2015-07-24T13:23:15.580Z",
description: "this is test project",
folder: "56fb93403eab9e1c14358fb7"
}){
clientMutationId
changedProjectEdge{
node{
_id
name
updatedDate
}
}
}
}
the above mutation returns the following:
{
"data": {
"addProject": {
"clientMutationId": "1",
"changedProjectEdge": {
"node": {
"_id": "56fb93ab3eab9e1c14358fb8",
"name": "testproject",
"updatedDate": "2015-07-24T13:23:15.580Z"
}
}
}
}
}
I am not using client like relay.
the problem was with the graffiti-mongoose library.
Turns out that maintainers of graffiti-mongoose just added embedded object feature and I did not update.

Resources