Prisma binding Nested filtering - apollo-server

I'm working on a food order platform with Prisma, Prisma-binding and Apollo Server on the backend. A customer can choose a restaurant in his neighbourhood and add one or more dishes to his cart. It is possible that when a dish from restaurant x is already added, the customer decides to order from another restaurant, restaurant y. Therefore I need to filter the added dishes when making an order based on the customer id and the final chosen restaurant in the backend first before creating the order and payment url.
I've got three data types inside my prisma datamodel: Customer, CartItem and Dish
type Customer {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
name: String
email: String
phone: String
user: User
cart: [CartItem]!
orders: [Order]!
}
type CartItem {
id: ID! #id
quantity: Int! #default(value: 1)
dish: Dish!
customer: Customer! #relation(link: INLINE)
}
type Dish {
id: ID! #id
name: String!
price: String!
description: String!
isAvailable: Boolean! #default(value: true)
category: String
restaurant: Restaurant!
}
In the Prisma GraphQL playground that is directly connected to the database I can filter the cartItems that I need to create the order like this:
query {
customer(where: { id: "ck8zwslgs00da0712cq88e3oh" } ) {
id
cart(where: { dish: { restaurant: { id: "ck904gwl400mz0712v0azegm3" } } }) {
quantity
dish {
name
price
restaurant {
id
name
}
}
}
}
}
output:
{
"data": {
"customer": {
"id": "ck8zwslgs00da0712cq88e3oh",
"cart": [
{
"quantity": 2,
"dish": {
"name": "Nachos Plate Hawaii",
"price": "1150",
"restaurant": {
"id": "ck904gwl400mz0712v0azegm3",
"name": "Taco Bell"
}
}
},
{
"quantity": 1,
"dish": {
"name": "Nachos Plate Vulcano",
"price": "1250",
"restaurant": {
"id": "ck904gwl400mz0712v0azegm3",
"name": "Taco Bell"
}
}
}
]
}
}
}
So far so good but now I need the same query in the Apollo Server using prisma-binding. I tried a few things but none of them are working. The first two are returning an error "Field \"cart\" is not defined by type CustomerWhereUniqueInput". The last two are just returning every cartItem without the restaurant filter.
const data = await ctx.db.query.customer({
where: {
AND: [
{
id: args.customerID
},
{
cart: {
dish : {
restaurant: {
id: args.restaurantID
}
}
}
}
]
}
}, info);
const data = await ctx.db.query.customer({
where: {
id: args.customerID
cart: {
dish : {
restaurant: {
id: args.restaurantID
}
}
}
}
}, info);
const data = await ctx.db.query.customer({
where: {
id: args.customerID
},
cart: {
where: {
dish : {
restaurant: {
id: args.restaurantID
}
}
}
}
}, info);
const data = await ctx.db.query.customer({
where: {
id: args.customerID
},
cart: {
dish : {
restaurant: {
where: {
id: args.restaurantID
}
}
}
}
}, info);
Can someone help me out with the right way to filter on the customer id and the restaurant id?

Related

How to merge a type whose fields come from two different GraphQL subschemas?

I'm running a gateway with apollo-server v2 with graphql-tools v7 and have the following services and subschemas, which I've set up as a (contrived) proof-of-concept:
Service 1:
Query {
getUser(input: GetUserInput!): User
}
input GetUserInput {
id: ID!
}
type User {
id: ID!
contacts: Contacts
}
type Contacts {
id: ID!
primary: String
}
Service 2:
Query {
getUser(input: GetUserInput!): User
}
input GetUserInput {
id: ID!
}
type User {
id: ID!
contacts: Contacts
}
type Contacts {
id: ID!
secondary: String
}
The gateway has the combined schema like this:
Query {
getUser(input: GetUserInput!): User
}
input GetUserInput {
id: ID!
}
type User {
id: ID!
contacts: Contacts
}
type Contacts {
id: ID!
primary: String
secondary: String
}
The gateway is configured to stitch the schemas and merge the User type like this:
import { stitchSchemas } from "#graphql-tools/stitch"
...
const schema = stitchSchemas({
subschemas: [
{
schema: service1Schema,
executor: service1Executor,
merge: {
User: {
fieldName: "getUser",
selectionSet: "{ id }",
args: (originalObject) => ({
input: {
id: originalObject.id
}
}),
},
}
},
{
schema: service2Schema,
executor: service2Executor,
merge: {
User: {
fieldName: "getUser",
selectionSet: "{ id }",
args: (originalObject) => ({
input: {
id: originalObject.id
}
}),
},
}
},
]
})
However, when I send a query to fetch the contacts field for a User, only Service 2 is called by the gateway. Instead, I would have expected the gateway to call Service 1 to get its representation of User.contacts and also Service 2 to get its representation of User.contacts and then merge the two into a combined result.
The query I executed:
query GetContacts {
getUser(input: { id: "123" }) {
contacts {
primary
secondary
}
}
}
The result I got (and I see in logs that Service 1 was not called at all):
{
"data": {
"getUser": {
"contacts": {
"primary": null,
"secondary": "Secondary contact from Service 2"
}
}
}
}
The result I expected:
{
"data": {
"getUser": {
"contacts": {
"primary": "Primary contact from Service 1", <-- This should be included in the result.
"secondary": "Secondary contact from Service 2"
}
}
}
}
I have confirmed that I'm able to fetch other fields (e.g. User.name) from Service 1 successfully, so the subschema for Service 1 is configured correctly on the gateway (though probably not the merge options for it).
Since this is similar to the example from the graphql-tools documentation, I'm confused about why the fields of the Contacts type aren't merged as expected in the query response.

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 graphql computed fields on relations

I have the following datamodel:
type Tvshow {
id: ID! #unique
title: String!
pricing: [Pricing]
startDate: DateTime!
endDate: DateTime!
subscribers: [Tvshowsubscription!]
.....
}
type FavoriteTvshow {
id: ID! #unique
tvshow: Tvshow!
user: User!
}
type User {
id: ID! #unique
name: String
email: String! #unique
password: String
googleID: String #unique
resetToken: String
resetTokenExpiry: String
permissions: [Permission]
address: Address
phone: String
favorites: [FavoriteTvshow!]
tvshowSubscriptions: [Tvshowsubscription!]
}
I have my custom Tvshow resolver using addFragmentToInfo:
resolver-queries.js
const Query = {
...
favoriteTvshows: forwardTo('db'),
tvshow: (parent, args, ctx, info) => {
const fragment = `fragment EnsureComputedFields on Tvshow { pricing { minQuantity maxQuantity unitPrice} subscribers { id }}`
return ctx.db.query.tvshow({}, addFragmentToInfo(info, fragment))
},
....
};
tvshow-resolver.js
const Tvshow = {
countSubscribers: (parent) => {
return parent.subscribers.length;
},
}
This is an example, I have more computed fields for Tvshow
I can query Tvshows with countSubscribers, It works fine doing something like this:
query SINGLE_TVSHOW_QUERY($id: ID!) {
tvshow(where: { id: $id }) {
id
title
pricing {
minQuantity
maxQuantity
unitPrice
}
startDate
endDate
countSubscribers
}
}
But what I want to do is to get all the favorite Tvshows from an user returning the countSubscribers, a query for that could be something like this:
query FAVORITES_FROM_USER($userId: ID!) {
favoriteTvshows(where: { user: {id: $userId} }) {
tvshow {
id
title
startDate
endDate
countSubscribers
}
}
}
The problem is that when I query this, in the tvshow-resolver.js I mentioned before, the parent doesn’t have any subscribers object
The error was very silly but I will post it anyway. I needed subscribers in the query
query FAVORITES_FROM_USER($userId: ID!) {
favoriteTvshows(where: { user: {id: $userId} }) {
tvshow {
id
title
startDate
endDate
subscribers { <---
id
quantity
}
countSubscribers
}
}
}
That way the parent in tvshow-resolver.js will have subscribers object

GraphQL - Prisma - resolvers using external API

I'am having this schema:
type Invoice {
id: ID! #unique
description: String
charge: Charge
}
type Charge {
id: ID! #unique
amount: Float
dataFromAPI: DataFromAPI
}
type DataFromAPI {
id: ID! #unique
status: String
}
in the Query Resolver, I have:
async function charge(parent, args, ctx, info) {
chargeData = await ctx.db.query.charge(args, info)
chargeData.dataFromAPI = await DO_THE_API_CALL_TO_RETRIEVE_DATA()
return chargeData
}
and
async function invoice(parent, args, ctx, info) {
invoiceData = await ctx.db.query.invoice(args, info)
return invoiceData
}
the query:
query ChargeQuery {
charge {
id
amount
dataFromAPI
}
}
will return
{
charge {
id: '232323'
amount: 323
dataFromAPI: 'GREAT! DATA IS FROM API'
}
}
but this query:
query InvoiceQuery {
invoice {
id
description
charge {
id
amount
dataFromAPI
}
}
}
will return
{
Invoice {
id: '7723423',
description:'yeah',
charge {
id: '232323'
amount: 323
dataFromAPI: null
}
}
}
dataFromAPI is null because I have not called the API in this resolver.
Where should I call the function DO_THE_API_CALL_TO_RETRIEVE_DATA().
In every resolvers? I guess it is not scalable to do that.
The solution is:
We should use a resolver on the field level.
schema.graphql
type Charge {
id: ID!
invoice: Invoice!
messageErrorPayment: String
stripeChargeId: String!
dateErrorPayment: DateTime
createdAt: DateTime!
chargeData: ChargeData
}
/resolvers/index.js
const { Query } = require('./Query')
const { Mutation } = require('./mutation/Mutation')
const { Charge } = require('./Charge')
module.exports = {
Query,
Mutation,
Charge,
}
charge.js
async function chargeData(parent, args, ctx, info) {
return {
dataFromAPI: await DO_THE_API_CALL_TO_RETRIEVE_DATA()
}
}
const Charge = {
chargeData,
}
module.exports = {
Charge,
}
source: https://www.prisma.io/forum/t/how-to-use-field-resolvers-to-get-aggregates-of-inner-relation-types/2930/2?u=alan345

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

Resources