Intention:
trying to query from apollo client based on dynamic id. Have successfully checked in server provided interface which is working... and trying to do same from the client.
From the doc it looks like i need to use variables which i did.
Problem:
query using variables looks good but i am getting undefined in client.
Query which is working in graphql API:
query abc {
getCategoryProduct(id:"NzI1NDc1MTM1") {
id
title
description
favorited
published
price_per_day
price_per_week
price_per_month
price_per_weekend
picture
pictures {
id
url
}
createdAt
updatedAt
}
}
Problematic code in client
const GETDETAILS = gql`
query abc($id: String!) {
getCategoryProduct(id: $id) {
id
title
description
favorited
published
price_per_day
price_per_week
price_per_month
price_per_weekend
picture
pictures {
id
url
}
createdAt
updatedAt
}
}
`;
const DetailScreen = () => {
const { loading, error, data } = useQuery(GETDETAILS, {
variables: { id: "NzI1NDc1MTM1" },
});
useEffect(() => {
if (loading == false) {
console.log("=====data=====", data); // DATA IS EMPTY DO NOT NOT WHY??
}
}, [data]);
}
I was getting the same bug, and it looked like I had tried everything to solve it, including following the instruction in useQuery returns undefined, But returns data on gql playground, but it still didn't work.
Later, I change the variable name—in your case $id—to something else, so it's different from the name in typeDefs (getCategoryProduct(id:ID)), and it now works for me 🤨🙏.
Related
I use react native with graphQL.
I want to be able to upload multiple photos in one post in my app.
What I made is this.
As you can see, file (where uploaded files is stored) has one string.
This one row means one post in my app.
And my prisma schema is also set as String in file column.
model Photo {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
*file String
}
But I want to upload multiple photos in one post which means one row in DB.
So my idea is to make my DB looks like below.
File column is made to be Array of String in one line.
In order to make this, I make my schema's file String[].
model Photo {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
*file String[]
}
And I send Upload data from Front as below using map.
const onValid = async ({ caption }) => {
const file = await Promise.all(
selectPhoto.map(
(sp, index) =>
new ReactNativeFile({
uri: sp,
name: `${index}.jpg`,
type: "image/jpeg",
})
)
);
await uploadPhotoMutation({
variables: {
caption,
file,
},
});
};
Then below Array data goes to backend.
Array [
ReactNativeFile {
"name": "0.jpg",
"type": "image/jpeg",
"uri": "file:///storage/emulated/0/DCIM/Screenshots/Screenshot_20220223-011625_KakaoTalk.jpg",
},
ReactNativeFile {
"name": "1.jpg",
"type": "image/jpeg",
"uri": "file:///storage/emulated/0/DCIM/Camera/20220222_161411.jpg",
},
]
Then I upload this data to aws in order to get Url.
export const uploadSingleFileToS3 = async (file, userId, folderName) => {
const { filename, createReadStream } = await file;
const readStream = createReadStream();
const objectName = `${folderName}/${userId}-${Date.now()}-${filename}`;
const { Location } = new AWS.S3()
.upload({
Bucket: "chungchunonuploads",
Key: objectName,
ACL: "public-read",
Body: readStream,
})
.promise();
return Location;
};
export const uploadFileToS3 = async (filesToUpload, userId, folderName) => {
const uploadPromises = filesToUpload.map((file) => {
uploadSingleFileToS3(file, userId, folderName);
});
return Promise.all(uploadPromises);
};
However, I can't even check if my code is right or not.
Because I keep facing this error.
I think it's not just Network issue.
Because all other functions of app is working well.
So I guess some error of backend cause this.
even I can't figure out any little clue from this error message ^^;
I need many opinions from many people.
If you have any advise please tell me..
We are using apollo-client in a react project. We made a cursor level on top of any list queries. For example:
query MediaList($mediaIds: [ID!], $type: [MediaType!], $userId: ID!) {
user {
id
medias_cursor(all_medias: true, active: true, ids: $mediaIds) {
medias {
id
type
category
name
}
}
}
}
Now for different MediaList query, the Media Objects might already exist in cache but we can not use it to skip network query. For example:
After we query medias_cursor({"all_medias":true,"active":true,"ids":["361","362","363"]}),
we've already got the three Media objects here - (Media:361, Media:362, Media:363).
So when we try to query medias_cursor({"all_medias":true,"active":true,"ids":["361","363"]}, we should have everything we need in the cache already. But right now, the apollo default behavior will just pass the cache and hit the network.
We tried to add a cacheRedirects config to solve this problem like this:
const cache = new InMemoryCache({
cacheRedirects: {
User: {
medias_cursor: (_, { ids }, { getCacheKey }) => {
if (!ids) return undefined
return {
medias: map(ids, id => {
return getCacheKey({ __typename: 'Media', id: id })
})
}
},
},
},
})
We are expecting that the cacheRedirects would help us to use the cache when it's available, but now it will skip the cache anyway.
Most of the information out there about Apollo Client and GraphQL queries is about fetching data and immediately rendering something.
What about the common use case where I want to fetch data to, let say, update the state in which I clearly don't need to render JSX, I just want to run Javascript code.
Use the following code snippet as an example
onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
console.info('variables ready', variables)
return (
<Query query={RESOLVE_REF} variables={variables}>
{ ({ data, error }) => {
console.info('data response', data)
console.info('error response', error)
return data
}}
</Query>
)
}
Apollo forces me to use the Query component just to perform a query, even when I don't want to render anything. Also those console.info never log anything, but the variables ready text does appear.
I have found that the documentation is pretty clear on using the Query component, but obscure on every option which is different. I feel I'm missing something.
I'm also concerned about how Apollo doesn't seems respect the separation of responsibilities, apparently merging both data and presentation into a single responsibility (as is clear with the Query component), which in my current understanding is quite silly, but most likely I'm fucking things up.
Any insight is appreciated.
As long as you've configured and included an ApolloProvider at the top of your component tree, you can get your query instance using either the withApollo HOC, or the ApolloConsumer:
const MyComponent = ({ client }) => {
// use it!
}
withApollo(MyComponent)
<ApolloConsumer>
{client => (
// use it!
)}
</ApolloConsumer>
You can then use any of the methods that are available to the client instance, including query and mutation, both of which return a Promise that resolves to an ApolloQueryResult object that includes data and errors. The full documentation for the client's API can be found here. Your code would then look something like:
async onRefChange (formValues) {
let { project, ref } = formValues
let projectFound = find(this.state.projects, (o) => { return o.id === project.value } )
let variables = {
slug: projectFound.slug, ref: parseInt(ref)
}
try {
const { data } = await this.props.client(RESOLVE_REF, { variables })
} catch (e) {
// Handle errors
}
}
It seems like I have my server set up according to the Apollo docs at http://dev.apollodata.com/tools/apollo-server/setup.html. In my server/main.js file:
//SET UP APOLLO INCLUDING APOLLO PUBSUB
const executableSchema = makeExecutableSchema({
typeDefs: Schema,
resolvers: Resolvers,
connectors: Connectors,
logger: console,
});
const GRAPHQL_PORT = 8080;
const graphQLServer = express();
// `context` must be an object and can't be undefined when using connectors
graphQLServer.use('/graphql', bodyParser.json(), apolloExpress({
schema: executableSchema,
context: {}, //at least(!) an empty object
}));
graphQLServer.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}));
graphQLServer.listen(GRAPHQL_PORT, () => console.log(
`GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}/graphql`
));
//SET UP APOLLO INCLUDING APOLLO PUBSUB
It prints out "GraphQL Server is now running on http://localhost:8080/graphql" to the terminal log indicating that the server was successfully initialized.
But at the top of my main_layout component, when I run this code:
import { Client } from 'subscriptions-transport-ws';
const wsClient = new Client('ws://localhost:8080');
...I get this console message:
WebSocket connection to 'ws://localhost:8080/' failed: Connection closed before receiving a handshake response
What am I missing?
You need to create a dedicated websocket server. It will run on a different port and the code to set it up is provided on the subscriptions-transport-ws package.
Take a look on the following code from GitHunt-API example:
https://github.com/apollostack/GitHunt-API/blob/master/api/index.js#L101-L134
Also you would see that this code is dependent on a class called SubscriptionManager. It is a class from a package called graphql-subscriptions also by the apollo team, and you can find an example of how to use it here:
https://github.com/apollostack/GitHunt-API/blob/master/api/subscriptions.js
TL;DR: You can use graphql-up to quickly get a GraphQL server with subscriptions support up and ready. Here's a more detailed tutorial on using this in combination with Apollo and the websocket client subscriptions-transport-ws.
Obtain a GraphQL Server with one click
Let's say you want to build a Twitter clone based on this GraphQL Schema in IDL syntax:
type Tweet {
id: ID!
title: String!
author: User! #relation(name: "Tweets")
}
type User {
id: ID!
name: String!
tweets: [Tweet!]! #relation(name: "Tweets")
}
Click this button to receive your own GraphQL API and then open the Playground, where you can add some tweets, query all tweets and also test out subscriptions.
Simple to use API
First, let's create a user that will be the author for all coming tweets. Run this mutation in the Playground:
mutation createUser {
createUser(name: "Tweety") {
id # copy this id for future mutations!
}
}
Here's how you query all tweets and their authors stored at your GraphQL server:
query allTweets {
allTweets {
id
title
createdAt
author {
id
name
}
}
}
Subscription support using websockets
Let's now subscribe to new tweets from "Tweety". This is the syntax:
subscription createdTweets {
Message(filter: {
mutation_in: [CREATED]
node: {
author: {
name: "Tweety"
}
}
}) {
node {
id
text
createdAt
sentBy {
id
name
}
}
}
}
Now create a new tab in the Playground and create a new Tweet:
mutation createTweet {
createTweet(
title: "#GraphQL Subscriptions are awesome!"
authorId: "<id-from-above>"
) {
id
}
}
You should see a new event popping up in your other tab where you subscribed before.
Here is a demo about using Apollo GraphQL, React & Hapi: https://github.com/evolastech/todo-react. It's less overwhelmed than GitHunt-React & GitHunt-API
Seems like you aren't actually making the websocket server. use SubscriptionServer. Keep in mind that it is absolutely NOT true that you have to have a dedicated websocket port (I thought this once too) as davidyaha says. I have both my normal queries and subs on the same port.
import { createServer } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';
import { schema } from './my-schema';
// All your graphQLServer.use() etc setup goes here, MINUS the graphQLServer.listen(),
// you'll do that with websocketServer:
// Create WebSocket listener server
const websocketServer = createServer(graphQLServer);
// Bind it to port and start listening
websocketServer.listen(3000, () => console.log(
`Server is now running on http://localhost:3000`
));
const subscriptionServer = SubscriptionServer.create(
{
schema,
execute,
subscribe,
},
{
server: websocketServer,
path: '/subscriptions',
},
);
I have written a (very) simple RESTFul Web service to retrieve data from MongoDB using Node, Express and Mongoose.
On the server side, I have this code:
router.route('/products').post(function(req,res){
var product = new Product(req.body);
product.save(function(err){
if(err)
res.send(err);
res.send({message:'Product Added'});
});
When I submit a request from my Ember client, the req.body contains something like the following:
{ attributes:
{ category: 1,
name: 'y',
price: 1,
active: false,
notes: null } }
The attribute names are exactly the same as my mongoose schema. I get no error but the document created in MongoDB is empty (just get the _id and __v fields).
What am I doing wrong. Should I convert the req.body further into ???
A couple things that will help debug:
1) From a quick glance (I haven't used mongoose before) it looks like call back function passed to save takes two arguments.
2) I don't know if your code got cut off, but the sample above was missing a matching });
3) I made the function short circuit itself on error, so you will not see 'Product added' unless that is truly the case.
Try these fixes.
router.route('/products').post(function(req,res){
var product = new Product(req.body);
product.save(function(err, product){
if(err){
return res.send(err);
}
return res.send({message:'Product Added'});
});
});
The issue was related to my lack of familiarity with Ember and Node+Express. The data received in the server is slightly different from what I had first indicated: (first line was missing)
{ product:
{ attributes:
{ category: ... } } }
On the server side I can access my data using req.body.product.attributes (instead of req.body):
router.route('/products').post(function(req,res){
var product = new Product(req.body.product.attributes);
product.save(function(err){
if(err)
res.send(err);
res.send({message:'Product Added'});
});