Getting error when using ra-graphql-simple with react-admin - apollo

My react-admin imp seems to be able to connect to a strapi api I've spun up and configured with the graphql plugin, but I get an error saying my client essentially doesn't know one of my strapi collections exists:
Unknown resource 'products'. Make sure it has been declared on your server side schema. Known resources are
That's the full message that appears in a flash. No actual list of resources.
My App.js is the following:
class App extends Component {
constructor() {
super();
this.state = { dataProvider: null };
}
componentDidMount() {
buildGraphQLProvider({
clientOptions: {
uri: 'http://localhost:1337/graphql',
},
}).then(dataProvider => {
this.setState({ dataProvider })
}).catch((err) => {
console.error(err);
});
}
render() {
const { dataProvider } = this.state;
if (!dataProvider) {
return <div>Loading</div>;
}
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
i18nProvider={i18nProvider}
title="GridPlus"
locale="en"
customRoutes={[
<Route
exact
path="/custom"
component={CustomRouteNoLayout}
noLayout
/>,
<Route exact path="/custom2" component={CustomRouteLayout} />,
]}>
<Resource name="products" {...products} />
</Admin>
);
}
}
export default App;
Any ideas why admin-client isn't able to see my products collection?
Thanks.

ra-data-graphql-simple send an introspection query to your graphql backend when it starts, gathering all possible resources matching the gql dialect it understands.
A dialect is a way to name the queries and mutations for each resource. For a resource to be detected, your schema must have the expected shape defined in the documentation.
Most of the time, you'll have to implement your own dialect using ra-data-graphql.

Related

AWS Amplify post request fails with "status code 403 at node_modules/axios"

I configured and initialized AWS Amplify for my ReactNative/Expo app and added a REST Api. Im new to AWS in general, but im assuming that once I add the API, my project is populated with amplify/backend folders and files and is ready for consumption.
So i tried to create a simple post request to create an item in my DynamoDB table with
import { Amplify, API } from "aws-amplify";
import awsconfig from "./src/aws-exports";
Amplify.configure(awsconfig);
const enterData = async () => {
API.post("API", "/", {
body: {
dateID: "testing",
},
headers: {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`
}
})
.then((result) => {
// console.log(JSON.parse(result));
})
.catch((err) => {
console.log(err);
});
};
const signIn = async () => {
Auth.signIn('test#test.com', 'testpassword')
.then((data) => {
console.log(data)
enterData() //enterData is attempted after signin is confirmed.
})
.catch((err) => {
console.log(err)
})
}
signIn()
I did not touch anything else in my project folder besides including the above in my App.tsx because im unsure if i need to and where. I got a 403 error code and it "points" to the axios package but im not sure if issue is related to aws integration.
I configured the REST Api with restricted access where Authenticated users are allowed to CRUD, and guests are allowed to Read. How could I even check if I am considered an "Authorized User" .
Yes, AWS Amplify API category uses Axios under the hood so axios is related to your problem.
Probably you get 403 because you didn't authorized, for Rest API's you need to set authorization headers,
I don't know how is your config but you can take help from this page. Please review the "Define Authorization Rules" section under the API(REST) section.
https://docs.amplify.aws/lib/restapi/authz/q/platform/js/#customizing-http-request-headers
To check authorization methods, you can use "Auth" class like that also you can see auth class usage in the above link.
import { Amplify, API, Auth } from "aws-amplify";
https://aws-amplify.github.io/amplify-js/api/classes/authclass.html

How to initialize ApolloClient in SvelteKit to work on both SSR and client side

I tried but didn't work. Got an error: Error when evaluating SSR module /node_modules/cross-fetch/dist/browser-ponyfill.js:
<script lang="ts">
import fetch from 'cross-fetch';
import { ApolloClient, InMemoryCache, HttpLink } from "#apollo/client";
const client = new ApolloClient({
ssrMode: true,
link: new HttpLink({ uri: '/graphql', fetch }),
uri: 'http://localhost:4000/graphql',
cache: new InMemoryCache()
});
</script>
With SvelteKit the subject of CSR vs. SSR and where data fetching should happen is a bit deeper than with other somewhat "similar" solutions. The bellow guide should help you connect some of the dots, but a couple of things need to be stated first.
To define a server side route create a file with the .js extension anywhere in the src/routes directory tree. This .js file can have all the import statements required without the JS bundles that they reference being sent to the web browser.
The #apollo/client is quite huge as it contains the react dependency. Instead, you might wanna consider importing just the #apollo/client/core even if you're setting up the Apollo Client to be used only on the server side, as the demo bellow shows. The #apollo/client is not an ESM package. Notice how it's imported bellow in order for the project to build with the node adapter successfully.
Try going though the following steps.
Create a new SvelteKit app and choose the 'SvelteKit demo app' in the first step of the SvelteKit setup wizard. Answer the "Use TypeScript?" question with N as well as all of the questions afterwards.
npm init svelte#next demo-app
cd demo-app
Modify the package.json accordingly. Optionally check for all packages updates with npx npm-check-updates -u
{
"name": "demo-app",
"version": "0.0.1",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build --verbose",
"preview": "svelte-kit preview"
},
"devDependencies": {
"#apollo/client": "^3.3.15",
"#sveltejs/adapter-node": "next",
"#sveltejs/kit": "next",
"graphql": "^15.5.0",
"node-fetch": "^2.6.1",
"svelte": "^3.37.0"
},
"type": "module",
"dependencies": {
"#fontsource/fira-mono": "^4.2.2",
"#lukeed/uuid": "^2.0.0",
"cookie": "^0.4.1"
}
}
Modify the svelte.config.js accordingly.
import node from '#sveltejs/adapter-node';
export default {
kit: {
// By default, `npm run build` will create a standard Node app.
// You can create optimized builds for different platforms by
// specifying a different adapter
adapter: node(),
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
}
};
Create the src/lib/Client.js file with the following contents. This is the Apollo Client setup file.
import fetch from 'node-fetch';
import { ApolloClient, HttpLink } from '#apollo/client/core/core.cjs.js';
import { InMemoryCache } from '#apollo/client/cache/cache.cjs.js';
class Client {
constructor() {
if (Client._instance) {
return Client._instance
}
Client._instance = this;
this.client = this.setupClient();
}
setupClient() {
const link = new HttpLink({
uri: 'http://localhost:4000/graphql',
fetch
});
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
return client;
}
}
export const client = (new Client()).client;
Create the src/routes/qry/test.js with the following contents. This is the server side route. In case the graphql schema doesn't have the double function specify different query, input(s) and output.
import { client } from '$lib/Client.js';
import { gql } from '#apollo/client/core/core.cjs.js';
export const post = async request => {
const { num } = request.body;
try {
const query = gql`
query Doubled($x: Int) {
double(number: $x)
}
`;
const result = await client.query({
query,
variables: { x: num }
});
return {
status: 200,
body: {
nodes: result.data.double
}
}
} catch (err) {
return {
status: 500,
error: 'Error retrieving data'
}
}
}
Add the following to the load function of routes/todos/index.svelte file within <script context="module">...</script> tag.
try {
const res = await fetch('/qry/test', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
num: 19
})
});
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
Finally execute npm install and npm run dev commands. Load the site in your web browser and see the server side route being queried from the client whenever you hover over the TODOS link on the navbar. In the console's network tab notice how much quicker is the response from the test route on every second and subsequent request thanks to the Apollo client instance being a singleton.
Two things to have in mind when using phaleth solution above: caching and authenticated requests.
Since the client is used in the endpoint /qry/test.js, the singleton pattern with the caching behavior makes your server stateful. So if A then B make the same query B could end up seeing some of A data.
Same problem if you need authorization headers in your query. You would need to set this up in the setupClient method like so
setupClient(sometoken) {
...
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: `Bearer ${sometoken}`
}
};
});
const client = new ApolloClient({
credentials: 'include',
link: authLink.concat(link),
cache: new InMemoryCache()
});
}
But then with the singleton pattern this becomes problematic if you have multiple users.
To keep your server stateless, a work around is to avoid the singleton pattern and create a new Client(sometoken) in the endpoint.
This is not an optimal solution: it recreates the client on each request and basically just erases the cache. But this solves the caching and authorization concerns when you have multiple users.

Access to fetch at 'API_Gateway_URL' from origin 'S3_host_url' has been blocked by CORS policy

I know this question has been asked previously but I couldn't find any answer that solves my problem, so please forgive me if it is repetitive.
I have created a Lambda function that reads data from a DynamoDB table. I created an API gateway for this Lambda function.
When I directly hit the url in my browser, I get the expected result. But when I fetch the URL in my react app, I'm getting the below error(I have hosted my react app on S3 bucket with static website hosting)
Access to fetch at 'API_gateway_url'
from origin 'S3_static_website_endpoint' has been blocked
by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
On searching the web, I found out that I need to set the 'Access-Control-Allow-Origin' header in my Lambda and I have done it, but still I'm getting the same issue.
PS: I'm posting this question after 1 whole day of trial-error and looking at different answers, so if you know the answer please help me!
Lambda function:
console.log('function starts');
const AWS = require('aws-sdk');
const dynamoDB = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
function formatResponse(data, code) {
return {
statusCode: code,
headers: {
'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Credentials" : true,
"Access-Control-Allow-Headers":"X-Api-Key"
},
body: JSON.stringify(data)
}
}
let param = {
TableName: 'tableName',
Limit: 100 //maximum result of 100 items
};
//Will scan your entire table in dynamoDB and return results.
dynamoDB.scan(param, function(err,data){
if(err){
return formatResponse(data, 400);
}else{
return formatResponse(data, 200);
}
});
}
React app:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: {}
};
}
async componentDidMount() {
try {
const response = await fetch('API_gateway_url');
let responseJson = await response.json();
this.setState(
{
isLoading: false,
dataSource: responseJson
},
function () { }
);
} catch (error) {
console.error(error);
}
}
render() {
let { dataSource } = this.state;
if (this.state.isLoading) {
return <div>Loading...</div>;
} else {
return (
<div>
{dataSource.Items.map(item => (
<div key={item.PlayerId}>
<h1>{item.PlayerId}</h1>
<li>{item.PlayerName}</li>
<li>{item.PlayerPosition}</li>
<li>{item.PlayerNationality}</li>
</div>
))}
</div>
);
}
}
}
export default App;
I suspect that your Lambda is not run for OPTIONS requests (i.e. a "preflight"). You can configure CORS in your API Gateway which should resolve the problem. See Enabling CORS for a REST API resource.
This was resolved by using the cors package.
Implementation can be found here:
https://epsagon.com/blog/aws-lambda-express-getting-started-guide/

Can't Programatically fetch data with Apollo Client

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
}
}

Apollo-client returns "400 (Bad Request) Error" on sending mutation to server

I am currently using the vue-apollo package for Apollo client with VueJs stack with django and graphene-python for my GraphQl API.
I have a simple setup with vue-apollo below:
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
import Cookies from 'js-cookie'
const httpLink = new HttpLink({
credentials: 'same-origin',
uri: 'http://localhost:8000/api/',
})
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
connectToDevTools: true,
})
export const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
// Install the vue plugin
Vue.use(VueApollo)
I also have CORS setup on my Django settings.py with the django-cors-headers package. All queries and mutations resolve fine when I use graphiQL or the Insomnia API client for chrome, but trying the mutation below from my vue app:
'''
import gql from "graphql-tag";
import CREATE_USER from "#/graphql/NewUser.gql";
export default {
data() {
return {
test: ""
};
},
methods: {
authenticateUser() {
this.$apollo.mutate({
mutation: CREATE_USER,
variables: {
email: "test#example.com",
password: "pa$$word",
username: "testuser"
}
}).then(data => {
console.log(result)
})
}
}
};
NewUser.gql
mutation createUser($email: String!, $password: String!, $username: String!) {
createUser (username: $name, password: $password, email: $email)
user {
id
username
email
password
}
}
returns with the error response below:
POST http://localhost:8000/api/ 400 (Bad Request)
ApolloError.js?d4ec:37 Uncaught (in promise) Error: Network error: Response not successful: Received status code 400
Regular queries in my vue app, however, work fine resolving the right response, except mutations, so this has me really baffled
400 errors generally mean there's something off with the query itself. In this instance, you've defined (and you're passing in) a variable called $username -- however, your query references it as $name on line 2.
In addition to graphiQL, I would like to add that apollo-link-error package would also had been of great help.
By importing its error handler { onError }, you can obtain great detail through the console about errors produced at network and application(graphql) level :
import { onError } from 'apollo-link-error';
import { ApolloLink } from 'apollo-link';
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
console.log('graphQLErrors', graphQLErrors);
}
if (networkError) {
console.log('networkError', networkError);
}
});
const httpLink = ...
const link = ApolloLink.from([errorLink, httpLink]);
const client = new ApolloClient({
...,
link,
...
});
By adding this configuration where you instantiate your Apollo Client, you would have obtained an error similar to this one:
GraphQLError{message: "Syntax Error: Expected {, found Name "createUser""}
Further information can be found in Apollo Doc - Error handling: https://www.apollographql.com/docs/react/features/error-handling.
Hope it helps in the future.
For me, it was the fact that I was using a field not defined in the GraphQL schema. Always be careful!
For sure the mutation is not formatted correctly if that is exactly what you are sending. You need an opening bracket in the mutation
mutation createUser($email: String!, $password: String!, $username: String!) {
createUser (username: $name, password: $password, email: $email) {
user {
id
username
email
password
}
}
}
With any of these queries when i run into bugs i paste it into either graphiql or graphql playground to identify what the formatting errors is in order to isolate what is wrong.
For people using laravel for backend, this helped me solve the problem
In the laravel project find file config/cors.php and change line 'paths' => ['api/*', 'sanctum/csrf-cookie'], to 'paths' => ['api/*', 'graphql', 'sanctum/csrf-cookie'],
Also in your vue app ensure that you're not using the no-cors mode in apollo config
Regards