sveltejs / sapper

The next small thing in web development, powered by Svelte
https://sapper.svelte.dev
MIT License
6.99k stars 433 forks source link

Enable sapper to run on AWS Lambda #356

Closed ispyinternet closed 4 years ago

ispyinternet commented 6 years ago

I've been able to setup sapper on AWS lambda without too much difficulty. Currently it can only be done using express and not polka as the server has to be wrapped using: https://github.com/awslabs/aws-serverless-express

However there would be a change required to sapper middleware to support this, but no sure exactly how to set this up.

The issue is that for the Lambda environment, the node server is setup to run on a file socket and not your traditional TCP port (e.g. 3000).

In sapper middleware this.fetch relies on node-fetch, and node-fetch makes local requests using the TCP port ( i.e. fetch('127.0.0.1:3000/page'). But in lambda we need to be able to fetch from the socket. node-fetch appear to be unwilling to support sockets, but got does (https://github.com/sindresorhus/got#comparison)

This would also need a mechanism to indicate to sapper whether to use or not, in my hack I use an environment variable.

https://github.com/sveltejs/sapper/blob/master/src/middleware.ts#L348-L382

if( parsed.href.substr(7,9) == '127.0.0.1'
   && process.env.HTTP_SOCKET ) {
    let urlPath = parsed.pathname + parsed.hash;
    let socket = process.env.HTTP_SOCKET;;
    return got(`unix:${socket}:${urlPath}`, opts).then( res => ({ json: () => JSON.parse(res.body)}));
}

If the authors / maintainers could work out how best to integrate and support this that would be great

ispyinternet commented 6 years ago

I have a working solution, that needs some more experienced eyes to look through, although @Rich-Harris has indicated he's not sure got is the route he would want to take. However it does give some insight to the issue and what the solution needs to address:

https://github.com/sveltejs/sapper/compare/master...ispyinternet:master

ispyinternet commented 6 years ago

I've worked on this issue a little more. My initial fix resorted to replacing node-fetch with got as got supports fetch over socket files (otherwise known as IPC connection https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections), but it was pointed out to me that fetch needs to fully align on browser and server, which is the goal of node-fetch. However node-fetch will not implement IPC support.

It seems the only option, if lambda support is to be provided (and I for one am keen to push this through), is to fork node-fetch and add IPC support.

I have made a first attempt at this. The only tests that seem to fail are some of the redirect tests. This is out of my depth now and I would really hope for some support with this. @lukeed, I'm suggesting your the man for the job (given your track record in this field) - but other experienced devs welcome! I dont think @Rich-Harris has a great deal of experience in this area, and I cant see it making his priority list without some support.

I know its a bit of stinky situation, but lambda is such a big target platform.

Forked node-fetch: https://github.com/ispyinternet/node-fetch Forked sapper: https://github.com/ispyinternet/sapper A template that will deploy the above forks: https://github.com/ispyinternet/sapper-lambda-template (sample deployed at https://sapper-lambda.cron.tv)

regular node-fetch over tcp port is unaffected: npm run test To see the failing tests when using sockets: npm run test-socket

I've tried to make it work to the best of my abilities and im not in anyway precious about my work so if anyone can review, and support I will be very grateful.

Thanks.

alexdilley commented 5 years ago

As we're a few months on, is there now a less "forky" :) way of getting Sapper to play nicely as/in a Lambda function? (There may be more interest in this given the surge in interest following Svelte v3 announcement.)

benmccann commented 4 years ago

You don't necessarily have to use sockets on Lambda do you? I think the issue is just that you can't make a request to 127.0.0.1. But I do think you can make HTTP requests in general. In which case, I think that the easier fix might be to provide an alias which switches 127.0.0.1 for the public hostname

benmccann commented 4 years ago

I've sent https://github.com/sveltejs/sapper/pull/1215 which would allow you to run Sapper on Lambda by letting you route web requests to your publicly facing address when fetch is called.

Note, however, that if that endpoint is also a Lambda (likely one running on the same server), I believe that would result a charge for the initial SSR request as well as the subsequent fetch request. For Lambda, if you are hosting your server end points in the same codebase, in the longer-term you probably would want to just execute the function containing the code for your endpoint rather than making a new HTTP request. It'd be better for latency as well as reducing your lambda bill. However, https://github.com/sveltejs/sapper/pull/1215 will at least make it possible to run on Lambda now with that caveat.

benmccann commented 4 years ago

I recently found out about now-sapper. It builds and deploys Sapper to Vercel with the backend being hosted on AWS Lambda. Perhaps you could use it to deploy your projects.

ajbouh commented 4 years ago

I believe that vercel actually runs a webserver on localhost and pipes the request to it. This approach might add a small amount of latency, but should make it much easier to implement fetch.

On Thu, May 21, 2020, 06:44 Ben McCann notifications@github.com wrote:

I recently found out about now-sapper https://github.com/thgh/now-sapper. It builds and deploys Sapper to Vercel with the backend being hosted on AWS Lambda. Perhaps you could use it to deploy your projects. Or figure out what they're doing differently that makes deployment to Lambda work for them and not you

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/sveltejs/sapper/issues/356#issuecomment-632094284, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAABZHO44TKEKM6UHJ7GE2TRSUV4HANCNFSM4FPKR5YA .

ryaninvents commented 4 years ago

Would it be possible to overwrite the preload_context.fetch using middleware? This could be a flexible way to unblock people without adding complexity.

EDIT: As a stopgap, I'd imagine it's possible to define session.fetch and use that instead.

ryaninvents commented 4 years ago

I was just thinking about this today; wouldn't it be possible to update rollup.config.js or webpack.config.js to alias a custom module instead of node-fetch? That way you could rewrite hardcoded 127.0.0.1 to point to the Unix socket (as mentioned in this comment) and use got for its socket compatibility.

benmccann commented 4 years ago

We have to decide how to handle configuration in Sapper in order to know how to pass in custom functions

ispyinternet commented 4 years ago

Basically the problem is if you use https://github.com/awslabs/aws-serverless-express package to proxy the incoming request to the Sapper server, the package uses sockets which will fail on local requests to 127.0.0.1:PORT.

You can easily modify the aws-serverless-express package to use ports instead of unix sockets.

https://github.com/awslabs/aws-serverless-express/blob/master/src/index.js#L56 comment out the socket property and uncomment the host, protocol, hostname, port properties

antony commented 4 years ago

Closing as kit will be serverless first!