dougmoscrop / serverless-http Goto Github PK
View Code? Open in Web Editor NEWUse your existing middleware framework (e.g. Express, Koa) in AWS Lambda ๐
License: Other
Use your existing middleware framework (e.g. Express, Koa) in AWS Lambda ๐
License: Other
I think it would be great as by supporting all providers this project has the potential to become the one serverless framework to rule them all. I don't know if this would fit the current scope of the project though.
Otherwise I am thinking maybe of making another project using this one as its "lambda" adapter.
The current assumption is that either (for AWS API Gateway):
/
basePath; or/
path prefix) and remote development paths (/basePath/stage
path prefix)Meaning that by default, an app that works locally or with /
basePath will get errors like:
Cannot GET /geo/v1/geoPlaces/findTopCityLikeByPopulation
When deployed to geo:*
basePath.
This is similar issue to CodeGenieApp/serverless-express#86 and claudiajs/claudia#170
Currently I use a workaround like this:
module.exports.handler = async (event, context) => {
// FIXME: Ugly workaround for https://github.com/awslabs/aws-serverless-express/issues/86 and https://github.com/claudiajs/claudia/issues/170
event.path = event.path.replace(/^\/geo\/v[^/]+/, '')
return await handler(event, context)
}
It'd be great if this use case can be explicitly supported by a configuration option, or at least, mentioned in the README.md
/ documentation for AWS provider.
I have a post method defined in the following way
app.post('/upload', function (req, res) {
res.send(req.body);
}
If i curl the method and pass a json object it works fine. I cannot get it work though with serverless invoke local --path event.json
.
Here is my event.json file
{
"path": "/upload",
"httpMethod": "POST",
"headers": {
"Content-Type": "application-json"
},
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"body": "{\"image\": \"asdf\"}",
"isBase64Encoded": false
}
I also tried giving an object for the body but i get the following error
{
"errorMessage": "Unexpected event.body type: object",
"errorType": "Error",
"stackTrace": [
"getBody (/var/task/node_modules/serverless-http/lib/request.js:17:9)",
"new ServerlessRequest (/var/task/node_modules/serverless-http/lib/request.js:29:18)",
"Promise.resolve.then (/var/task/node_modules/serverless-http/serverless-http.js:30:25)"
]
}
How should the body look like in the event.json so that i get a json object inside my handler?
Seems to me that aws maps cognito's claims to request's body and turns it into a object.
Therefore, this line is executed.
Error:
{
"errorMessage": "Unexpected event.body type: object",
"errorType": "Error",
"stackTrace": [
"getBody (/var/task/node_modules/serverless-http/lib/request.js:17:9)",
"new ServerlessRequest (/var/task/node_modules/serverless-http/lib/request.js:29:18)",
"Promise.resolve.then (/var/task/node_modules/serverless-http/serverless-http.js:30:25)"
]
}
I had two serverless projects using nodejs + express, one locked to version 1.5.3
and the other one was using the latest version 1.7.1
. The one with the newer version was making api gateway(AWS) to return a 502 error page with the header X-Cache: Error from Cloudfront
which is usually related to the fact that the function handler is not returning the correct response payload structure.
The last modified date is always last-modified: Tue, 01 Jan 1980 00:00:00 GMT, so it use caches all the time, how to change the last-modified time to real file update time
Tradeoffs between node-mocks-http and just using the 'actual' objects.
This may be something super simple, but I can't figure it out.
Instead of pointing my handler to a wrapped function can I run a function before it?
I need to make an ssm call to get a variable for what I'm passing into express.
export default endpoint = () => serverless(app)
The wrapper is really nice! Will it support LoopBack 4 one day?
We use stage variables to configure remote endpoints that are used by our lambda. Currently there is no way to access those from express using this library.
request.originalUrl does not include the stage path segment from API Gateway.
Example of expected value: /api-gateway-stage/my/route
Example of actual value: /my/route
The correct path value is indicated in event.requestContext.path
Should probably copy from here as a starting point:
https://github.com/dougmoscrop/serverless-plugin-split-stacks/blob/master/__tests__/_integration.js
I would like to be able to run a bunch of tests to confirm that it works within the Lambda environment as opposed to just the local code.
Has anyone else tried this?
Just want to raise this so it is here for future reference, this is the same as CodeGenieApp/serverless-express#196. Application load balancer supports this via a configuration option in the backend, and api gateway has recently got support for it. Pretty nasty but unfortunately pretty common in a lot of codebases these days.
For application load balancer this is an example event:
multiValueHeaders: {
accept: [ 'application/json, text/javascript, */*; q=0.01' ],
'accept-encoding': [ 'gzip, deflate, br' ],
'accept-language': [ 'en-GB,en-US;q=0.9,en;q=0.8' ],
...
}
multiValueQueryStringParameters: {
'dateRange%5B%5D': [ '2018-12-14', '2018-12-14' ],
roleId: [ '0' ]
}
The behaviour of my express app currently with that same request is:
req.query == {
'dateRange': ['2018-12-14', '2018-12-14' ],
'roleId': '0'
}
Not entirely sure how you would go about handling this in a useful manner.
I would probably be happy if there was some way of way of pre-processing the event so I have a hook to transform the data to ensure that ServerlessRequest behaves itself.
Hi,
Is this tested on node-6.9.1? I am trying this with a koa-1.4 app, with a simple code that throws a 401 error (this.throw(`Unauthorized: ${this.request.method} ${this.request.url}`, 401);
), but this error never reaches the aws lambda service. I always get a timeout error from aws lamda.
Thank you for your help!
Tamas
How I do to works in all project replace headers response:, I try:
module.exports.handler = serverless(app, {
response: function (response, event, context) {
// Not working
response.headers['Access-Control-Allow-Origin'] = '*';
}
});
But, if I do for each function it works:
function (req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.status(200).json('OK');
}
Can you help me?
Hi.
I love this package. ๐ฅ
I'm getting 404 for all endpoints after upgrade to 1.5.4.
The cause is the new way to find the path before pass it to the request (5fc27b2#diff-4f98820926ab9e533c0e3ef619e49f66R10)
The follow line always return '/' because the route is in event.path.
event.requestContext.path = event.requestContext.path || '/';
I moved on with this workaround:
'use strict';
import serverless from 'serverless-http';
import app from './app/main';
export const handler = serverless(app, {
request: function(request, event, context) {
if (process.env.IS_OFFLINE) {
event.requestContext.path = event.path || event.requestContext.path;
request.url = event.requestContext.path;
}
}
});
Did I missed something?
OS: Ubuntu 16.04
NodeJS: v6.9.1
serverless: 1.26.1
serverless-offline: v3.18.0
Hi,
I was playing around with serverless-http and I love the idea of it!
I did however, find an issue when trying to use inversify-express-utils.
Currently, using serverless-http the only way I can send data to my controllers is through using query strings.
Even when using body-parser as middleware to express, when passing the body into the XHR that goes through serverless-http, it ends up getting to the controller as an empty object ({}).
If I run my server locally without using my configured lambda.ts, I am able to pass data via body to my controllers.
Any ideas on how to solve this issue?
Thanks very much!
Server.ts:
import 'reflect-metadata';
import * as express from 'express';
import { json } from 'body-parser';
import { InversifyExpressServer } from 'inversify-express-utils';
import { InjectModule } from './Injector';
export class Server {
public app: express.Application;
private _server: InversifyExpressServer;
constructor() {
this._server = new InversifyExpressServer(InjectModule);
this.app = this._server.setConfig((app) => {
app.use(json());
}).build();
this.app.listen(3000, () => {
console.log('listening on port 3000');
});
}
}
lambda.ts:
import { Server } from './Server';
import * as serverless from 'serverless-http';
const server = new Server();
module.exports.handler = serverless(server.app);
Can you please provide some more detailed example?
I still don't get how can I define a "catch all" handler in my serverless.yml
Getting spammed with this error, not really sure why. Seems to happen when there might be an error, and then it just loops repeatedly
Can't be run locally, perhaps event
is empty
serverless invoke local --function handler --log
Type Error ---------------------------------------------
Cannot read property 'body' of undefined
This pops up when combined with morgan.
Right now the request ID being used is context.awsRequestId
, but that is the request ID of the Lambda invocation, not the API Gateway request. The correct request ID is event.requestContext.requestId
.
Right now only express/connect/koa are supported for handling the request. However, I think any function that has a valid signature for http.createServer()
should work as a handler.
From quickly looking at the code it could be a simple additional block in getHandler()
if (typeof app === 'function') {
return app;
}
What do you think?
Hi,
This is a really nice tool and love the idea of just mocking the request and response layers, but, for some reason with serverless-offline plugin in serverless framework i always get empty body in response, also the write function in response object never gets called, (although the assignSocket stub does get all the data).
Typically on an HTTP Express setup you would connect to the database before you start listening on a port. But since each request requires a brand new Lambda request, integrating MongoDb requires a connection (possibly cached) to MongoDB per Lambda request.
What is the recommended place in this library to initialize this MongoDB connection before the request is handled?
I was thinking that the MongoDB connection could be injected into the request object via an Express middleware, but I'm not sure if it's a good idea.
I am using the bugsnag
error middleware to report errors with Express.
The middleware makes serverless-http
crash, as req.connection.address()
is not defined.
Maybe serverless-http
could implement this method, as other Express/Connect middlewares probably use it?
See bugsnag/bugsnag-node#129 for details about the error.
Use publish, etc.
The event provided by AWS contains a generated request ID in the request context. It would be interesting to have that automatically injected as a header. The de facto standard would be X-Request-ID
(ref. https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Common_non-standard_request_fields)
In this case, Amazon!
First, thanks for this library! I need to access some attributes from the API Gateway: the context.requestId
and context. requestTime
. I was able to fetch the get the requestId
by using:
module.exports.handler` = serverless(app, {
request: (req, event, context) => {
req.context = event.requestContext;
req.awsRequestId = context.awsRequestId;
req.eventAws = event;
req.contextAws = context;
}
});
However, I am not seeing the dates. I show al the context, and I got the following:
{
"resource": "/boosts",
"path": "/boosts",
"httpMethod": "POST",
"headers": {},
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"path": "/boosts",
"accountId": "xxx",
"resourceId": "xxx",
"stage": "test-invoke-stage",
"requestId": "test-invoke-request",
"identity": {
"cognitoIdentityPoolId": null,
"cognitoIdentityId": null,
"apiKey": "test-invoke-api-key",
"cognitoAuthenticationType": null,
"userArn": "arn:aws:iam::xxx:user/Marcelo_Olivas",
"apiKeyId": "test-invoke-api-key-id",
"userAgent": "userAgent",
"accountId": "xxx",
"caller": "xxx",
"sourceIp": "sourceIp",
"accessKey": "xxx",
"cognitoAuthenticationProvider": null,
"user": "xxx"
},
"resourcePath": "/boosts",
"httpMethod": "POST",
"extendedRequestId": "test-invoke-extendedRequestId",
"apiId": "xxx"
},
"body": "{\n\t\"attribute\": \"another.attribute\", \n\t\"boost\" : 1, \n\t\"status\" : \"analysis\"\n}",
"isBase64Encoded": false
}
Now for the events, I was able to get:
{
"callbackWaitsForEmptyEventLoop": false,
"logGroupName": "/aws/lambda/three-eyed-raven-dev-createBoost",
"logStreamName": "2018/05/11/[$LATEST]9be2c7eadced41b487b87d1a1885d761",
"functionName": "three-eyed-raven-dev-createBoost",
"memoryLimitInMB": "1024",
"functionVersion": "$LATEST",
"invokeid": "xxx-yyy-11e8-aaa6-aaa",
"awsRequestId": "xxx-yyy-zzz",
"invokedFunctionArn": "arn:aws:lambda:us-east-1:aabbvv:function:three-eyed-raven-dev-createBoost"
}
Thanks for the help, and again...thanks for the library!
When attempting to use connect-dynamodb
, a DynamoDB-backed session middleware for express, the AWS Lambda function times out according to the logs.
I traced it down and it seems to be calling callback properly, so not sure exactly what's happening. I did not encounter this issue when I switched over to https://github.com/awslabs/aws-serverless-express.
I'm not sure if this is an intended use case for this library, but if it is I have a snapshot of the code before switching over: https://github.com/hbcwr/pco-schedule-swapper/tree/bookmark/serverless-http.
I'm trying to create a little app that processes images of celebrities. I'm utilizing the multer library to handle my image uploads. When tested with only express, the image uploads work great, but when wrapped with serverless-http
the image uploads are corrupted. Here is a sample repository that demonstrates the problem. You can test with serverless-offline
, but the problem also persists when released to AWS as well. Thanks for any insight you can provide!
Hi,
We are using APIG authorizers in our project and they were not working with serverless http, the problem is that parameters coming from APIG authorizers are coming in event.requestContext object, I can do the fixes to current Request object and send a PR.
Suggestion:
in request object the event.requestContext.auhtorizers is mapped to req.authorizers
EDIT: will close this, just figured out that I can use transformers doing this. maybe there could be a more accurate example made thou for using transformers, and that documentation should be put to the front page :)
Hi,
Is serverless-http able to serve jpg/png images? I have some image assets that I deployed with my application but it's not rendering the img/png correctly. The content-type shows image/jpeg and it's serving properly, getting 200 but not displaying correctly. Is this an encoding issue.
Any help would be greatly appreciated.
How can we set this up with SailsJS. Following is my app.js file
var sails = sails = require('sails');
var rc = require('sails/accessible/rc');
const sls = require('serverless-http');
// Start server
sails.lift(rc('sails'));
module.exports.server = sls(sails);
Throws the following error
{"errorMessage":"Error while loading app","errorType":"Error","stackTrace":["Error: serverless-http only supports koa, express/connect or a generic http listener","at getHandler (/Volumes/SailsApp/node_modules/serverless-http/lib/get-handler.js:16:9)","at module.exports
using
module.exports.handler = serverless(app);
I get the following error:
2018-03-18T06:08:03.347Z - error: /home/ajmwagar/usr/dev/git/outrigger/node_modules/serverless-http/serverless-http.js:58
callback(e);
^
TypeError: callback is not a function
at process.nextTick (/home/ajmwagar/usr/dev/git/outrigger/node_modules/serverless-http/serverless-http.js:58:9)
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
2018-03-18T06:08:03.355Z - info: Execution took 12 ms, finished with status: 'crash'
2018-03-18T06:08:03.374Z - error: Function worker crashed with exit code: 1
I will look into a fix.
Thanks for maintaining such a great library.
Hello! I am trying to use serverless-http
to wrap a Koa image manipulation API and deploy it on AWS Lambda with APIGW. I ran into some problems returning a binary payload so I tried base64 encoding my body response and then do this in the handler:
const serverless = require('serverless-http');
const server = require('./server');
module.exports.handler = serverless(server, {
request: function(request, event, context) {
console.log(request);
},
response: function(response, event, context) {
// Set isBase64Encoded true so API gateway knows the body needs to be decoded
response.isBase64Encoded = true;
console.log(response);
}
});
I've setup CloudWatch logging and it correctly logs the response object having the isBase64Encoded
field set. However when I invoke the function using awscli I get:
{
"isBase64Encoded":false,
"statusCode":200,
"headers": {...},
"body": "..."
}
Any ideas why my response modification is not present in the invocation result?
I'm print all ctx but can not find body
serverless:
// index.js
const serverless = require('serverless-http');
var bodyParser = require('koa-bodyparser');
const AWS = require('aws-sdk');
const Router = require('koa-router');
var router = new Router();
const Koa = require('koa');
const app = new Koa();
// register your middleware as normal
app.use(bodyParser());
app
.use(router.routes())
.use(router.allowedMethods());
router
.post('/test', async (ctx, next) => {
ctx.body = [{"name": 1, userId: "099999"}, ctx];
})
module.exports.handler = serverless(app);
request body:
{"userId": 123}
but ctx:
[
{
"name": 1,
"userId": "099999"
},
{
"request": {
"method": "POST",
"url": "/test",
"header": {
"content-length": 10,
"x-request-id": "577c76fd-bb37-11e8-8e29-9757c42b128b"
}
},
"response": {
"status": 200,
"message": "OK",
"header": {
"content-type": "application/json; charset=utf-8"
}
},
"app": {
"subdomainOffset": 2,
"proxy": false,
"env": "development"
},
"originalUrl": "/test",
"req": "<original node req>",
"res": "<original node res>",
"socket": "<original node socket>"
}
]
p/s: using LAMBDA_PROXY
Is there any interest in adding typescript definition files to this project?
I currently have a working definitions file that I submitted to DefinitelyTyped but perhaps it would be better to make it more generic move it here instead.
DefinitelyTyped/DefinitelyTyped#31284
In the response object, the code calls getString
with 2 arguments:
serverless-http/lib/response.js
Line 47 in 1470fe1
getString
only takes 1 argument:serverless-http/lib/get-string.js
Lines 3 to 11 in 1470fe1
why always call "/" route after upgrade to 1.5.4 ?
my code is :
const express = require('express');
const serverless = require('serverless-http');
const app = express()
//first route
//always call this
app.get('/', function (req, res) {
res.send('Hello World!')
})
//the second route
//never call this
app.get('/test', function (req, res) {
res.send('Hello Test!')
})
module.exports.handler = serverless(app);
Event can contain important information especially when utilizing custom authorizers. I recommend exposing at least the event
on the request object so it can be accessed by middle.
As an example, in our fork of this repo we're doing:
// in lib/request.js
Object.assign(this, {
ip: event.requestContext.identity.sourceIp,
complete: true,
apiGateway: { event },
httpVersion: '1.1',
httpVersionMajor: '1',
httpVersionMinor: '1',
method: event.httpMethod,
headers: headers,
url: url.format({
pathname: event.path,
query: event.queryStringParameters
})
});
If client issues requests with if-modified-since': 'Tue, 01 Jan 1980 00:00:00 GMT'
i.e. to check whether resource have had been updated,
there is no content-type header generated, but binary handler still being called:
binary: headers => {
const ct = headers['content-type'];
if (ct === undefined) {
console.error("No content-type header: " + JSON.stringify(headers));
return false;
}
return String(ct).match(/text\/.*/) || ct == "application/json" ? false : true;
},
resulting in multiple errors "No content-type header" in logs/CloudWatch.
Hi. Do you have any plans on supporting lambda integration? I would like to use the User Pool as my authentication of the API and I need to specify the integration type as lambda
to use the User Pool claims.
Here is my partial serverless.yml
definition.
functions:
# Test API
test:
handler: handler.main
events:
- http:
path: / # this matches the base path
method: ANY
integration: lambda
authorizer:
arn: ${self:custom.userPoolARN}
claims:
- custom:organization
- cognito:groups
- http:
path: /{any+} # this matches any path, the token 'any' doesn't mean anything special
method: ANY
integration: lambda
authorizer:
arn: ${self:custom.userPoolARN}
claims:
- custom:organization
- cognito:groups
Here is my handler file.
'use strict';
import serverless from 'serverless-http';
import express from 'express';
const app = express()
app.get('/', function (req, res) {
res.send('Hello World!')
})
module.exports.main = serverless(app);
When I try to deploy it using the above configuration, the following output is return on the API Gateway.
{"statusCode":404,"headers":{"x-powered-by":"Express","content-security-policy":"default-src 'self'","x-content-type-options":"nosniff","content-type":"text/html; charset=utf-8","content-length":155},"body":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET [object%20Object]</pre>\n</body>\n</html>\n"}
The endpoint for lambdas will have the stage as their baseUrl. However, currently, requests sent to the wrapped express app don't include this. This means that redirects will not work as expected.
e.g. a request to an express.static directory path without the trailing "/" attempts to redirect to the same path appended with "/". But when wrapped in serverless-http the redirect is wrong.
https://{host}/{stage}/staticdir => https://{host}/staticdir/
Your sample code should be as such
const wrapped = serverless(app);
module.exports.handler = async (evt, ctx) => {
return wrapped(evt, ctx);
}
It would be nice if the original event from aws, the one that is given to the aws lambda handler, get exposed in the 'HttpRequest' object, so one could have access to it through context.originalEvent
, in a koa example.
I had to write a wrapper like this to have access to the original event:
function injectEvent(event, context, callback) {
const app = new Koa();
app.context.originalEvent = event;
serverlessHttp(app)(event, context, callback)
}
I had expected that the framework somehow provided this but it is not the case. Every framework gives us the original http request so I think that's the right place. Well, if I'm wrong please tell me why.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.