How to fetch data from Sanity to React? - reactjs

I have trouble understanding on how to fetch my data from sanity. I have read the documentation but still i'm confused.
I tried just logging the data to the console but it gives me an error like, "No 'Access-Control-Allow-Origin' header is present on the requested resource."
import React from "react";
import sanityClient from "#sanity/client";
const Post = () => {
const client = sanityClient({
projectId: "6sf5fafo",
dataset: "production",
useCdn: true
});
// fetching the data
client
.fetch('*[__type == "post"][0]{title, "name": author->name}', {})
.then(res => {
console.log("Post info: ", res); // Here is when i tried to log the data but gets an error message.
})
.catch(err => {
console.log(err);
});
return (
<div>
<h1>Hello</h1>
</div>
);
};
export default Post;
Can someone do some edits to my code to properly fetch the data from sanity it would be very much appreciated.

You're getting this error because Sanity denies access from unknown browser origins. By default (when generating a new project), the only origin allowed is http://localhost:3333. You may grant access to any additional origins.
Say you're running your Content Studio on https://studio.mysite.com and want to grant access to that URL. There are two ways of doing this:
Open your terminal, switch directory to where you keep your Studio source code, then type:
sanity cors add https://studio.mysite.com
Go to your project settings and add the origin via the web UI. Since you projectId is 6sf5fafo, these settings can be found at https://manage.sanity.io/projects/6sf5fafo/settings/api
For more on Sanity and CORS, please refer to the documentation at https://www.sanity.io/docs/front-ends/cors

Related

Error while deleting S3 objects - likely an issue with credentials but could not locate the problem

So I have been following other Q&A on stackoverflow and AWS SDK docs but I still couldn't delete S3 files with the following code, it simply gives the following error Error TypeError: Cannot read properties of undefined (reading 'byteLength')
My code (s3Client.js):
import { S3Client } from "#aws-sdk/client-s3";
const REGION = `${process.env.S3_UPLOAD_REGION}`;
const creds = {
accessKeyId: process.env.S3_UPLOAD_KEY,
secretAccessKey: process.env.S3_UPLOAD_SECRET
};
// Create an Amazon S3 service client object.
const s3Client = new S3Client({
region: REGION,
credentials: creds
});
export { s3Client };
My nextJS component:
import { DeleteObjectCommand } from "#aws-sdk/client-s3";
import { s3Client } from "../lib/s3Client.js"
const bucketParams = { Bucket: "my bucket name...", Key: "my key..." };
const run = async () => {
try {
const data = await s3Client.send(new DeleteObjectCommand(bucketParams));
console.log("Success. Object deleted.", data);
return data; // For unit tests.
} catch (err) {
console.log("Error", err);
}
};
I understand from others that this seems like a credential issue but I still couldn't wrap my head around where the problem is.
Edit:
Solution to this problem -
Check the .env, depending on whether you are using React or NextJS, you will need to have either "REACT_PUBLIC" or "NEXT_PUBLIC" in order for the environment objects to be exposed via process.env.
Check your permission in your S3 bucket and set "AllowedOrigins" to include your localhost. I had mine set as "AllowedOrigins": "*" but it wasn't enough. So I have included http://localhost:3000 and it worked.
So it looks like that you're using keys credentials for this. To debug your problem the 1st thing you should do is to check the credentials outside code and SDK and make sure they're fine.
To do this, setup the credentials on CLI by setting environment variables.
export AWS_ACCESS_KEY_ID=<Your Key here>
export AWS_SECRET_ACCESS_KEY=<SECRET HERE>
export AWS_DEFAULT_REGION=<AWS REGION>
Reference: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
After this run this command to verify credentials are setup correctly aws sts get-caller-identity
If they show you the account and user details. Then delete the file using CLI. If that works then it is confirmed that issue is not with the credentials and with code. If not, it will point you in the right direction where the issue is.

Accessing Drive thumbnailLink with gapi.client.request gives 404

I'm currently working on a React app that should load a set of (non-public) files from a user's Google Drive and display thumbnails using the CSS background-image: url(...) property. I can load all of the file metadata using the Gapi Files:get method, and an OAuth token is set ahead of time using gapi.client.setToken. I'm hoping to load the thumbnail as follows:
function CollectionArtifact(props){
const [thumbnail, setThumbnail] = useState(null);
async function loadThumbnail(){
try {
const res = await window.gapi.client.request(props.artifact.thumbnailLink);
const data = await res.blob();
const localURL = URL.createObjectUrl(data);
setThumbnail(localURL);
catch(e){ console.log('Failed to load thumbnail', e); }
}
useEffect(() => {
if(props.apisLoaded){
loadThumbnail();
}
}, [props.apisLoaded]);
return (
...
<div style={{backgroundImage: `url(${thumbnail})`}}>
...
</div>
...
);
}
The client is initialized in a wrapper component like so:
window.gapi.load('client:auth2', () => {
window.gapi.client.init({
apiKey: developerKey,
clientId: clientId,
scope: 'https://www.googleapis.com/auth/drive'
}).then(() => {
window.gapi.client.load('drive', 'v3', () => {
if(props.onGapisLoad) props.onGapisLoad();
setInitialized(true);
});
});
});
However, the window.gapi.client.request call gives Failed to load resource: the server responded with a status of 404, and the URL that's actually giving this error is the proxy request https://docs.google.com/static/proxy.html?usegapi=1&.... Using fetch also doesn't work, as it gives a CORS error. Meanwhile, if I try to access the thumbnailLink in the browser while signed in to the correct Google account (and no other accounts), I can see the image without a problem.
What would be the proper way to load thumbnails through an authorized request? I've seen other answers that recommend generating a PDF and using it to create a public thumbnail, but I'd like to avoid creating extra files in the user's drive or making file data publicly accessible. And since the image can be loaded directly from the browser, I would assume there's a way to access it with an authorized client.

React/Axios API Get Request Issues (CORS & Internal Server Error 500)

I am attempting to complete an axios GET request to an API and I'm running into an Internal Server Error - 500 and I'm curious if this is simply my code and/or my attempt at making this call or something else. Even though the CORS issue seems to be behind me, I'll start from the beginning just in case its related to my current issue.
My initial attempt at the request gave me the following CORS error:
...from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: It does not have HTTP ok status.
After doing a lot of research on this, I found that I could append https://cors-anywhere.herokuapp.com to my target API URL and get around this issue. So far, so good but now I am getting the following locally in my browser: net::ERR_NAME_NOT_RESOLVED
So I decided to jump over to Postman and input the given headers to access this API to see if I could find more information and I'm getting the following on Postman:
{
"timestamp": "2020-11-13T01:04:47.288+0000",
"message": "General error occurred please contact support for more details",
"error": "Internal Server Error",
"status": 500
}
Now, within the documentation of this API, it states that a 500 is a server error on their part but I'm not confident in that as I think it may just be my own doing here. So I basically have two questions...
Should the developer of the API do/change anything to avoid the CORS issue or is that a common thing to run into?
Is the 500 error response on me or them?
Below is my axios request in my App.js file of my React application. Please let me know if any other code or info is needed. Thanks so much in advance for any help!
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
class App extends Component {
state = {
events: []
}
constructor() {
super();
const proxyURL = 'https://cors-anywhere.herokuapp.com'
const URL = 'https://api.example.com/api/'
const proxiedURL = proxyURL + URL
axios.get(proxiedURL, {
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + process.env.REACT_APP_AUTH_API_KEY
}
})
.then((res) => {
console.log(res.data)
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<div className="App">
<header className="App-header">
<h1>Data</h1>
</header>
</div>
);
}
}
export default App;
According to the documentation for cors-anywhere:
This API enables cross-origin requests to anywhere.
Usage:
/ Shows help /iscorsneeded This is the only resource
on this host which is served without CORS headers. /
Create a request to , and includes CORS headers in the response.
Your code is missing a trailing slash after https://cors-anywhere.herokuapp.com to work, i.e.: https://cors-anywhere.herokuapp.com/.
To answer your two other questions:
CORS issues are very common and it is up to the developer of the API to set from which domain the API can be called. More on MSDN
The 500 response means this in an internal server error, so on the server-side. Though it can be because of many reasons, like querying the wrong URL, passing unexpected data... Ideally all these should be covered and different errors would be returned every time but this is rarely the case. :)

AXIOS request method changes to 'OPTIONS' instead of 'GET'

I am trying to call and API with react app(TSX) using Axios(this the first time I am using Axios) every time I run the app the method changes to 'OPTIONS' and the request becomes invalid. Help will be appreciated. Sharing my code sorry I am hiding the Auth Token for security reasons.
Code
import React, { useState, useEffect } from 'react';
import axios from 'axios';
interface Brands {
BrandId: number;
Name: string;
}
const AUTH_TOKEN = Something hiden for security;
var baseUrl = axios.defaults.baseURL = 'https://fppdirectapi-prod.fuelpricesqld.com.au/Subscriber/GetCountryBrands?countryId=21';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.get['Content-Type'] = 'application/json';
axios.defaults.method = 'get';
const FetchFuelType = () => {
const [brands, setPosts] = useState<Brands[]>([]);
useEffect(() => {
axios.get(baseUrl)
.then(res => {
console.log(res)
setPosts(res.data)
})
.catch(err => {
console.log(err)
})
}, [])
return (
<div>
<ul>
{brands.map(Brand => (<li key={Brand.BrandId}>{Brand.Name}</li>))}
</ul>
</div>
);
};
export default FetchFuelType;
Attached image of response
OPTIONS request is part of the so-called preflight request which is needed to figure out the CORS headers to know what needs/is allowed to be sent to the server with the actual GET request. Thats why you normally see two requests in your network tab (depending on your setting)
In your example it seems you have not configured anything CORS related on your server (thus the 405) or specifically have forbidden anything other than GET/POST requests. Or potentially the site has forbidden others to access its data
Usually, options request is sent before get automatically by axios, to get some preliminary data before firing get call. Check this https://github.com/axios/axios/issues/475.
The OPTIONS request is an inherent request generated by browser.
Browser uses the OPTIONS call to find out what methods are allowed by the server.
The API server meant for supporting requests from browser must allow OPTIONS in addition to the actual method (GET / POST / etc).
If the server does not support OPTIONS, then it may not support browser.
A server that does not support OPTIONS can only support non-browser clients
(Examples: REST client in Java / nodejs)
How to solve the problem?
The problem of '405 - OPTIONS method not allowed' can be solved in one of these 2 ways:
Update the server to support OPTIONS method (Recommended for server that is supposed to support browsers)
Develop an 'Intermediary REST client' which will request data from the server, on behalf of the browser
Browser <--> REST client (supports OPTIONS, POST) <--> Actual web service (does not support OPTIONS)

Getting “TypeError: failed to fetch” when sending an email with SendGrid on ReactJS project

I am trying to send email with SendGrid in ReactJS project.
This is my componnet:
//Email.js
import React from 'react'
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg).catch(error => {alert(error.toString()); });
export const Email= () => (
<h1>Email Sending Page</h1>
)
When I am trying to run the app with "npm start" on localhost, the email is not sent and I got the error message "TypeError: Failed to fetch".
But, if I am using this code:
//Email.js
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
sgMail.send(msg)
and do this command: "node Email.js" the mail is sent. It works only this way and I cannot understand why.
I tried any solution that I could find but nothing works.
(I tried even to put the api_key hardcoded in the code just for the test and I got the same result).
EDIT
After looking around a bit I found out that you can't use Sendgrid to send email directly from the browser.
Sendgrid won't let you send an email directly using Javascript in the
browser.
You will need to have a server set-up and use the server to send the
email instead (using your favourite back-end framework/language,
Node.js, php, Java, etc.).
The steps for sending a mail will be similar to this:
Write email details in the React application
Send a POST request to
your server endpoint (for example, /sendemail) with the email data
(recipient, title, content, etc.) Receive Email data in the server and
send it to Sendgrid api Here is the official Sendgrid documentation
regarding their CORS policy:
https://sendgrid.com/docs/for-developers/sending-email/cors/
Source: React unable to send email with SendGrid
EDIT 2
If you want to implement Sendgrid without actually building and deploying a server, you can use a simple Firebase function which is free to host.
I know this may look intimidating but in reality its pretty easy. Also I just put this together real quick so if anything doesn't work out for you, shoot me a comment.
Follow steps 1-3 on the getting started page for firebase functions. It is pretty straightforward and you end up with the firebase tools CLI installed.
Navigate to the functions/ folder inside your project on the command line/terminal.
Install the Sendgrid and cors libraries in the functions folder
npm i #sendgrid/mail cors
Add your Sendgrid API key to your firebase environment with the following command in your project:
firebase functions:config:set sendgrid.key="THE API KEY"
Copy this into your functions/index.js file:
const functions = require("firebase-functions");
const cors = require("cors")({ origin: true });
const sgMail = require("#sendgrid/mail");
exports.sendEmail = functions.https.onRequest((req, res) => {
sgMail.setApiKey(functions.config().sendgrid.api);
return cors(req, res, () => {
const { msg } = req.body;
sgMail.send(msg).catch(error => {
alert(error.toString());
});
res.status(200).send(msg);
});
});
Save it and run firebase deploy --only functions on the command line. Your function should now be live at https://us-central1-<project-id>.cloudfunctions.net/sendEmail
Now change your React file to:
//Email.js
import React, { useEffect } from 'react'
export const Email= () => {
useEffect(() => {
const sendEmail = async() => {
const msg = {
to: 'aaaaa#gmail.com',
from: 'bbbb#gmail.com',
subject: 'This is a test mail',
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
};
const response = await fetch(
'https://us-central1-FIREBASE-PROJECT-ID-HERE.cloudfunctions.net/sendEmail', {
method: 'POST',
body: JSON.stringify(msg),
headers: {
'Content-Type': 'application/json'
}
});
console.log("response", response);
}
sendEmail();
}, []);
return <h1>Email Sending Page</h1>
}
And thats it! You basically have a server side function without making a server and its free!
Feel free to ignore this if you don't feel like putting in the work but if you need any help, let me know.

Resources