I've been having CORS error when deploying my app so I decided to try out Netlify Dev.
I followed all steps of the written tutorials but I keep getting errors without being able to identify whats wrong. I haven't even deployed it yet and right now I am having the 403 error, before this was the 500 internal server error.
Please let me know if you notice any obvious mistake on my part.
Here's the node-fetch code:
const fetch = require("node-fetch");
exports.handler = async function () {
const headers = {
"x-api-key": process.env.REACT_APP_MY_API_KEY,
"content-type": "application/json",
};
try {
const response = await fetch("https://api.crimeometer.com", { headers });
if (!response.ok) {
return { statusCode: response.status, body: response.statusText };
}
const data = await response.json();
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (err) {
console.log(err);
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }),
};
}
};
Here's the frontend of my app (with the relevant code):
const initializeApp = useCallback(() => {
async function fetchData() {
try {
let req = new Request(
`./.netlify/functions/node-fetch?lat=${lat}&lon=${lon}&distance=10km&datetime_ini=${newdateyear}&datetime_end=${newdatenow}&page=1`);
const res = await fetch(req);
const info = await res.json();
setCrimes(info.incidents);
} catch (err) {
console.log(err);
}
}
}, [submit, lat, lon]);
useEffect(() => {
initializeApp();
}, [initializeApp]);
This is the error in my console:
This is where i am trying to fetch data from:
https://api.crimeometer.com/v1/incidents/raw-data?lat=${lat}&lon=${lon}&distance=10km&datetime_ini=${newdateyear}&datetime_end=${newdatenow}&page=1
It requires two headers to be set on frontend, which I have done in the netlify function
The API key is currently being added to the React side, where it sets a header and makes a call to the netlify function. But the API key is actually required at the netlify function to request the third party API.
So you have to move the request header from React to Netlify function with something like:
const headers = {
'x-api-key': API_KEY,
'content-type': 'application/json',
}
const response = await fetch(
"https://api.crimeometer.com/v1/incidents/raw-data",
{ headers }
);
Related
I'm posting data to a DynamoDB table with axios in a React front-end.
The API is set up through a serverless configuration with an API Gateway and Lambda on AWS.
While the request goes through and I see the added item on the database I still get a CORS error https://i.stack.imgur.com/m7yMG.jpg
This is the axios method:
import axios from "axios";
export const sendItemToDB = async (_data) => {
if (!_data) { return };
try {
const res = await axios({
method: "POST",
url: process.env.REACT_APP_QUERY_API,
data: _data,
headers: {
"Content-Type": "text/plain"
},
});
console.log("data returned from api", res);
} catch (error) {
console.log("Error sending File to db: ");
console.log(error);
}
};
And the API method on Lambda:
const createRecord = async (event) => {
const response = { statusCode: 200 };
try {
const body = JSON.parse(event.body);
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME,
Item: marshall(body || {}),
};
const createResult = await db.send(new PutItemCommand(params));
response.body = JSON.stringify({
message: "Successfully created record.",
createResult,
});
} catch (e) {
console.error(e);
response.statusCode = 500;
response.body = JSON.stringify({
message: "Failed to create record.",
errorMsg: e.message,
errorStack: e.stack,
});
}
return response;
};
I based this configuration on this tutorial : https://github.com/jacksonyuan-yt/dynamodb-crud-api-gateway
I solved this following amazon documentation and reconfiguring the serveless deployment yml.
Serverless documentation on api gateway and lambda proxy integration here
Adding the missing headers to all lambda functions was essential.
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
};
Also testing that OPTIONS is working for the preflight:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-test-cors.html
Just as Stavros noticed, the problem is that this is not a simple cross-origin POST method request (because it contains custom headers), so you need to tweak CORS settings of AWS API Gateway by adding
"POST, GET & OPTIONS" for Access-Control-Allow-Methods
"content-type" for Access-Control-Allow-Headers
You can do it through console like this
You also might need to add those headers in lambda like this
and it will work.
I was trying to post a comment through Axios. But I am getting the 401 error, even though I have passed the header with the Authorization token. I don't know what is causing this error.
Please Help . My boss is yelling at me :) (I am a junior developer).
const handleSubmit = async (e: any) => {
e.preventDefault();
const finalComment = { comment };
try {
const response = await axios.post(
`${TEST_API_URL}/user/proposal/comments/admin/create`,
finalComment,
{
withCredentials: true,
headers: {
Authorization: `${localStorage.getItem('access_token')}`,
},
}
)
console.log(response);
} catch (error) {
console.log(error);
}
}
I'm attempting to send a complete form to back-end server (decoupled MERN application in localhost).
The request is reaching the server, but not posting to the database.
This is seen in server console...
POST /contracts 404 1.371 ms - 19
The data to be sent in form is logging in console as an object (as intended).
This is the service function making post request from the frontend to the backend ...
const BASE_URL = `${process.env.REACT_APP_BACKEND_SERVER_URL}/contracts`
export const createContract = async (formData) => {
try {
const res = await fetch(BASE_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'Authorization': `Bearer ${tokenService.getToken()}`
},
body: JSON.stringify(formData)
})
return await res.json()
} catch (error) {
console.log(error)
throw error
}
}
this is the backend routes... (already set up as /contracts in server.js)
import { Router } from 'express'
import * as contractsCtrl from '../controllers/contracts.js'
const router = Router()
router.post('/', contractsCtrl.create)
router.get('/all', contractsCtrl.index)
export { router }
create controller function on the backend...
function create(req, res) {
console.log(req.body)
Contract.create(req.body)
.then(contract => res.json(contract))
.catch(err => res.json(err))
}
Any help you have would be appreciated!!
I am working on a react-admin project. The backend is written using Django rest framework which runs on a docker container. The authentication endpoints for access and refresh tokens are written using djangorestframework-simplejwt and served at http://localhost:8000/api/token/ and http://localhost:8000/api/token/refresh/ respectively.
I have written my own authProvider.js and dataProvider.js for react admin. The login and checkAuth functions for authProvider.js looks like this
// in src/authProvider.js
import jwt from "jsonwebtoken";
export default {
login: async ({ username, password }) => {
const request = new Request('http://localhost:8000/api/token/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { refresh, access } = await response.json();
localStorage.setItem('refreshToken', refresh);
localStorage.setItem('accessToken', access);
},
logout: ...
checkAuth: async () => {
const accessToken = localStorage.getItem('accessToken');
const refreshToken = localStorage.getItem('refreshToken');
if (accessToken && refreshToken) {
const { exp } = await jwt.decode(accessToken);
if (exp > (new Date().getTime() / 1000) - 10) {
return Promise.resolve();
} else {
const request = new Request('http://localhost:8000/api/token/refresh/', {
method: 'POST',
body: JSON.stringify({ "refresh": refreshToken }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request)
.then(response => {
if (response.status !== 200) {
throw new Error(response.statusText);
}
return response.json();
})
.then(({ token }) => {
localStorage.setItem('accessToken', token);
return Promise.resolve();
});
return response;
}
}
return Promise.reject();
},
checkError: ...
getPermissions: () => Promise.resolve(),
}
Retrieving data works fine. But whenever I perform a create, edit and delete operation, I am automatically logged out with a 401 Unauthorized error. Error message from docker server log
Unauthorized: /api/products/2
"PUT /api/products/2 HTTP/1.1" 401
Error from browser console: PUT HTTP://localhost:8000/api/products/2 401 (Unauthorized)
Prior to adding authProvider and using docker container as backend, CRUD data mutations worked fine, using a local python venv as backend. So I assume the dataProvider.js is not responsible here.
I have not been able to figure this out for quite some time. Can anyone help me figure out what I might be doing wrong here? Thank you for your time.
EDIT 1: It seems the access token is not sent from the frontend during API request, hence the server returning 401 Unauthorized
You need to modify your dataProvider to include the token (in a token, a cookie, or in a GET parameter, depending on what your backend requires). This is explained in the react-admin auth documentation:
import { fetchUtils, Admin, Resource } from 'react-admin';
import simpleRestProvider from 'ra-data-simple-rest';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const { token } = JSON.parse(localStorage.getItem('auth'));
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const dataProvider = simpleRestProvider('http://localhost:3000', httpClient);
const App = () => (
<Admin dataProvider={dataProvider} authProvider={authProvider}>
...
</Admin>
);
I'm trying to integrate useSWR in a next js project I'm working on.
I want to pass a config to fetcher as an argument. I have read about Multiple Arguments in the docs
but it's not returning the data for some reason. it is making the api request I can see that in the network tab.
not sure how to do this.
any suggestions?
const fetcher = async (url, config) => {
let res;
if (config) {
res = await fetch(url, config);
} else {
res = await fetch(url);
}
if (!res.ok) {
const error = new Error('An error occurred while fetching the data.');
error.info = await res.json();
error.status = res.status;
throw error;
}
return res.json();
};
const { data, error } = useSWR(
[
rolesUrl,
{
headers: {
Authorization: `Bearer ${user.token}`,
'Content-Type': 'application/json',
},
},
],
fetcher
);
After a very long debuging I found out. fetch is getting the config object.
and then makes the request to the api. then useSWR returns the response. which causes the component to re-render. the config object gets recreated.
useSWR thinks argument updated and make the api request again. that's why we don't get the data.
I have fixed this with useMemo hook
const config = useMemo(
() => ({
headers: {
Authorization: `Bearer ${user.token}`,
'Content-Type': 'application/json',
},
}),
[user.token]
);
const { data, error } = useSWR([rolesUrl, config], fetcher);