dougmoscrop / serverless-http

Use your existing middleware framework (e.g. Express, Koa) in AWS Lambda 🎉
Other
1.74k stars 167 forks source link

Add path-parameters #127

Closed DeltaByte closed 6 months ago

DeltaByte commented 5 years ago

Do people think it would be worth mapping Lambda's pathParameters object to the Request object? Mostly so that it is easy to provide some easy access to URL params from API-Gateway, ideally the end result would be to bind it in ctx within koa to mimic behavior from koa-router

dougmoscrop commented 5 years ago

So the problem is that not all frameworks actually "forward" the req/res (from core http) in - they sometimes create their own or modify the prototype of the object. It's not a blocker but something to consider - the initial goal of this library was to make it transparent that your code was running in Lambda, and I appreciate all abstractions fail at some point - just have to make sure it's worth it, and where possible I prefer to keep things simple and let users complicate it (so e.g. just give access to the event/context objects, and any reusable logic can be a standalone piece of koa/express middleware.)

I personally only mount either /{proxy+} or /fixed-base/{proxy+} - curious what your use case is?

DeltaByte commented 5 years ago

My use case is extracting the params in a reliable fashion since I run only a single lambda function per resource/method in api-gateway. In my case, I also don't run exclusively on AWS so I cannot rely on there always being an eventContext object.

Right this moment my use case is being able to use APIGW, or use a routing middleware so that the project can run both on Lambda and standalone as the user needs.

dougmoscrop commented 5 years ago

Wouldn't the routing middleware be the only real portable option then? That is the intended default use case of this library:

APIg (proxy) -> serverless-http -> koa -> koa-router

You can replace koa/koa-router with express or whatever library you want, but the routing is always done at the end. But then the idea is that part can be taken out and thrown inside Docker and it just works.

DeltaByte commented 5 years ago

Ideally, I want to avoid having any routing being done at the application-level when running on lambda and let API-GW handle everything, having a router in the app is actually an edge-case.

I'm mostly using a framework like Koa for the friendly handling of request/response and async error handling, also the whole multi-cloud constraint that I need to work with (same code for AWS, GCP, and Azure).

dougmoscrop commented 5 years ago

I don't think you need this library then - you just want to write a vanilla Lambda function?

DeltaByte commented 5 years ago

Existing codebase is already written in a mix of Koa and express, so we are splitting the controllers into individual functions, which is where this library comes in since I would rather avoid having to manually do the mapping of events to request objects myself.

example usage

dougmoscrop commented 5 years ago

I see - yeah, I mean, that's good. I don't know about mounting different applications with dynamic path parts though.

Like let's consider an existing app that is 'monolithic', it has:

/customers/{customerId} /orders/{orderId} /search?searchText={text}

You could mount this application in a single /{proxy+} lambda, or you could mount it in /customers/{...} -> one function /orders/{...} -> one function

The latter, you still have 'routing' (the express/koa app is responsible for extracting customerId)

But separating in to separate functions like this isn't always the right choice - remember you will have separate scaling and cold start times for each one, which can be valuable if you want to separate search (only allow 200 concurrency search requests) but it can also mean that you get slowness when you didn't expect it

If you are just doing this prefix based routing, this library already has a base path type option.

DeltaByte commented 5 years ago

The reason for separation is due to security and analytics purposes, more specifically precisely limiting the IAM access for each function. Having the ability to let each microservice run standalone is primarily for tenants that want to run on-premises using existing K8s clusters (not Knative).

What I'm hoping to achieve is having a single codebase that can function in two ways;

  1. standalone functions
    • /users API-GW -{proxy}-> lambda --> controller
    • /users/{id} API-GW -{proxy}-> lambda --> controller
  2. as a service
    • /users K8s --> router-middleware --> controller
    • /users/{id} K8s --> router-middleware --> controller

There are other reasons to use frameworks such as Express/Koa/Hapi in standalone Lambda functions too, e.g. error handling and existing middleware

dougmoscrop commented 5 years ago

Ah, I understand. Are your paths complex though? Like can you still not do:

/users -> usersCollectionHandler /users/{any} -> usersResourceHandler (where {any} might be /{id} or /{id}/subresource)

Do you intend /users/1/things to be a completely separate function too?

I must admit your use case has piqued my interest! I'm just not sure how to do it in a way that works everywhere.

DeltaByte commented 5 years ago

Some of the routes can get fairly complicated, for example if you wanted to reply to a specific comment on a specific post from a specific forum (I realise this is bad api design but typing on my phone)

/users/{userId}/posts/{postId}/comments/{commentId}

Perhaps for the implementation it could be something along the lines of adding a params key on the Request object? That name seems consistent between Koa and Express at the minimum, I'll have a look at other frameworks in a bit.

dougmoscrop commented 5 years ago

Please feel free to contribute something that works, but I'm just forewarning you that some of these libraries do clever stuff, like koa destroys the prototype chain of the req/res you pass it, so it just needs to be well tested. I'm happy to add req.event/req.context to every request, then your code can do what it wants.