How to set up CORS in Netlify Serverless function - javascript

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.

Related

How to passing data from middleware to components/api in Next Js 13?

i just trying Next Js 13 middleware feature. But i confuse how to passing data from middleware to components/page/api.
For example i try to pass payload or who user is currently loggin.
Normally without middleware feature, i just make middleware file and if jwt verify true, i will send/pass payload data to my components/api
import {example} from 'middleware/example'
const payload = await example(req, res)
But if i using Next Js 13 feature and i read the docs, i just find how to send response like
return new NextResponse(
JSON.stringify({
success: false,
message: 'authentication failed'
}),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
if i use that, it will return json data, and not continue the middleware chain, if i use
return NextResponse.next()
it will continue the middleware chain, but how do i passing my payload data to components/page/api?.
im trying like this
return NextResponse.next({tes: "tes"})
but i can't find how to get that data from components/api.
This is my middleware code
if (request.nextUrl.pathname.startsWith('/api/posts')) {
const requestHeaders = new Headers(request.headers)
const authorization = requestHeaders.get('authorization')
if (!authorization) {
return new NextResponse(
JSON.stringify({
success: false,
message: 'authentication failed'
}),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}
const authSplit = authorization.split(' ')
const [authType, authToken] = [
authSplit[0],
authSplit[1]
]
if (authType !== 'Bearer') {
return new NextResponse(
JSON.stringify({
success: false,
message: 'authentication failed'
}),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}
const payload = await verify(authToken)
if (!payload) {
return new NextResponse(
JSON.stringify({
success: false,
message: 'authentication failed'
}),
{ status: 401, headers: { 'content-type': 'application/json' } }
)
}
return NextResponse.next()
}

Google Cloud Function + Calendar API

I'm trying to use a Google Cloud Function to handle authorization for the google calendar api. But I'm having trouble exchanging the code for a token. I have written two functions, one that generates an auth url that directs users to sign in, with a code and the second is the one that should exchange the code for a token and redirect users to the page. The second function just times out and doesn't return anything.
// The first cloud function
const { google } = require('googleapis');
const credentials = require('./credentials.json')
exports.getURL = async (req, res) => {
const SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"];
let redirectURL, oAuth2Client, response;
try {
redirectURL = await authorize(credentials);
}
catch (e) {
// console.log(e.message);
response = JSON.stringify({
statusCode: 500,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
},
body: JSON.stringify({
error: e.message,
}),
})
return res.send(response);
}
console.log(redirectURL);
response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
'Cache-Control': 'no-cache',
'Content-Type': 'text/html',
},
body: redirectURL,
}
return res.send(response);
function authorize(credentials) {
// const { CLIENT_SECRET, CLIENT_ID, REDIRECT_URIS } = process.env
const {client_secret, client_id, redirect_uris} = credentials.installed
oAuth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
return getAccessToken(oAuth2Client);
}
function getAccessToken(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: "offline",
scope: SCOPES,
});
// returns link that user clicks to authorize app
return authUrl;
}
}
The output of the first function is this:
{
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": "true",
"Cache-Control": "no-cache",
"Content-Type": "text/html"
},
"body": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly&response_type=code&client_id=92706390722-rl3h6c2cmqqi15m0ou58cva56hp2vo0c.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fus-central1-learning-node-291903.cloudfunctions.net%2FgetCalEvents"
}
I'm not sure what is happening, but the console log statements, don't output anything useful. Any hints as to where I could be encountering a problem?
// 2nd function that exchanges code for token
exports.getCalEvents = async (req, res) => {
// wait for an event
// request comes in with code parameter and query string from 'GET' request
let params = req.query;
const code = params.code;
let token, response;
try {
token = await getAccessToken(code);
console.log('token', token);
} catch (err) {
response = JSON.stringify({
statusCode: 500,
})
return response;
}
async function getAccessToken(code) {
console.log(code);
const {client_secret, client_id, redirect_uris} = credentials.installed;
oAuth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[2]
);
try {
let accessToken = await oAuth2Client.getToken(code);
console.log('access token', accessToken);
return accessToken;
} catch (e) {
throw e;
}
}
//return access token
response = JSON.stringify({
statusCode: 302,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true',
'Cache-Control': 'no-cache',
},
body: {'token': token},
});
console.log(response);
return res.send(response);
}

how to pass value in api body in protractor while requesting the api

I want to pass the value in API request body.
I tried below code for that
var options = { method: 'POST',
url: 'https://ssgpreprod.serviceurl.in/gonogo-api/atm/tw/cro-approval',
headers:
{ 'Postman-Token': '9d6a0ad1-c3a1-402f-b845-b6416f49df6b',
'cache-control': 'no-cache',
'Content-Type': 'application/json' },
body:
{ oHeader:
{ sReqType: 'application/json',
sAppSource: 'WEB 2.02.01',
sSourceID: 'GONOGO_HDBFS',
sAppID: 610961419000670,
dtSubmit: '',
sCroId: 'HDB_TW_CRO#cell.com',
sDsaId: 'default',
sInstID: 4019,
sUserName: 'CHDBTWCRO',
sProduct: 'TW',
sDealerId: '61096' },
sRefID:testData.twPreIpa.twReferenceId,
sAppStat: testData.twCroDetails.twCroDecision,
aCroJustification: [ { sRemark: testData.twCroDetails.twRemark, sSubTo: testData.twCroDetails.twSubjectTo} ],
bApprAmtExist: true,
dApprAmt: testData.twApplyDetails.twLoanAmount,
dItrRt: testData.twCroDetails.twRoi,
dLtv: testData.twCroDetails.twLtv,
aDedupeRefID: [] },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
browser.logger.info(JSON.stringify(body));
browser.logger.info(JSON.stringify(response));
browser.logger.info('status code is : ' + response.statusCode);
expect(response.statusCode).toBe(200).then(function () {
browser.logger.info('case is approved');
this.logOut(testData);
})
});
I am passing value from xlsx file i.e testData.twPreIpa.twReferenceId but I am getting 422 status code and below output
[2019-05-28 15:42:10.403] [INFO] : - {"title":"Conversion Failed","status":422,"detail":"The content you've sent is probably malformed."}
Also, when I add - browser.logger.info('approved'); above var options it prints on console.. but when I add - browser.logger.info(testData.twPreIpa.twReferenceId);
It gives me error .. ouput displayed -
Failed: Cannot read property 'twReferenceId' of undefined
TypeError: Cannot read property 'twReferenceId' of undefined
While this may not directly answer your question it should be helpful to you.
I worked on a similar framework to what (I assume) yours looks like,some api and some UI validations.
I had a separate class for my API calls which returned some value, sometimes the entire response body. I allowed myself the ability to pass in whatever body I needed as a parameter.
This is an example of the approach I took.
module.exports = class Endpoints {
addSomethingToUser(bearerToken, userId, JSONbody) {
//Function will preform a POST request and return the status code if successful
//Else if will return the body of the response
let endpoint = 'http://myApp.com:9090/api/users/' + userId;
return new Promise((resolve, reject) => {
let options = {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify(JSONbody),
};
request(
endpoint,
options ,
function (error, response, body) {
if (!error && (response.statusCode >= 200 && response.statusCode < 300)) {
resolve(response.statusCode);
} else {
console.log('error:', error, response && response.statusCode);
reject(JSON.stringify(response, undefined, 2));
};
}
);
});
};
}
It is called like
let apiCallsFile = require('../apiCalls/restCalls');
let apiCalls = new apiCallsFile();
it('simple test', async function(){
let requiredBody = {
"NAME": "Test Name",
"GENDER": "MALE",
};
let apiResult = await apiCalls.addSomethingToUser(bearerToken, userId, requiredBody );
expect(apiResult).toBe('201');
}
Your issue seems to be related to the actual values you are using from excel. You should attempt to print out your values before attempting to send the request to ensure they are all present and in the format you expect.

Using the Node Request module with AWS Lambda and API Gateway

This lambda function works:
exports.handler = function(event, context, callback) {
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, 'works'))
}
I can hit the API Gateway end point and receive the response message, 'works'.
However, when I add a request call (POSTing a message to Slack) that looks like this:
const request = require('request')
const moment = require('moment-timezone')
exports.handler = function(event, context, callback) {
// the json object is built following these docs:
// https://api.slack.com/docs/message-attachments
const SLACK_WEB_HOOK_URL = process.env.SLACK_WEB_HOOK_URL
const uri = SLACK_WEB_HOOK_URL
const method = 'POST'
const options = {
json,
uri,
method,
}
const slack = (opts) => {
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return 'Success!'
} else {
return 'Err!'
}
})
}
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, slack(options))
}
The server hangs when I hit the API end point ...
And I get a Task timed out after 10.00 seconds error:
{
"errorMessage": "2017-06-23T16:41:21.483Z c385e32e-5832-11e7-8c4f-673b524cf783 Task timed out after 10.00 seconds"
}
But my Slack POST request gets sent and I see the message in the channel.
How do I send the POST request and then wait for it to return, and then exit the lambda function with a custom response message?
Thanks for the help!
Put the callback into the callback of the post method
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
});
} else {
return callback(err);
}
});

How to send cookies with node-fetch?

I've got nodejs application which handles user's requests and receives cookies which i want to proxy to internal API service. How to approach this by using node-fetch?
Don't offer superagent please.
You should be able to pass along cookies by setting it in the header of your request:
const opts = {
headers: {
cookie: 'accessToken=1234abc; userId=1234'
}
};
const result = await fetch(`/some/url`, opts);
Read & write cookies like a bot
async function login() {
return fetch('<some_url>/login', {
'headers': {
'accept': '*/*',
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'cookie': '',
},
'body': 'username=foo&password=bar',
'method': 'POST',
});
}
(async() => {
const loginResponse = await login();
const loginCookies = parseCookies(loginResponse);
})();
You may want to include: accept-language, user-agent, referer, accept-encoding, etc. (check a sample request on your Chrome DevTools via the Network tab)
For some reason the resulting cookies of node-fetch requests are not compatible with new requests, but we can parse them like this:
function parseCookies(response) {
const raw = response.headers.raw()['set-cookie'];
return raw.map((entry) => {
const parts = entry.split(';');
const cookiePart = parts[0];
return cookiePart;
}).join(';');
}
Pass cookies in your future requests through the same headers:
return fetch('<some_url>/dashboard', {
'headers': {
'accept': '*/*',
'cookie': parsedCookies,
},
'method': 'GET',
});
For simple, you can write a middleware which will include the cookies to global.fetch, like below.
const realFetch = fetch;
function cookieFetch(fetch, cookie) {
return (url, opts) => {
opts = opts || {};
return fetch(url, Object.assign(opts, {
headers: Object.assign(opts.headers || {}, { cookie })
}));
};
}
function middleware(req, res, next) {
const kuki = req.headers.cookie;
global.fetch = kuki ?
cookieFetch(realFetch, kuki) :
realFetch;
next();
}
module.exports = middleware;

Categories