How to resolve Inconsistent __typename error in Relay? - graphql

I just tried to implement the Relay in Frontend for this graphql tutorial, In that tutorial, they created graphql server to store URL(Link) bookmarks with the User who posted those URLs.
The relationship between the link and the users is:
Link belongs_to :user,
User has_many :links.
And I listed out all the Links with Users in Frontend, at the time I got the below error.
Warning: RelayResponseNormalizer: Invalid record 1. Expected __typename to be consistent, but the record was assigned conflicting types Link and User. The GraphQL server likely violated the globally unique id requirement by returning the same id for different objects
I'm not aware of how much it will impact the application. because I got the expected result from Frontend.
Frontend View of Query.
I read this relay official blog for this kind of error, but there is no example to know how exactly to resolve this. so can someone help to resolve this?
Relay Query
graphql`
query LinkListQuery {
allLinks {
id,
description,
url,
postedBy {
id,
name
}
}
}`
Schema:
input AUTH_PROVIDER_CREDENTIALS {
email: String!
password: String!
}
input AuthProviderSignupData {
credentials: AUTH_PROVIDER_CREDENTIALS
}
type Link implements Node {
description: String!
id: ID!
postedBy: User
url: String!
votes: [Vote!]!
}
input LinkFilter {
OR: [LinkFilter!]
descriptionContains: String
urlContains: String
}
type Mutation {
createLink(description: String!, url: String!): Link!
createUser(name: String!, authProvider: AuthProviderSignupData): User!
createVote(linkId: ID): Vote!
signinUser(credentials: AUTH_PROVIDER_CREDENTIALS): SignInUserPayload
}
"""An object with an ID."""
interface Node {
"""ID of the object."""
id: ID!
}
type Query {
allLinks(filter: LinkFilter, first: Int, skip: Int): [Link]!
"""Fetches an object given its ID."""
node(
"""ID of the object."""
id: ID!
): Node
}
"""Autogenerated return type of SignInUser"""
type SignInUserPayload {
token: String
user: User
}
type User implements Node {
email: String!
id: ID!
links: [Link!]!
name: String!
votes: [Vote!]!
}
type Vote {
id: ID!
link: Link!
user: User!
}

Related

GraphQL Prisma - use orderBy on nested relationships

I am fairly new to GraphQL Prisma combo, I cannot seem to order nested relationships without error because the only available orderBy fields are regarding the ID.
My intended goal in the playground would be to create a query that looks similar to the following:
getPosts{
connections(orderBy: "text_asc"){
id
text
}
}
Resolver
getPosts(parent, args, ctx, info){
return ctx.db.query.posts({}, info)
}
Data Model
type Post {
id: ID! #id
isPublished: Boolean! #default(value: false)
title: String!
text: String!
connections: [Connection!]! #relation(name: "ClientConnect")
}
type Link {
id: ID! #id
text: String!
url: String!
}
type Connection {
id: ID! #unique #id
client: Link #relation(name: "LinkConnect")
}

Is there a more elegant way instead of writing lots of queries?

I'm building a small blog using GraphQL, Apollo Express and MongoDB with Mongoose.
Currently, articles are fetched by their IDs and visitors can browse an article with the id of let's say "123" here: example.com/articles/123
Instead, I would like to use slugs, so visitors can go to example.com/articles/same-article-as-above
My resolver so far:
import { gql } from 'apollo-server-express';
export default gql`
extend type Query {
articles: [Article!]
article(id: ID!): Article
}
type Article {
id: ID!
slug: String!
title: String!
desription: String!
text: String!
}
`;
I could just add another query:
articleBySlug(slug: String!): Article
This would work perfectly fine. However, this doesn't look very elegant to me and I feel like I am missing some basic understanding. Do I really have to add a new query to my resolvers each time I am trying to fetch an article by its title, text, description or whatever? I would end up with a lot of queries like "articleByTitle", "articleByDate", and so on. Can someone please give me a hint, an example or some best practices (or just confirm that I do have to add more and more queries☺)?
A common way to do this is to add all inputs to the same query, and make them optional:
export default gql`
extend type Query {
articles: [Article!]
article(id: ID, slug: String, date: String, search: String): Article
}
type Article {
id: ID!
slug: String!
title: String!
description: String!
text: String!
}
`;
Then, in the resolver just check that exactly one of id, slug or date is provided, and return an error if not.
Another option is to use a search string similar to what Gmail uses (eg id:x before:2012-12-12) that you then parse in the resolver.
export default gql`
extend type Query {
articles: [Article!]
article(search: String): Article
}
type Article {
id: ID!
slug: String!
title: String!
description: String!
text: String!
}
`;
A third option is to set up a separate search query that can return several types:
export default gql`
extend type Query {
articles: [Article!]
search(query: String!, type: SearchType): SearchResult
}
union SearchResult = Article | User
enum SearchType {
ARTICLE
USER
}
type Article {
id: ID!
slug: String!
title: String!
description: String!
text: String!
}
type User {
id: ID!
email: String!
name: String!
}
`;

How to resolve subselections / relations in prisma (nested lists)

Let's take an example from the github repo of prisma:
We have a user, the user could have multiple posts, and one post could have multiple links.
My goal is, to retrieve all posts and all links.
This means, my response is a list (links) in a list (posts).
I want to map the values I get back as two nested lists.
datamodel.prisma
type User {
id: ID! #id
email: String! #unique
name: String
posts: [Post]!
}
type Post {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
published: Boolean! #default(value: false)
title: String!
content: String
author: User!
links: [Link]!
}
type Link {
id: ID! #id
url: String
title: String
post: Post!
}
schema.graphql
type Query {
...
}
type Mutation {
...
}
type Link {
id: ID!
url: String
title: String
post: Post!
}
type Post {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
published: Boolean!
title: String!
content: String
author: User!
}
type User {
id: ID!
email: String!
name: String
posts: [Post]!
}
I want to query all posts of a user, and all of the links for every post in the response.
How would I query this request?
user {
id
posts {
id
links {
id
}
}
}
The above code snipper would not work.
EDIT
I want to use the following:
User: {
listPosts: (parent, args, context, info) {
return context.prisma.posts().links()
}
}
So in my response (data in front-end via react-apollo Query Component), I want to map over posts AND the links in each post.
BUT the links attribute in posts is null.
Is there another way to achieve this?!
According to the docs:
Prisma client has a fluent API to query relations in your database. Meaning you can simply chain your method calls to navigate the relation properties of the returned records. This is only possible when retrieving single records, not for lists. Meaning you can not query relation fields of records that are returned in a list.
In order to get around that limitation, you can use the $fragment method:
const fragment = `
fragment UserWithPostsAndLinks on User {
id
email
name
posts {
id
title
content
links {
id
url
title
}
}
}
`
const userWithPostsAndLinks = await prisma.user({ id: args.id }).$fragment(fragment)

Cannot query field 'password' on type 'User' graphql

I'm using graphql and prisma.
datamodel.prisma
type User {
id: ID! #id
createdAt: DateTime! #createdAt
updatedAt: DateTime! #updatedAt
email: String! #unique
password: String!
first_name: String
}
schema.graphql
scalar Date
type Query {
users: [User!]!
}
type User {
id: ID!
createdAt: Date!
updatedAt: Date!
email: String!
first_name: String
}
resolver
users: (parent, args, context) => {
return context.prisma.users();
}
I expected to get a user list, but received the error:
query
{
users {
email
}
}
error
"Cannot query field 'password' on type 'User'. (line 7, column 5):\n password\n ^"
UPDATE 1
Tried to use a fragment, but got the same:
{
users {
...userFields
}
}
fragment userFields on User {
email
}
I'd like to also add a scenario that can very easily cause this same issue that took me a while to debug and I'm sure others will encounter, because it took me quite some time to realize the issue was actually being caused in my FRONTEND code where I was defining my auth-related Mutations.
Set Up
Here's what that looked like while developing much of my application:
datamodel.prisma (I've omitted some fields for simplicity sake)
type User {
id: ID! #id
name: String!
email: String! #unique
password: String!
}
schema.graphql (just showing the signUp Mutation for simplicity)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
SignUp.js (where I access the signUp Mutation exposed in schema.graphql)
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
password
}
}
`
Notice that I am returning id, email, name, and password - this was because I wanted to make sure everything was working in development.
Introducing the Cannot query field 'password' on type 'User' error
Once I began working on security and created a special User type in schema.graphql so that I could hide protected fields such as password, that's when I got this issue:
schema.graphql (notice that I am now not exposing the password field on this frontend-facing User type)
type Mutation {
signUp(email: String!, password: String!, name: String!): User!
}
type User {
id: ID!
name: String!
email: String!
}
Solution
Because of the nature of this error message, I spent most of my morning puzzling over my backend code. But it turned out that the error was actually being caused in SignUp.js, where I was RETURNING the password field.
The solution was to simply remove that line from the list of return fields like so:
const SIGNUP_MUTATION = gql`
mutation SIGNUP_MUTATION(
$email: String!
$name: String!
$password: String!
) {
signUp(email: $email, name: $name, password: $password) {
id
email
name
}
}
`
Key Lessons
So if you're experiencing this issue, please check ALL of your relevant mutations and make sure that you're not returning any fields that you have protected as I described here.
Be sure to also check your frontend code and make sure you aren't trying to return fields that you have now protected and are no longer exposing to the frontend.
I hope this is helpful and saves people some time!
... aaah Prisma ...
I don't know if interfaces, unions or input types are supported. Graphql docs
Prisma generates almost everything ... but defining password as required (as type for DBB generation) for datamodel should not block querying for a type subset or type defined on existing model without using all fields.
For me it's a bit missleading error message. It can be resolver related.
Try to match types in resolver, don't return direct prisma query (operates on model types), but map queried data (an array) to filter out password field/property (to be query type compatible). It's a security concern, too - passwords shouldn't be read from outside.
I've created custom query which return a fragment and seems the error gone.
Just run in your console(in prisma folder):
PRISMA_MANAGEMENT_API_SECRET=mysecret42 prisma deploy

authenticate user and serve only their related data

I have a schema in graphcool with these nodes (not sure what the correct term is here... leaf? node? model? type??)
type User #model {
auth0UserId: String #isUnique
createdAt: DateTime!
id: ID! #isUnique
userIdentifier: String
bundleIdentifier: String
updatedAt: DateTime!
devices: [Device!]! #relation(name: "UserOnDevice")
tokens: [Token!]! #relation(name: "TokenOnUser")
}
type Device #model {
id: ID! #isUnique
deviceIdentifier: String!
users: [User!]! #relation(name: "UserOnDevice")
token: Token #relation(name: "DeviceOnToken")
}
I'd like to make it so that a user must be authenticated and be related to the device data to be able to query on it. So, for a query like:
query($deviceIdentifier: String!) {
device(deviceIdentifier: $deviceIdentifier) {
id
}
}
This should return null unless they are autthenticated and are a user in the specified relation. I was thinking I needed a permission query like this one:
query ($node_id: ID!, $user_id: ID!) {
SomeDeviceExists(filter: {
id: $node_id,
users: {
id: $user_id
}
})
}
But it turns out that is invalid. How do I do it?
query ($node_id: ID!, $user_id: ID!) {
SomeDeviceExists(filter: {
id: $node_id,
users_some: {
id: $user_id
}
})
}
but this does require submitting the user_id in the request.

Resources