I'm trying to run a simple script on AWS Lambda using Serverless to push it, the script fetches a url and returns it (a proxy), for some reason I can't see the response.
The script in question:
'use strict';
let axios = require('axios')
module.exports.hello = async (event, context) => {
let res = await axios.get('http://example.com')
return {
statusCode: 200,
body: JSON.stringify({
message: res,
input: event,
}),
}
};
My serverless YML:
service: get-soundcloud-tracks
provider:
name: aws
runtime: nodejs8.10
profile: home
functions:
hello:
handler: handler.hello
events:
- http:
path: users/create
method: get
cors: true
The solution was changing res to res.data inside the JSON.stringify
Related
I am running a graphql server using serverless offline and trying to connect to the server with Aollo Studio. The weirdest bug is that it was connecting properly a week back and now the exact same server is not connecting. The same thing I have deployed on the AWS and Aollo Studio is able to connect to the deployed server. Any idea what could be the reason for it?
Environment
I am on Macbook m1 pro.
Node Version 15.6
As you can in the config file I have started a playground as well on this path http://localhost:3000/dev/playground which I am able to access but this playground is also not connecting the server.
One thing which I have observed is my local network IP URL like http://192.168.1.3:3000/dev/playground is also not working when I am trying to visit so maybe some kind of network issue might be there.
But when I run something like a React App I am able to access it on this http://192.168.1.3:3000
My serverless.yml looks like below
service: serverless-graphql-rds
frameworkVersion: "3.8.0"
provider:
name: aws
runtime: nodejs14.x
stage: ${env:PROVIDER_STAGE}
region: ${env:REGION}
environment:
JWT_SECRET: ${env:JWT_SECRET}
DATABASE_URL: ${env:DATABASE_URL}
REDIRECT_TO_DASHBOARD: ${env:REDIRECT_TO_DASHBOARD}
HUB_SPOT_CLIENT_ID: ${env:HUB_SPOT_CLIENT_ID}
HUB_SPOT_CLIENT_SECRET: ${env:HUB_SPOT_CLIENT_SECRET}
HUB_SPOT_REDIRECT_URI: ${env:HUB_SPOT_REDIRECT_URI}
plugins:
- serverless-plugin-typescript
- serverless-offline
package:
patterns:
- "migrations/**"
- "**.js"
- "config"
custom:
serverless-offline:
httpPort: ${env:httpPort, 3000}
lambdaPort: ${env:lambdaPort, 3002}
serverless-plugin-typescript:
tsConfigFileLocation: "./tsconfig.json"
functions:
graphql:
handler: server.handler
events:
- http:
path: graphql
method: post
cors: true
playground:
handler: server.playgroundHandler
events:
- http:
path: playground
method: get
cors: true
oauth-callback:
handler: ./rest-apis-handlers/oauth-callback.handler
events:
- http:
path: oauth-callback
method: get
cors: true
And the file server.ts looks like below which contains the handler function
import { ApolloError, ApolloServer } from "apollo-server-lambda";
import lambdaPlayground from "graphql-playground-middleware-lambda";
import { verifyToken } from "./common/jwt";
import { useContext } from "./core/context";
import resolvers from "./graphql/resolvers";
import typeDefs from "./graphql/schema";
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ event, context, express }) => {
const auth = express.req.headers["authorization"] as string;
if (auth) {
const [_, token] = auth.split("Bearer ");
try {
const user = verifyToken(token);
if (user) {
return useContext({
type: "user",
properties: {
...user,
},
});
} else {
throw new ApolloError("Not authenticated", "UNAUTHENTICATED");
}
} catch (ex) {}
}
return useContext({
type: "public",
});
},
});
export const handler = server.createHandler({});
// for local endpointURL is /graphql and for prod it is /stage/graphql
export const playgroundHandler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
return lambdaPlayground({
endpoint: process.env.REACT_APP_GRAPHQL_ENDPOINT,
})(event, context, callback);
};
After struggling for 3 days today I finally figured it out.
As I am using the serverless-offline package to run the local graphql server and due to the node version it requires less than 15. Here is the details discussion on it.
So just downgrade the node version to anything < 15 and it will work.
I have a Nestjs application, which I am deploying to AWS Lambda and Serverless.
In the root of my application I have a serverless.ts -
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import serverlessExpress from '#vendia/serverless-express';
import { Handler, Callback, Context } from 'aws-lambda';
let server: Handler;
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.init();
const expressApp = app.getHttpAdapter().getInstance();
return serverlessExpress({ app: expressApp });
}
bootstrap();
export const handler: Handler = async (
event: any,
context: Context,
callback: Callback,
) => {
server = server ?? (await bootstrap());
return server(event, context, callback);
};
serverless.yaml -
service: prism-backend
frameworkVersion: '3'
useDotenv: true
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs14.x
region: ap-south-1
functions:
main:
handler: dist/serverless.handler
events:
- http:
method: ANY
path: /
- http:
method: ANY
path: '{proxy+}'
package:
patterns:
- '!node_modules/**'
When I deploy this using 'serverless deploy` deployment is successful and I get generated endpoints.
When I access the default endpoint, I always get this in my browser -
{message: 'Internal Server Error' }
Do I need to configure something else? How can I resolve this?
Have you tried to use API Gateway test feature to call your function ?
Do you have a controller listening to the default endpoint of your application ? Watchout that by default, serverless appends the stage to your API. By default, the stage is dev. So your default endpoint should be "/dev/
Have you taken a look into cloudwatch already ? If so, could you please paste the logs here ?
The event body is not getting encoded while invoking sam local start-api and sending a multipart request but it's being encoded in the cloud. So, I'd like to have the same behavior in my local environment.
Steps to reproduce the issue:
Create the Hello World project provided by sam init
Add a post method
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: post
ContentHandling: CONVERT_TO_BINARY # This is not doing the magic as I was expecting
BinaryMediaTypes:
- "*/*"
- multipart/form-data
Return the isBase64Encoded in the handler.
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
'statusCode': 200,
'body': JSON.stringify({
message: event.isBase64Encoded,
// location: ret.data.trim()
})
}
} catch (err) {
console.log(err);
return err;
}
return response
};
Perform the HTTP request:
curl --location --request POST 'http://127.0.0.1:3000/hello' \
--header 'saa: csv/csv-file' \
--form 'foo=#/home/user/csv-file.csv'
The response is always the same:
{
"message": false
}
I've tried to use the proxy integration but it didn't work.
My workaround is to have something like this in the handler:
const csvString = event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('utf-8') : event.body;
I have a problem with correcltly returning the response of a Lambda function that I deploy using the Serverless framework:
module.exports.hello = async (event, context, callback) => {
const content = fs.readFileSync('./cn23_template.html', 'utf-8')
const Vue = require('vue')
const app = new Vue({
template: content,
data: function () {
return event
}
})
const renderer = require('vue-server-renderer').createRenderer()
const html = await renderer.renderToString(app)
const browser = await chromium.puppeteer.launch({
// Required
executablePath: await chromium.executablePath,
// Optional
args: chromium.args,
defaultViewport: chromium.defaultViewport,
headless: chromium.headless || true
});
const page = await browser.newPage();
await page.setContent(html);
let pdf = await page.pdf({ pageRanges: '1', format: 'a4', printBackground: true });
await browser.close();
return {
statusCode: 200,
headers: {
'Content-Type': 'application/pdf',
'Content-Length': pdf.length
},
body: pdf ? pdf.toString('base64') : null,
isBase64Encoded: true
}
}
My serverless.yml file:
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: post
integration: lambda
response:
headers:
Content-Type: "'Test'"
Cache-Control: "'max-age=120'"
The problem is that what I return from the function is not correctly mapped to the response. The response doesn't include the statusCode and headers, it just uses the whole returned object as body of the response.
Besides this, the headers as configured in the .yml are also not being used.
It seems like a very silly mistake, but I'm just doing exactly what is inside the Serverless API gateway docs.
So my question is: How do I properly configure the response properties so that the HTTP request gives the correct response using the Serverless framework?
You need to configure your lambda function to be invoked as a lambda-proxy event.
See Serverless Example Lambda Proxy
I have a serverless function, you can find the code below, and this function is deployed on aws lambda using the serverless framework. from within this function i call an external web api.
When i do serverless invoke -f my_func i get the expected response. but if i run a curl command it fails and i get {"message": "Internal server error"}
this is my curl command:
curl -X GET \
https://0wb3echzu8.execute-api.us-east-1.amazonaws.com/dev/my_func \
-H 'cache-control: no-cache' \
-H 'postman-token: 1fc551c0-7010-e1e3-7287-6a0d82d1e91a'
this is my code:
var request = require("request");
var options = { method: 'GET',
url: 'https://api.coinmarketcap.com/v2/listings/',
headers:
{ 'postman-token': '090e284e-62ad-29f0-0f10-92ae14656a37',
'cache-control': 'no-cache' } };
module.exports.my_func = (event, context, callback) => {
request(options, function (error, response, body) {
if (error) { console.log(error); callback(null, error) }
console.log(body)
callback(null, body)
});
};
this is the serverless.yml file:
service: aws-nodejs
app: sonarsic
tenant: vincent
provider:
name: aws
runtime: nodejs6.10
functions:
my_func:
handler: handler.my_func
events:
- http:
path: my_func
method: get
cors: true
It must have to do something with the calling of the web api in my function. if i don't call a web api it works fine.
If I check the logs via serverless logs -f my_func i only get the logs of the calls that worked using serverless invoke.
what can i do to find out what is going wrong inside my function when making a curl command?
adding cors:true to the http section does not solve the problem
cloudwatch is attached to aws lambda but it seems there is nothing written to:
After some discussion on chat, we discovered that statusCode on the response body is missing
let request = require('request');
let options = {
method: 'GET',
url: 'https://api.coinmarketcap.com/v2/listings/',
headers: {
'postman-token': '090e284e-62ad-29f0-0f10-92ae14656a37',
'cache-control': 'no-cache',
},
};
module.exports.my_func = (event, context, callback) => {
request(options, (error, response, body) => {
if (error) { console.log(error); callback(null, error) }
console.log(body)
callback(null, { body, statusCode: 200 }) // response statusCode added
});
};