mocking service and running parameterised query in react apollo graphql - apollo

I’m trying to mock the service as in the docs example http://dev.apollodata.com/tools/graphql-tools/mocking.html#Default-mock-example
I’m exporting my schema with the standard introspection query, building a schema object and using this when trying to run a query as mentioned in the docs.
import * as introspectionResult from "../../graphql/schema.json"
const GRAPHQL_QUERY = `
query eventsFeatured(
$type: String!,
$entity_type: String,
$market__role: String,
$outcome__odds_rank_unique: Int,
$limit: Int
) {
featured(
type: $type,
entity_type: $entity_type,
market__role: $market__role,
outcome__odds_rank_unique: $outcome__odds_rank_unique,
limit: $limit
) {
...FeaturedFragment
media {
description
extralarge
large
medium
preview
__typename
}
event {
...EventFragment
market {
...MarketFragment
outcome {
...OutcomeFragment
media_logo {
preview
small
__typename
}
bookie {
...BookieFragment
__typename
}
__typename
}
__typename
}
article {
...ArticleFragment
__typename
}
category3 {
...AllCategoriesFragment
__typename
}
__typename
}
offer {
id
name
conditions
deeplink
domain {
...EventFragment
__typename
}
__typename
}
__typename
}
}
fragment EventFragment on Event {
id
name
canonicalised_name
display_name
date
date_human
date_short
type
__typename
}
fragment MarketFragment on Market {
id
name
display_name
canonicalised_name
type
role {
name
__typename
}
__typename
}
fragment OutcomeFragment on Outcome {
id
name
display_name
canonicalised_name
odds
odds_decimal
odds_rank
deeplink
home_or_away
type
__typename
}
fragment BookieFragment on Bookie {
name
__typename
}
fragment ArticleFragment on BaseArticle {
title
body
excerpt
author
date_no_time
date_stamp
date_human
date_short
date
__typename
}
fragment AllCategoriesFragment on Category3 {
...Category3Fragment
category2 {
...Category2Fragment
category1 {
...Category1Fragment
__typename
}
__typename
}
__typename
}
fragment Category1Fragment on Category1 {
name
canonicalised_name
__typename
}
fragment Category2Fragment on Category2 {
name
canonicalised_name
__typename
}
fragment Category3Fragment on Category3 {
name
canonicalised_name
__typename
}
fragment FeaturedFragment on Featured {
id
type
display_order
__typename
}
`
// this builds our mock apollo schema
const schema = buildClientSchema(introspectionResult)
addMockFunctionsToSchema({ schema })
// execute the supplied apollo query
graphql(schema, GRAPHQL_QUERY, {
options: (props) => ({
variables: {
type: "IMAGEGRIDHOMEPAGE"
},
})})
.then(graphqlResult => {
.... etc
```
However, I am seeing the following error:
```
{ errors:
[ GraphQLError {
message: 'Variable "$type" of required type "String!" was not provided.',
locations: [Object],
path: undefined } ] }
It seems that the graphql function does not recognise the standard options object, at least not the variables definitions when used in this context of having a schema object as first arg. I have tried digging into the source here but my typescript ain’t the best - https://github.com/apollographql/react-apollo/blob/master/src/graphql.tsx

user error - as can be seen in the docs, the graphql function here is from the base graphql library, not indeed the apollo-react one.

Related

AWS Amplify searchable field by geo distance (location)

I'm developing an application that will allow users to search for other users by ordering them by geo location.
According to the information found on the internet, to do this on amplify I have to perform the following steps:
create an elastic search mapping, indicating the type 'geo_point' on the lastPosition attribute (I would like to define it once in the project file and not at each build in the elastic search console)
create a custom query and a custom vtl resolver and set the sort by lastPosition (of type '_geo_distance') in it.
But I don't understand how to do these 2 steps, so I need some suggestion :(.
// my schema.graphql
type User
#model
#auth(rules: [{allow: owner}, {allow: private, operations: [read]}])
#searchable {
// ...other fields
id: ID!
lastPosition: Position
positionUpdatedAt: AWSDateTime
}
type Position {
lat: Float!
lon: Float!
}
// my custom searchUsers query in graphql/custom_queries.ts
export const searchUsersNearby = /* GraphQL */ `
query SearchUsersNearby(
$filter: SearchableUserFilterInput
$sort: [SearchableUserSortInput]
$location: PositionInput // current user position that i need to use in vtl resolver to sort users by distance,
$limit: Int
$nextToken: String
$from: Int
$aggregates: [SearchableUserAggregationInput]
) {
searchUsersNearby(
filter: $filter
sort: $sort
limit: $limit
nextToken: $nextToken
from: $from
aggregates: $aggregates
) {
items {
id
// ...other fields
lastPosition
positionUpdatedAt
createdAt
updatedAt
owner
}
nextToken
total
aggregateItems {
name
result {
... on SearchableAggregateScalarResult {
value
}
... on SearchableAggregateBucketResult {
buckets {
key
doc_count
}
}
}
}
}
}
`;
I use amplify v8.1.0 with transformer v2.
Thank you guys

Call a GraphQL mutation with #hasMany

I'm using DynamoDB of AWS and i'm trying to insert item with relations (hasMany).
When i'm trying to create item with relations it returns error:
"The variables input contains a field name 'contacts' that is not defined for input object type 'CreateBookingInput' "
Here is the definition of my models where you can see the relations
type Traineeship #model #auth(rules:[{allow:public}]){
traineeshipID: ID! #primaryKey
}
type Booking #model #auth(rules:[{allow:public}]){
bookingID: ID! #primaryKey
traineeship: Traineeship #hasOne
contacts:[BookingContact] #hasMany
}
type BookingContact #model #auth(rules:[{allow:public}]){
bookingContactID: ID! #primaryKey
}
And here is the mutation use to insert
export const createBooking = /* GraphQL */ `
mutation CreateBooking(
$input: CreateBookingInput!
$condition: ModelBookingConditionInput
) {
createBooking(input: $input, condition: $condition) {
bookingID
traineeship {
traineeshipID
title
description
fulldesc
place
from
to
price
img
horaire
garderie
age
full
createdAt
updatedAt
}
email
tel
firstname
name
birthdate
address
zipcode
city
Remark
submitedDate
validatedDate
validated
contacts {
items {
bookingContactID
firstname
name
phone
parent
createdAt
updatedAt
bookingContactsId
}
nextToken
}
createdAt
updatedAt
bookingTraineeshipId
}
}
`;
As you can see contacts is define in createBooking. If i remove this property it works correctly.
THanks for your help

Apollo GraphQL client doesn't return cached nested types in a query

I'm performing a query to get PowerMeter details in which contains another type inside called Project. I write the query this way:
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
When I perform the query for the first time, project is successfully returned. The problem is that when I perform subsequent queries with the same parameters and default fetchPolicy (cache-first), project isn't returned anymore.
How may I solve this problem?
Also, I call readFragment to check how powerMeter is saved in the cache and the response shows that powerMeter has project saved.
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
Power Meter returned first time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
}
Fragment after calling power meter first time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Power Meter returned second time
{
"powerMeter":{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"__typename":"PowerMeter"
}
}
Fragment after calling power meter second time
{
"id":"7168adb4-4198-443e-ab76-db0725be2b18",
"name":"asd123123",
"registry":"as23",
"project":{
"id":"41d8e71b-d1e9-41af-af96-5b4ae9e492c1",
"name":"ProjectName",
"__typename":"Project"
},
"__typename":"PowerMeter"
}
Edit 1: Fetching Query
The code below is how I'm fetching data. I'm using useApolloClient and not a query hook because I'm using AWS AppSync and it doesn't support query hook yet.
import { useApolloClient } from '#apollo/react-hooks';
import gql from 'graphql-tag';
import { useEffect, useState } from 'react';
export const getPowerMeterQuery = gql`
query getPowerMeter($powerMeterId: ID!) {
powerMeter: powerMeter(powerMeterId: $powerMeterId) {
id
name
registry
project {
id
name
}
}
}
`;
export const useGetPowerMeter = (powerMeterId?: string) => {
const client = useApolloClient();
const [state, setState] = useState<{
loading: boolean;
powerMeter?: PowerMeter;
error?: string;
}>({
loading: true,
});
useEffect(() => {
if (!powerMeterId) {
return setState({ loading: false });
}
client
.query<GetPowerMeterQueryResponse, GetPowerMeterQueryVariables>({
query: getPowerMeterQuery,
variables: {
powerMeterId,
},
})
.then(({ data, errors }) => {
if (errors) {
setState({ loading: false, error: errors[0].message });
}
console.log(JSON.stringify(data));
const frag = client.readFragment({
fragment: gql`
fragment P on PowerMeter {
id
name
registry
project {
id
name
}
}
`,
id: 'PowerMeter:' + powerMeterId,
});
console.log(JSON.stringify(frag));
setState({
loading: false,
powerMeter: data.powerMeter,
});
})
.catch(err => setState({ loading: false, error: err.message }));
}, [powerMeterId]);
return state;
};
Edit 2: Fetching Policy Details
When I use fetchPolice equals cache-first or network-only, the error persists. When I use no-cache, I don't get the error.
I think this might have been the solution:
https://github.com/apollographql/apollo-client/issues/7050
Probably way too late, but it could help people coming to this issue in the future.
When using apollo client's InMemoryCache it seems you need to provide a list of possible types so the fragment matching can be done correctly when using the InMemoryCache.
You can do that manually when having few union types and a pretty stable API which doesn't change very often.
Or you automatically generate these types into a json file, which you can use directly in the InMemoryCache's possibleTypes config directly.
Visit this link to the official docs to find out how to do it.
Cheers.

How do I add union type in Apollo graphql

I created this question in case anyone was curious on how to add union / Polymorphic types in Apollo. Hopefully this will make it easier for them.
In this example I wanted the response to either be a Worksheet or ApiError
// typedefs.js
export default [`
schema {
query: Query
}
type Query {
worksheet(id: String!): Worksheet | Error
}
type Worksheet {
id: String!
name String
}
type ApiError {
code: String!
message: String!
}
`];
// resolvers.js
export default {
Query: {
worksheet(_, args, { loaders }) {
return loaders.worksheet.get(args.id).catch(() => {
// ApiError
return {
code: '1',
message: 'test'
}
});
}
}
};
// Express Server
import { graphqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
import typeDefs from './typedefs';
import resolvers from './resolvers';
...
app.post(
'/graphql',
graphqlExpress(req => ({
makeExecutableSchema({ typeDefs, resolvers }),
context: mkRequestContext(req.ctx, req.log),
formatError: formatGraphQLError(req.ctx, req.log)
}))
);
In GraphQL to add a union type in the typedefs you have to define the union
i.e union WorksheetOrError = Worksheet | ApiError
// typedefs.js
export default [
`
schema {
query: Query
}
type Query {
worksheet(id: String!): WorksheetOrError
}
union WorksheetOrError = Worksheet | ApiError
type Worksheet {
id: String!
name String
}
type ApiError {
code: String!
message: String!
}
`];
In the resolvers you have to define a resolver for the union type that has the property __resolveType. This will help tell the GraphQL executor which type the result is.
// resolvers.js
export default {
Query: {
worksheet() {
...
}
},
WorksheetOrError: {
__resolveType(obj) {
if (obj.id) {
return 'Worksheet';
}
if (obj.code) {
return 'ApiError';
}
return null;
}
},
};
To create a GraphQL Query in Apollo Client
// Your application code.
// This is my Worksheet Query in my React code.
const WorksheetQuery = gql`
query GetWorksheet($worksheetId: String!) {
worksheet(id: $worksheetId) {
... on Worksheet {
id
name
}
... on ApiError {
code
message
}
}
}
Now you can check the __typename to check what type is in the response.
Note: For those who are wondering why I'm not using GraphQL errors. It's because Apollo doesn't seem to handle errors well when it encounters a graphQL error. So for a work around I'm trying to return a custom ApiError in my response.
There a few reasons why using a union with an error type is nice.
Currently if you wanted a partial response with GraphQLError. Apollo does not cache the errors so if you wanted to re-use the cached response later you wouldn't have the complete response since the errors are removed. (Now you can't display the proper UI with errors)
Getting GraphQLError back in Apollo would return a flat list of errors with the path to where the error is in the data. So you would need to verify that which part of your schema did the error occur in. However if you follow the instructions above you would have the error within the schema already. That way you already know which part of the schema the error happened.

How to query all entities from InMemoryCache?

I am trying to query all entities from my apollo local cache (InMemoryCache) but without success.
Here is how I proceed.
query EntityList($limit: Int!, $offset: Int!) {
entities(
limit: $limit,
offset: $offset
) {
__typename
EntityId
}
}
With this query : no problem.
But later, I would like to query all entities from the cache and without any params.
query LocalEntityList {
entities {
EntityId
}
}
This simple code triggers an error
Can't find field entities on object (ROOT_QUERY)....
From the documentation site, I understand that I need to use cacheResolvers options on the InMemoryCache object.
But there is no example without passing an id as argument.
I think what you want is a #connection directive. This will allow you to cache your entities query without the limit/offset arguments.
query EntityList($limit: Int!, $offset: Int!) {
entities (
limit: $limit,
offset: $offset
) #connection(key: "EntityList_entities") {
__typename
EntityId
}
}
Making a successive call with the same connection should return all the entities from that key.
query LocalEntityList {
entities #connection(key: "EntityList_entities") {
EntityId
}
}