I am using Netlify Functions in my web application and getting the following error: "Blocked request to a resource of another origin: the "Same Origin Policy" rules do not allow loading remote resources from http://localhost:8888/api/contact". I need help in resolving this issue. Can anyone provide any suggestions or solutions for this problem specifically with Netlify Functions?
When I set proxy in package.json
"proxy": "http://localhost:8888",
it works but only on development on production it doesnt.How can achive it to work for development and production.
this is my netlify config
[build]
command = "astro build"
functions = "netlify/functions"
publish = "dist"
[functions]
node_bundler = "esbuild"
[[redirects]]
from = "/api/*" # simplify all calls to serverless functions
to = "/.netlify/functions/:splat" # all function calls will go to this path
status = 200 # ok code
force = true # ensure to always redirect
i have netlify/functions/contact.ts
import { createTransport } from "nodemailer";
if (!process.env.SENDGRID_API_KEY) {
throw new Error("Missing SENDINBLUE_API_KEY");
}
if (!process.env.TO_EMAIL) {
throw new Error("Missing TO_EMAIL");
}
export const handler = async ({ body }) => {
try {
let payload;
try {
payload = JSON.parse(body);
} catch (error) {
return {
statusCode: 400,
body: JSON.stringify({ message: "Invalid request body" }),
};
}
const { name, email, message } = payload;
if (!name || !email || !message) {
return {
statusCode: 422,
body: "Missing required fields",
};
}
let transporter = createTransport({
host: "smtp-relay.sendinblue.com",
port: 587,
auth: {
user: process.env.TO_EMAIL,
pass: process.env.SENDGRID_API_KEY,
},
});
const date = new Date().toDateString();
const html = `
<div>
You got email from ${name}! <br><br>
${message}
<br><br>
Date: ${date}
</div>
`;
const mail = {
from: email,
to: process.env.TO_EMAIL,
subject: `Portfolio Contact from ${name}`,
html: html,
};
transporter.sendMail(mail);
return {
statusCode: 200,
body: JSON.stringify({ message: "Email sent" }),
};
} catch (error) {
return {
statusCode: error.statusCode || 500,
body: error.message,
};
}
};
Try to add the following section to your netlify config file:
[[headers]]
for = "/api/*"
[headers.values]
Access-Control-Allow-Origin = "http://localhost:8888"
Related
Added in a pre-requisite for the endpoint to validate that the client information being passed is legit or it will throw an error. The clientProfileValidation.clientProfileValidation method receives the request object and returns a profile object that gets attached to the request.pre.
When trying to update my route unit test, I get the below error.
Unhandled rejection occurred. One of your test may have failed silently.
TypeError: Cannot read properties of undefined (reading 'routes')
This is a nodejs api using HAPI framework. When I remove the pre from the route, the test passes. I attempted to mock the clientProfileValidation method but its not working as expected.
Route
const drayageRampRecommendation = {
method: 'POST',
path: '/endpoint',
handler: async (request, h) => {
try {
const resp = await rampHandler.rampRecommendation(request);
return h.response(resp).code(201);
} catch (error) {
return handleError(error).toBoom();
}
},
config: {
pre: [
{
method: clientProfileValidation.clientProfileValidation,
assign: 'profile'
}
],
payload: {
allow: ['application/json', 'application/*+json']
}
}
};
Unit Test:
Using the Tape and Test Double Libraries for testing
test('drayage/recommend-ramps route: should return 201 when successfully processed', async (t) => {
beforeEachRampRecommendation();
const options = {
method: 'POST',
url: '/endpoint',
payload: recommendRampFixture,
headers: { authorization: 'Bearer 123' },
auth: {
credentials: { user: 'test', clientId: 'testClient' },
strategy: 'default'
}
};
const testProfile = {
_id: 'testId',
auth0ClientName: 'test client'
};
td.when(clientProfileValidation.clientProfileValidation(), {
ignoreExtraArgs: true
}).thenReturn(testProfile);
td.when(recommendRampHandler.rampRecommendation(), {
ignoreExtraArgs: true
}).thenReturn('');
const server = await buildServer(routes);
const response = await server.inject(options);
t.equal(response.statusCode, 201, 'Should return 201 status code');
td.reset();
t.end();
});
I have a quick question. I am using Axios to send requests to the nodejs API, when I set the token in the request header the API returns "jwt must be provided". The API expects the token with a custom name attached to it - here's how far I've gotten.
Snippet of API code that sends the token on login:
const token = jwt.sign(
{
userID: result[0].userID,
firstName: result[0].firstName,
lastName: result[0].lastName,
email: result[0].email,
role: result[0].role,
// exp: Math.floor(Date.now() / 1000) + 60 * 60,
},
"HeyImaPrivateKeyyy"
);
res.json({ token });
console.log("Login Attempt", res.statusMessage, req.body);
} else {
res.status(400).send({ message: "Invalid credentials!" });
console.log("Login Attempt", res.statusMessage, req.body);
}
-- React code from here --
Response from API on successful login:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySUQiOjEsImZpcnN0TmFtZSI6IkNhbWVyb24iLCJsYXN0TmFtZSI6IkVyYXNtdXMiLCJlbWFpbCI6ImNhbWVyb25AY2xpZnRjb2xsZWdlLmNvbSIsInJvbGUiOiJzdXBlckFkbWluIiwiaWF0IjoxNjYzMzEzNTM2fQ.9R6vXn-5Vb5fj48eUJGPNUGnXMw9TXOjJCox7U36WMI"
}
Saving the token on successful login (React)
const login = async ({ email, password }) => {
const res = await api.post(
"/auth",
{
email: email, //varEmail is a variable which holds the email
password: password,
},
{
headers: {
"Content-type": "application/json",
Authorization: false,
},
}
);
const { from } = state || {};
let token = jwt(res.data.token);
setToken("x-auth-token", token); // your token
localStorage.setItem("x-auth-token", res.data.token);
localStorage.setItem("userLogged", true);
localStorage.setItem("name", token.firstName);
localStorage.setItem("role", token.role);
navigate("/auth/dashboard" || from.pathname, { replace: true });
};
Here is the React component that is trying to call the API:
const [count, setCount] = useState(null);
const token = localStorage.getItem("x-auth-token");
const studentCount = useEffect(() => {
const config = {
headers: { "x-auth-token": token },
"Content-type": "application/json",
};
api.get("/students/", {}, config).then((response) => {
setCount(response.data);
});
}, [token]);
if (!count) return null;
This is what the API is expecting on request:
export const teacher = (req, res, next) => {
const token = req.header("x-auth-token");
if (!auth && !token)
return res.status(401).send({ message: "Access denied." });
const decoded = jwt.verify(token, "DemoPrivateKey");
if (auth && ["superAdmin", "admin", "teacher"].includes(decoded.role)) {
next();
} else {
res.status(400).send({ message: "Access denied!" });
}
};
Ideally, I would like to send the token as a header on successful login, but it saves as undefined on the client (have no idea how to fix that).
If you're using Axios then, as per the doc, get method should have config parameter in second position not third one.
So maybe, simply updating api.get("/students/", {}, config) into api.get("/students/", config) should solve your issue.
I am following the stripe docs about validating webhooks and despite I do everything as they do i keep getting 400 error. BTW in the docs here https://stripe.com/docs/webhooks/signatures they don't return from the catch blok but here https://stripe.com/docs/webhooks/quickstart they do, so I assyme that the correct option is to return from it? And back to my main problem I have no idea what am I missing here this is my code:
import { NextApiHandler } from "next";
import { Stripe } from "stripe";
import { apolloClient } from "../../graphql/apolloClient";
import {
UpdateOrderDocument,
UpdateOrderMutation,
UpdateOrderMutationVariables,
} from "../../generated/graphql";
import type { StripeWebhookEvents } from "../../stripeEvents";
const stripeWebhookHandler: NextApiHandler = (req, res) => {
const webhookSignature = req.headers["stripe-signature"];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
const stripeSecret = process.env.STRIPE_SECRET_KEY;
if (!stripeSecret || !webhookSignature || !webhookSecret) {
return res.status(500).json({ error: "Stripe credential not provided" });
}
const stripe = new Stripe(stripeSecret, { apiVersion: "2020-08-27" });
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
webhookSignature,
webhookSecret
) as StripeWebhookEvents;
} catch (err: unknown) {
return res
.status(400)
.send(`Webhook Error: ${err instanceof Error && err.message}`);
}
switch (event.type) {
case "charge.succeeded":
apolloClient.mutate<UpdateOrderMutation, UpdateOrderMutationVariables>({
mutation: UpdateOrderDocument,
variables: {
id: {
id: event.data.object.metadata.cartId,
},
data: {
stripeCheckoutId: event.data.object.id,
email: event.data.object.receipt_email,
},
},
});
break;
}
res.status(204).end();
};
export default stripeWebhookHandler;
and I thought that maybe next has maybe different shape of req.headers or req.body and I am not sure abouut req body, headers seem to be in tact however. At least the signature seems to be extracted correctly. Dhose are test data of course:
{
webhookSignature: 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
webhookSecret: 'whsec_0d8a54d09bf221f7c5c77ca7a3fca4b988ccd9e49d8a31d7c91f854025503fe4',
stripeSecret: 'sk_test_51Kuvp4KsMpogemXo9vUcgihi1vK4dlof76OL4EcYhmVgN8r81tl7r0rSsqWgOtXxYnZPJlo6S2KA0gFWZmyBQIbS00ABzicwum',
headers: {
host: 'localhost:3000',
'user-agent': 'Stripe/1.0 (+https://stripe.com/docs/webhooks)',
'content-length': '2818',
accept: '*/*; q=0.5, application/xml',
'cache-control': 'no-cache',
'content-type': 'application/json; charset=utf-8',
'stripe-signature': 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
'accept-encoding': 'gzip'
}
is there something I do incorrectly here? Thanks a lot
I am following a MailChimp API tutorial
When I test the API, I get a 401 response saying my API key is invalid.
Error -
Status: 401
"Your API key may be invalid, or you've attempted to access the wrong datacenter."
I have yet to register a domain yet, this is being testing using a local server. Could this be error be caused by MailChimp's refusing the request for another reason, perhaps CORS?
app.post('/signup', (req, res) => {
// Get form data
const { email } = req.body;
// Make sure field is filled
if(!email) {
res.redirect('/html/fail.html');
return;
}
// Construct req data
const data = {
members: [
{
email_address: email,
status: 'subscribed'
}
]
}
// Convert to JSON
const postData = JSON.stringify(data);
const options = {
url: 'https://us19.api.mailchimp.com/3.0/lists/listID',
method: 'POST',
headers: {
Authorization: 'auth xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us19'
},
body: postData
};
request(options, (err, response, body) => {
if(err) {
console.log(err);
res.redirect('/html/fail.html');
} else {
if(response.statusCode === 200) {
res.redirect('/html/success.html');
} else {
console.log(response.body);
res.redirect('/html/fail.html');
}
}
});
})
I tried running the same code in request in PostMan and I got back a 200 response.
I was initially importing the API key from a config file, that I had not destructured...
I couldn't find any way how can I set up CORS using serverless netlify functions.
I have used this function example to create my own e-mail form sender:
const nodemailer = require('nodemailer');
exports.handler = function(event, context, callback) {
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: process.env.MAIL_LOGIN,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
refreshToken: process.env.REFRESH_TOKEN,
accessToken: process.env.ACCESS_TOKEN
}
});
console.log(event.body);
transporter.sendMail({
from: process.env.MAIL_LOGIN,
to: process.env.MAIL_TO,
subject: process.env.SUBJECT + new Date().toLocaleString(),
text: event.body
}, function(error, info) {
if (error) {
callback(error);
} else {
callback(null, {
statusCode: 200,
body: "Ok"
});
}
});
}
But unfortunately, I am able to send it through every single domain which is not really safe as some people can send spam into that inbox.
Would you be able to follow me to any example?
Thank you in advance
You can do something like this:
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE'
};
if (event.httpMethod !== 'POST') {
// To enable CORS
return {
statusCode: 200, // <-- Important!
headers,
body: 'This was not a POST request!'
};
}
Here is how I used it in a larger mapper function:
// src/customers.js
exports.handler = async (event, context) => {
const path = event.path.replace(/\.netlify\/functions\/[^\/]+/, '');
const segments = path.split('/').filter(e => e);
switch (event.httpMethod) {
case 'GET':
// e.g. GET /.netlify/functions/customers
if (segments.length === 0) {
return require('./customers/read-all').handler(event, context);
}
// e.g. GET /.netlify/functions/customers/123456
if (segments.length === 1) {
event.id = segments[0];
return require('./customers/read').handler(event, context);
} else {
return {
statusCode: 500,
body:
'too many segments in GET request, must be either /.netlify/functions/customers or /.netlify/functions/customers/123456'
};
}
case 'POST':
// e.g. POST /.netlify/functions/customers with a body of key value pair objects, NOT strings
return require('./customers/create').handler(event, context);
case 'PUT':
// e.g. PUT /.netlify/functions/customers/123456 with a body of key value pair objects, NOT strings
if (segments.length === 1) {
event.id = segments[0];
console.log(event.id);
return require('./customers/update').handler(event, context);
} else {
return {
statusCode: 500,
body:
'invalid segments in POST request, must be /.netlify/functions/customers/123456'
};
}
case 'DELETE':
// e.g. DELETE /.netlify/functions/customers/123456
if (segments.length === 1) {
event.id = segments[0];
return require('./customers/delete').handler(event, context);
} else {
return {
statusCode: 500,
body:
'invalid segments in DELETE request, must be /.netlify/functions/customers/123456'
};
}
case 'OPTIONS':
// To enable CORS
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE'
};
return {
statusCode: 200, // <-- Must be 200 otherwise pre-flight call fails
headers,
body: 'This was a preflight call!'
};
}
return {
statusCode: 500,
body: 'unrecognized HTTP Method, must be one of GET/POST/PUT/DELETE/OPTIONS'
};
};
I wrote a tutorial about how to work build serverless databases & netlify functions where I had CORS enabled, you can find the article here.
exports.handler = async (event, context) => {
return {
statusCode: 200,
headers: {
/* Required for CORS support to work */
'Access-Control-Allow-Origin': '*',
/* Required for cookies, authorization headers with HTTPS */
'Access-Control-Allow-Credentials': true
},
body: JSON.stringify({
message: 'Hello from netlify',
event: event,
})
}
}
You can also use express with cors. It's a way better dynamic handling of cors options.
I extracted my own netlify configuration from my projects and pushed it to GitHub:
https://github.com/kevludwig/netlify-functions-express
There is also an example sending mails using nodemailer.