How can I connect to the user on Apollo Server? - graphql

I am a student practicing GraphQL. I want to manage the money list for each user.
I tried to create money list connected with the user, but the playground result value coming out null. I don't know where I wrong.
export default {
Mutation: {
createMoney: async (_, { title, amount, date }, { loggedInUser }) => {
await client.moneyList.create({
data: {
title,
amount,
date,
User: {
connect: {
id: loggedInUser.id,
},
},
},
});
return {
ok: true,
error,
};
},
},
};
this is my prisma studio T.T

It was an issue that came out because the resolver file name was wrong.

Related

Strapi update username from custom controller

I am trying to create a custom controller to update the user profile.
I created the routing file and the corresponding controller.
Routing file: server/src/api/profile/routes/profile.js
module.exports = {
routes: [
{
method: 'GET',
path: '/profile',
handler: 'profile.getProfile',
},
{
method: 'PUT',
path: '/profile',
handler: 'profile.updateProfile',
},
]
}
Controller: src/api/profile/controllers/profile.js
async updateProfile(ctx) {
try {
const { id } = ctx.state?.user;
const user = strapi.query('admin::user').update({
where: { id },
data: {
username: "testUsername"
}
})
ctx.body = "User updated"
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
The above code returns "User updated", but the username does not update. I am executing the PUT call with a correct Bearer authorisation token and the user permissions for that user are set to enable "updateProfile".
Oddly enough, the same code, when changed to update a different API item, works perfectly fine:
async updateArticle(ctx) {
try {
const { id } = ctx.state?.user;
const article = strapi.query('api::article.article').update({
where: { author: id },
data: {
title: "New title"
}
})
ctx.body = article
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
I am also confused by different syntaxes appearing in the official Strapi documentation, for example some docs mention:
strapi.query('admin::user').update({ id }, data)
But in other places in the documentation its:
strapi.plugins['users-permissions'].services.user.update({ id });
And then elsewhere:
strapi.query('user', 'users-permissions').update(params, values);
Another question is: do I need to sanitise the input / output in any way? If yes, how? Importing sanitizeEntity from "Strapi-utils" doesn't work, but it's mentioned in several places on the internet.
Additionally, I cannot find a list of all ctx properties. Where can I read what is the difference between ctx.body and ctx.send?
The lack of good documentation is really hindering my development. Any help with this will be greatly appreciated.

Apollo cache redirect for field with no arguments

I have a login mutation tha looks similiar to this:
mutation login($password: String!, $email: String) {
login(password: $password, email: $email) {
jwt
user {
account {
id
email
}
}
}
}
On the other hand, I have a query for getting the account details. The backend verifies which user it is by means of the JWT token that is send with the request, so no need for sending the account id as an argument.
query GetUser {
user {
account {
id
email
}
}
}
The issue I am facing is now: Apollo is making a network request every time as GetUser has no argument. I would prever to query from cache first. So I thought, I could redirect as described here.
First, I was facing the issue that user field does not return an id directly so I have defined a type policy as such:
const typePolicies: TypePolicies = {
User: {
keyFields: ["account", ["id"]],
},
}
So regarding the redirect I have add the following to the type policy:
const typePolicies: TypePolicies = {
User: {
keyFields: ["account", ["id"]],
},
Query: {
fields: {
user(_, { toReference }) {
return toReference({
__typename: "User",
account: {
id: "1234",
},
})
},
},
},
}
This works, however there is a fixed id of course. Is there any way to solve this issue by always redirecting to the user object that was queried during login?
Or is it better to add the id as argument to the GetUser query?
I have solve this by means of the readField function:
Query: {
fields: {
user(_, { toReference, readField }) {
return readField({
fieldName: "user",
from: toReference({
__typename: "LoginMutationResponse",
errors: null,
}),
})
},
},
},
What happens if the reference cannot be found? It seems like apollo is not making a request then. Is that right? How can this be solved?
I have noticed this in my tests as the login query is not executed.

Relay Modern subscriptions: returning record mutiple times incrementally

I am working on a very simple app which contains Posts and comments on those Posts. I have got
1) comment_mutation.js ( Server side - i am publishing the saved comment using pubsub). I have removed unnecessary code
CommentCreateMutation: mutationWithClientMutationId({
name: 'CommentCreate',
inputFields: {
},
outputFields: {
commentEdge: {
type: GraphQLCommentEdge,
resolve: async ({ comment, postId }) => {
// Publishing change to the COMMENT_ADDED subscription.
await pubsub.publish(COMMENT_SUB.COMMENT_ADDED, { commentAdded: commentEdge, postId });
return commentEdge;
},
},
},
mutateAndGetPayload: () => {
// Save comment in DB
},
}),
2) CommentSubscription.js (server side) - getting the subscription and filtering it based on postId.
commentAdded: {
type: GraphQLCommentEdge,
args: {
postId: { type: new GraphQLNonNull(GraphQLID) },
...connectionArgs,
},
subscribe:
withFilter(
() => pubsub.asyncIterator(COMMENT_SUB.COMMENT_ADDED),
(payload, args) => payload.postId === args.postId
),
},
Server side is working very good. Whenever any comment is created it publishes the result to subscrition. Subscription catches it and displays the results.
Now the client side:
1) CommentMutaton.js - client side. Whenever user himself creates a comment on client side (react native) it is updating the store very well.
const mutation = graphql`
mutation CommentCreateMutation($input:CommentCreateInput!) {
commentCreate(input: $input) {
commentEdge {
__typename
cursor
node {
id
_id
text
}
}
}
}
`;
const commit = (environment, user, post, text, onCompleted, onError) => commitMutation(
environment, {
mutation,
variables: {
input: { userId: user._id, userName: user.userName, postId: post._id, text },
},
updater: (store) => {
// Update the store
},
optimisticUpdater: (store) => {
// Update the store optimistically
},
onCompleted,
onError,
},
);
2) CommentSubscription.js (client side)
const subscription = graphql`
subscription CommentAddedSubscription($input: ID!) {
commentAdded(postId: $input) {
__typename
cursor
node {
id
_id
text
likes
dislikes
}
}
}
`;
const commit = (environment, post, onCompleted, onError, onNext) => requestSubscription(
environment,
{
subscription,
variables: {
input: post._id,
},
updater: (store) => {
const newEdge = store.getRootField('commentAdded');
const postProxy = store.get(post.id);
const conn = ConnectionHandler.getConnection(
postProxy,
'CommentListContainer_comments',
);
ConnectionHandler.insertEdgeAfter(conn, newEdge);
},
onCompleted,
onError,
onNext,
}
);
export default { commit };
Problem is on client side. Whenever i create a comment on server side I can see the first comment rightly on client side. When i send another comment on server side i can see 2 same comments on client side. When i send comment third time i can see 3 same comments on client side. Does it mean:
every time comment_mutation.js (on server side) runs it create a new
subscription besides existing one. That is the only logical
explanation i can think of.
I commented out the updater function of CommentMutation.js (on client side) but still seeing this error. any help will be much appreciated.

Each Node of data perform separate database request

below is the GraphQLObject Fields
userId: {
type: GraphQLID,
resolve: obj => {
console.log(obj._id);
return obj._id;
}
},
email: { type: GraphQLString },
password: { type: GraphQLString },
firstName: { type: GraphQLString },
lastName: { type: GraphQLString },
mine server sents multiple request equally as of mine documents, here it will send 5 different request.
how can i optimize these request get all data in one request
589800cf39b58b29c4de90dd
--------------------------------
58980673e7c9a733009091d1
--------------------------------
58985339651c4a266848be42
--------------------------------
589aac5f884b062b979389bc
--------------------------------
589aad9d24989c2d50f2a25a
In such a case you could create a query method which would accept an array as a parameter, which would be an array of IDs in this case.
getUsers: {
type: new GraphQLList(User),
args: {
ids: {
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID)))
}
},
resolve: (root, args, context) => {
let query = 'SELECT * FROM users WHERE id = ANY($1)';
return pgClient.query(query, [args.ids], (err, result) => {
// here you would access all returned rows in the result object
console.log(result);
});
}
}
The query variable would differ depending on what database you are using. In this example I have used the node-postgres module for PostgreSQL. However, the concept is the same - use array of ids to perform single query returning all users.
And then you could call that query:
query getUsers($ids: [ID!]!) {
getUsers(ids: $ids){
id
email
...
}
}
// and the variables below
{
ids: ['id#1', 'id#2', 'id#3']
}
This is a job for Dataloader, a library from Facebook specifically for batching together queries like this:
https://github.com/facebook/dataloader

Relayjs Graphql user authentication

Is it possible to authenticate users with different roles solely trough a graphql server in combination with relay & react?
I looked around, and couldn't find much info about this topic.
In my current setup, the login features with different roles, are still going trough a traditional REST API... ('secured' with json web tokens).
I did it in one of my app, basically you just need a User Interface, this one return null on the first root query if nobody is logged in, and you can then update it with a login mutation passing in the credentials.
The main problem is to get cookies or session inside the post relay request since it does'nt handle the cookie field in the request.
Here is my client mutation:
export default class LoginMutation extends Relay.Mutation {
static fragments = {
user: () => Relay.QL`
fragment on User {
id,
mail
}
`,
};
getMutation() {
return Relay.QL`mutation{Login}`;
}
getVariables() {
return {
mail: this.props.credentials.pseudo,
password: this.props.credentials.password,
};
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
user: this.props.user.id,
}
}];
}
getOptimisticResponse() {
return {
mail: this.props.credentials.pseudo,
};
}
getFatQuery() {
return Relay.QL`
fragment on LoginPayload {
user {
userID,
mail
}
}
`;
}
}
and here is my schema side mutation
var LoginMutation = mutationWithClientMutationId({
name: 'Login',
inputFields: {
mail: {
type: new GraphQLNonNull(GraphQLString)
},
password: {
type: new GraphQLNonNull(GraphQLString)
}
},
outputFields: {
user: {
type: GraphQLUser,
resolve: (newUser) => newUser
}
},
mutateAndGetPayload: (credentials, {
rootValue
}) => co(function*() {
var newUser = yield getUserByCredentials(credentials, rootValue);
console.log('schema:loginmutation');
delete newUser.id;
return newUser;
})
});
to keep my users logged through page refresh I send my own request and fill it with a cookie field... This is for now the only way to make it work...

Resources