Should we add the backend graphql schema in the case of Apollo Client? - graphql

I'm using backend node server with the Apollo graphql (Server side) and on the client, I'm using Apollo Client as well.
I created a few client specific types in my client Schema for Apollo client but I'm wondering:
Should I do the same for backend Types (models)? Just to add some sanity, etc.
Let me explain it a bit detailed with an example:
Here is the client schema: (Client specific types)
import gql from 'graphql-tag';
export default gql`
type System {
showSignInModal: Boolean!
}
type Robot {
name: String!
status: String!
}
type Member {
name: String!
isLogged: Boolean!
}
type Author {
id: Int!
posts: Int!
name: String
}
input AuthorInput {
id: Int!
posts: Int!
name: String
}
`;
I have a query that fetches the user data from the server (Server specific data)
so should I describe the whole User type in my schema as well?
import gql from "graphql-tag";
export const GET_USER_SHORT_DATA = gql`
mutation getUserShortData {
me {
id,
email,
name,
profileUrl,
locale
}
}
`;
Thanks for any advice!

I assume you're using mongoose for your database operations. Your main types should be defined in your mongoose schema as GraphQL is just a query language meant for "getting only what you want". So, you should add the same types in both the places. In your mongoose code for database handling & In your GraphQL Schema for query fetch support.
For Client side, if you're using a typed language like typescript, then you should define them in the client side too so as to prevent bugs & get better IDE suggestions.

Related

How to fetch schema from graphQL API endpoint

I need to fetch a schema from graphQL API endpoint.
So the result must be a string like:
type User {
id: ID!
name: String!
}
type Home {
user: User!
address: String
country: String!
}
type Query {
MyUsers: User!
}
Is it possible to do by using codegen?
It really depends on the GraphQL server you are using, Some GraphQL servers provide a GraphQL Explorer. The GraphQL Explorer is a page where you can manually input GraphQL queries into the page such as Graphiql
Other way is to try out graphql-cli you can do with the below command,
get-graphql-schema ENDPOINT_URL -j > schema.json

Graphql - How to include schema from other types

Let us say I have the following type:
type Foo {
id: ID!
field1: String
}
Now, I wish to define another type, which includes the earlier type. Something like this:
type Bar {
...Foo,
field2: String
}
How do I achieve the above in graphql? I want to basically first create a type, and then include that type in the definition of other types so that I don't have to type all the attributes multiple times.
I am using Amplify / AWS Appsync so if there's any special directive that I could use that would also be helpful
GraphQL has the concept interfaces for this. Appsync, AWS's GraphQL implementation, supports interfaces.
[Edit:] GraphQL does not support "...spread" syntax for interfaces. Fields are defined explicitly. Spread syntax does figure in GraphQL, but in the form of Fragments, resuable units of fields for reducing repetition in queries.
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
Amplify, which automagically creates AppSync schemas, resolvers and data sources, is apparently a more difficult story. The amplify-cli repo has an open feature request, Does the GraphQL Transformer support interfaces?. I am no Amplify expert, but a quick look at the loooong feature request comment thread suggests the answer for Amplify is "not out-of-the-box", but "maybe works in narrow circumstances or with advanced customization".

How to update single data when using Fauna and GraphQL

I have some problems when using Fauna and GraphQL
When I use GraphQL to update data, all parameters are required
For example
// Schema
type User {
username: String!
password: String!
phone: String!
}
// Mutation
mutation {
updateUser(id: "xxxxx", {"phone": "+886 110220330"}){
username,
phone
}
}
// Error
But I only want to update the phone data this time, how can I skip other parameters?
Although it can solve the problem when I delete the Not null of all attributes in User type,
But it doesn't look like a good way and safe
// after
// Schema
type User {
username: String
password: String
phone: String
}
There is a preview feature that you can use by adding a header to your GraphQL query.
X-Schema-Preview: partial-update-mutation
Once you do this, you will have access to a new mutation called partialUpdateUser which will make all of the input values optional.
See the docs for more information: https://docs.fauna.com/fauna/current/api/graphql/previews/

Include relationship when querying node using Prisma generated wrapper

I am following the GraphQL Prisma Typescript example provided by Prisma and created a simple data model, generated the code for the Prisma client and resolvers, etc.
My data model includes the following nodes:
type User {
id: ID! #unique
displayName: String!
}
type SystemUserLogin {
id: ID! #unique
username: String! #unique
passwordEnvironmentVariable: String!
user: User!
}
I've seeded with a system user and user.
mutation {
systemUserLogin: createSystemUserLogin({
data: {
username: "SYSTEM",
passwordEnvironmentVariable: "SYSTEM_PASSWORD",
user: {
create: {
displayName: "System User"
}
}
}
})
}
I've created a sample mutation login:
login: async (_parent, { username, password }, ctx) => {
let user
const systemUser = await ctx.db.systemUserLogin({ username })
const valid = systemUser && systemUser.passwordEnvironmentVariable && process.env[systemUser.passwordEnvironmentVariable] &&(process.env[systemUser.passwordEnvironmentVariable] === password)
if (valid) {
user = systemUser.user // this is always undefined!
}
if (!valid || !user) {
throw new Error('Invalid Credentials')
}
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET)
return {
token,
user: ctx.db.user({ id: user.id }),
}
},
But no matter what I do, systemUser.user is ALWAYS undefined!
This makes sense - how would the client wrapper know how "deep" to recurse into the graph without me telling it?
But how can I tell it that I want to include the User relationship?
Edit: I tried the suggestion below to use prisma-client.
But none of my resolvers ever seem to get called...
export const SystemUserLogin: SystemUserLoginResolvers.Type<TypeMap> = {
id: parent => parent.id,
user: (parent, args, ctx: any) => {
console.log('resolving')
return ctx.db.systemUserLogin({id: parent.id}).user()
},
environmentVariable: parent => parent.environmentVariable,
systemUsername: parent => parent.systemUsername,
createdAt: parent => parent.createdAt,
updatedAt: parent => parent.updatedAt
};
And...
let identity: UserParent;
const systemUserLogins = await context.db.systemUserLogins({
where: {
systemUsername: user,
}
});
const systemUserLogin = (systemUserLogins) ? systemUserLogins[0] : null ;
if (systemUserLogin && systemUserLogin.environmentVariable && process.env[systemUserLogin.environmentVariable] && process.env[systemUserLogin.environmentVariable] === password) {
console.log('should login!')
identity = systemUserLogin.user; // still null
}
Edit 2: Here is the repository
https://github.com/jshin47/annotorious/tree/master/server
There are currently two ways to solve this problem:
Using the Prisma client as OP does at the moment
Using Prisma bindings as was suggested by #User97 in the accepted answer
You can learn more about the difference between Prisma client and Prisma bindings in this forum post.
As OP is currently using Prisma client, I'll use it for this answer as well!
Let's take a look at a statement OP made in the question:
This makes sense - how would the client wrapper know how "deep" to recurse into the graph without me telling it?
OP stated correctly that the Prisma client can't know how to deep to go into the graph and what relationships to fetch. In fact, unless explicitly told otherwise (e.g. using the $fragment API), the client will never fetch any relationships and will always only fetch the scalar values. From the Prisma docs:
Whenever a model is queried using the Prisma client, all scalar fields of that model are fetched. This is true no matter if a single object or a list of objects is queried.
So, how to properly resolve this situation? In fact, the solution is not to make changes to the way how the Prisma client is used, but to implement an additional GraphQL resolver function!
The point about resolvers is that they're fetching the data for specific fields in your schema. In OP's case, there currently is no resolver that would "resolve" the user relation that's defined on the SystemUserLogin type:
type SystemUserLogin {
id: ID! #unique
username: String! #unique
passwordEnvironmentVariable: String!
user: User! # GraphQL doesn't know how to resolve this
}
To resolve this situation, you need to implement a dedicated "type resolver" for it like so:
const resolvers = {
SystemUserLogin: {
user(parent, args, ctx) {
return ctx.db.systemUserLogin({id: parent.id}).user()
}
}
}
Full disclosure: I work at Prisma and we're working on adding better documentation and resources for that use case. Also check out this example where explicit resolvers for the author and posts relation fields are required for the same reason.
Hope that helps!
EDIT: We have also added a slightly more thorough explanation in the Prisma tutorial about Common resolver patterns.
Second parameter of prisma binding functions accept GraphQL query string. Changing following line from
const systemUser = await ctx.db.query.systemUserLogin({ username })
to
const systemUser = await ctx.db.query.systemUserLogin({ username }, `{id username user {id displayName}}`)
will give you the data of user.
Prisma binding will return only direct properties of model in case second parameter is not passed to it.

GraphQL Relay Connections Specification for Apollo

I'm trying to implement Relay Connections in Apollo, but I have some problems with the specification.
Schema looks like this:
type PageInfo {
startCursor: String!,
endCursor: String!,
hasNextPage: Boolean!,
hasPreviousPage: Boolean!
}
type User : Node {
id: ID!
firstname: String
lastname: String
}
type UserConnection : Connection {
pageInfo: PageInfo!
edges: [UserEdge!]!
}
type UserEdge : Edge {
cursor: String!
node: User!
}
extend type Query {
users(first: Int, after: ID, order: String): UserConnection
}
Let's say I have 100 users in db;
When I query with limit ex.users(first: 10), PageInfo.endCursor refers to the 10nth edge or the 100th?
More Questions:
1.Should I make another query to get hasPreviousPage, hasNextPage.
2.Are there any code examples(with resolvers) that have a similar structure?
3.Are there any graphql chats (gitter, irc)?
endCursor refers to the 10th, i.e. it's equal to the cursor field of the last edge in the result.
Generally-speaking, cursor-based pagination like this isn't bi-directional. So you can't know both hasNextPage and hasPreviousPage. But whichever you're fetching, you don't need to do issue it in another query, fetch as much data as you can in one query, that's the point!
There must be loads of examples of Relay's pagination around by now, I don't know any off the top of my head.
There's a slack team for Apollo and for GraphQL itself. apollographql.slack.com and graphql.slack.com

Resources