Skip to content

Commit

Permalink
Merge pull request #5 from H4ad/hotfix/peer-dependencies
Browse files Browse the repository at this point in the history
Hotfix/peer dependencies
  • Loading branch information
H4ad authored Apr 2, 2022
2 parents c049498 + 90d7f29 commit 8bf07eb
Show file tree
Hide file tree
Showing 61 changed files with 295 additions and 179 deletions.
114 changes: 73 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@
[![Commitizen Friendly][commitizen-img]][commitizen-url]
[![Semantic Release][semantic-release-img]][semantic-release-url]

Run REST APIs and other web applications using your existing Node.js application framework (Express, Koa, Hapi and Fastify)
, on top of AWS Lambda, Amazon API Gateway and many other event sources.
Run REST APIs and other web applications using your existing Node.js application framework (Express, Koa, Hapi and
Fastify), on top of AWS Lambda, Amazon API Gateway and many other event sources.

This library was a refactored version of [@vendia/serverless-express](https://github.com/vendia/serverless-express), I created a new way to interact and extend event sources by creating contracts to abstract the integrations between each library layer.
This library was a refactored version of [@vendia/serverless-express](https://github.com/vendia/serverless-express), I
created a new way to interact and extend event sources by creating contracts to abstract the integrations between each
library layer.

Why you would use this libray instead of [@vendia/serverless-express](https://github.com/vendia/serverless-express)?

- Better APIs to extend library functionality.
- You don't need me to release a new version to integrate with the new event source, you can create an adapter and just call the `addAdapter` method when building your handler.
- You don't need me to release a new version to integrate with the new event source, you can create an adapter and
just call the `addAdapter` method when building your handler.
- All code can be extended, if you want to modify the current behavior you can.
- This is important because if you find a bug, you can quickly resolve it by extending the class, _and then you can submit a PR to fix the bug_.
- This is important because if you find a bug, you can quickly resolve it by extending the class, _and then you can
submit a PR to fix the bug_.
- All code was written in Typescript.
- We have >99% coverage.

Expand All @@ -51,6 +55,11 @@ yarn add @h4ad/serverless-adapter
You can quickly use this library as follows:

```ts
import { ServerlessAdapter } from '@h4ad/serverless-adapter';
import { ApiGatewayV2Adapter } from '@h4ad/serverless-adapter/lib/adapters/aws';
import { ExpressFramework } from '@h4ad/serverless-adapter/lib/frameworks/express';
import { DefaultHandler } from '@h4ad/serverless-adapter/lib/handlers/default';
import { PromiseResolver } from '@h4ad/serverless-adapter/lib/resolvers/promise';
import app from './app';

export const handler = ServerlessAdapter.new(app)
Expand All @@ -67,21 +76,26 @@ export { handler };

Too fast? Ok, I can explain.

First, you need to create an instance of the builder with `ServerlessAdapter.new(app)`.
The variable `app` is the instance of your framework (Express, Fastify, Koa, etc).
First, you need to create an instance of the builder with `ServerlessAdapter.new(app)`. The variable `app` is the
instance of your framework (Express, Fastify, Koa, etc).

So you need to specify for this library which `framework` you will use to handle the `request` with `setFramework`, you can only use one.
If you use express in our backend, use [ExpressFramework](./src/frameworks/express.framework.ts), see more in [support](#support).
So you need to specify for this library which `framework` you will use to handle the `request` with `setFramework`, you
can only use one. If you use express in our backend, use [ExpressFramework](src/frameworks/express/express.framework.ts)
, see more in [support](#support).

Then you specify which `handler` you will use to interact with the serverless cloud with `setHandler`, currently we only have one, the [DefaultHandler](./src/handlers/default.handler.ts).
In the next releases, maybe we will add support for Azure, Huawei and others, for now, you can use this default.
Then you specify which `handler` you will use to interact with the serverless cloud with `setHandler`, currently we only
have one, the [DefaultHandler](src/handlers/default/default.handler.ts). In the next releases, maybe we will add support
for Azure, Huawei and others, for now, you can use this default.

Then you specify with `setResolver` which `resolver` you will use to wait for the library to forward the request to your `framework`, and then get the `response` back to your cloud.
I recommend you use [PromiseResolver](./src/resolvers/promise.resolver.ts), it's the most cloud-agnostic `resolver`.
Then you specify with `setResolver` which `resolver` you will use to wait for the library to forward the request to
your `framework`, and then get the `response` back to your cloud. I recommend you
use [PromiseResolver](src/resolvers/promise/promise.resolver.ts), it's the most cloud-agnostic `resolver`.

By now, you've already added the basic abstractions of this library, now, you can add the `adapters` that will add support for receiving and processing different sources of `addAdapter` events.
In the example I added [AlbAdapter](./src/adapters/aws/alb.adapter.ts), [SQSAdapter](./src/adapters/aws/sqs.adapter.ts) and [SNSAdapter](./src/adapters/aws/sns.adapter.ts).
With these adapters you can connect your lambda to three different event sources and you can add more if you wish.
By now, you've already added the basic abstractions of this library, now, you can add the `adapters` that will add
support for receiving and processing different sources of `addAdapter` events. In the example I
added [AlbAdapter](./src/adapters/aws/alb.adapter.ts), [SQSAdapter](./src/adapters/aws/sqs.adapter.ts)
and [SNSAdapter](./src/adapters/aws/sns.adapter.ts). With these adapters you can connect your lambda to three different
event sources and you can add more if you wish.

Finally, we call `build` which will assemble your handler that you can expose directly to your cloud.

Expand All @@ -102,10 +116,10 @@ By design we have these contracts that define the layers of the library: Framewo
Currently, we support these frameworks:

- [Express](https://expressjs.com/) by using ([ExpressFramework](./src/frameworks/express.framework.ts))
- [Fastify](https://www.fastify.io/) by using ([FastifyFramework](./src/frameworks/fastify.framework.ts))
- [Hapi](https://hapi.dev/) by using ([HapiFramework](./src/frameworks/hapi.framework.ts))
- [Koa](https://koajs.com/) by using ([KoaFramework](./src/frameworks/koa.framework.ts))
- [Express](https://expressjs.com/) by using ([ExpressFramework](src/frameworks/express/express.framework.ts))
- [Fastify](https://www.fastify.io/) by using ([FastifyFramework](src/frameworks/fastify/fastify.framework.ts))
- [Hapi](https://hapi.dev/) by using ([HapiFramework](src/frameworks/hapi/hapi.framework.ts))
- [Koa](https://koajs.com/) by using ([KoaFramework](src/frameworks/koa/koa.framework.ts))

We support these event sources:

Expand All @@ -127,53 +141,71 @@ We support these event sources:
- [AWS SQS](https://docs.aws.amazon.com/pt_br/lambda/latest/dg/with-sqs.html) by
using ([SQSAdapter](./src/adapters/aws/sqs.adapter.ts))
- Azure
- The support is cooming soon.
- The support is coming soon.
- Huawei
- The support is cooming soon.
- The support is coming soon.

We support these resolvers:

- Promise by using ([PromiseResolver](./src/resolvers/promise.resolver.ts))
- Callback by using ([CallbackResolver](./src/resolvers/callback.resolver.ts))
- AWS Context by using ([AwsContextResolver](./src/resolvers/aws-context.resolver.ts))
- Promise by using ([PromiseResolver](src/resolvers/promise/promise.resolver.ts))
- Callback by using ([CallbackResolver](src/resolvers/callback/callback.resolver.ts))
- AWS Context by using ([AwsContextResolver](src/resolvers/aws-context/aws-context.resolver.ts))

We support these handlers:

- Default by using ([DefaultHandler](./src/handlers/default.handler.ts))
- Default by using ([DefaultHandler](src/handlers/default/default.handler.ts))

# Architecture

The main purpose of this library is to allow the developer to add support for any cloud and as many event sources as he wants, without having to create an issue to request the feature or copy the library code because the library doesn't expose good APIs for you to extend its functionality
The main purpose of this library is to allow the developer to add support for any cloud and as many event sources as he
wants, without having to create an issue to request the feature or copy the library code because the library doesn't
expose good APIs for you to extend its functionality

So I refactored [@vendia/serverless-express](https://github.com/vendia/serverless-express) with 4 layers of abstraction: Framework, Handler, Resolver and Adapter.
So I refactored [@vendia/serverless-express](https://github.com/vendia/serverless-express) with 4 layers of abstraction:
Framework, Handler, Resolver and Adapter.

The [FrameworkContract](./src/contracts/framework.contract.ts) is responsible for forwarding to [IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage) and [ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse) for your application instance.
With this abstraction you can implement any framework you want, they just need to accept both parameters and call [end](https://nodejs.org/api/http.html#class-httpserverresponse) in [ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse), so the library knows when to continue and return the response.
The [FrameworkContract](./src/contracts/framework.contract.ts) is responsible for forwarding
to [IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)
and [ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse) for your application instance. With this
abstraction you can implement any framework you want, they just need to accept both parameters and
call [end](https://nodejs.org/api/http.html#class-httpserverresponse)
in [ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse), so the library knows when to continue
and return the response.

The [HandlerContract](./src/contracts/handler.contract.ts) is responsible to get the input from the serverless and then manage to call each layer of abstraction to return a response.
With this abstraction, you can implement different ways to receive input from your serverless environment.
They usually have the same structure, but if you need to deal with a very different cloud, you can use this abstraction to add support for that cloud.
The [HandlerContract](./src/contracts/handler.contract.ts) is responsible to get the input from the serverless and then
manage to call each layer of abstraction to return a response. With this abstraction, you can implement different ways
to receive input from your serverless environment. They usually have the same structure, but if you need to deal with a
very different cloud, you can use this abstraction to add support for that cloud.

> Handler is a good choice for implementing (~~monsters~~) ways to receive input.
> For example, we can create an http server as its handler to test our serverless code without having to launch the framework. Because? I don't know, but you can.
The [ResolverContract](./src/contracts/resolver.contract.ts) is responsible for waiting for the framework to handle the request and then returning the response to the cloud.
Using AWS for example, you have three ways to wait for the response: returning a promise, calling the callback, and ~~using in-context methods~~, each option has its own benefits, but generally the promise option will be the better because any good cloud provider will support promises.
The [ResolverContract](./src/contracts/resolver.contract.ts) is responsible for waiting for the framework to handle the
request and then returning the response to the cloud. Using AWS for example, you have three ways to wait for the
response: returning a promise, calling the callback, and ~~using in-context methods~~, each option has its own benefits,
but generally the promise option will be the better because any good cloud provider will support promises.

Finally, the masterpiece of this library, the [AdapterContract](./src/contracts/adapter.contract.ts) is responsible for handling the received event, transforming the request in a way that your application can understand and then transforming the response in a way your cloud can understand.
Finally, the masterpiece of this library, the [AdapterContract](./src/contracts/adapter.contract.ts) is responsible for
handling the received event, transforming the request in a way that your application can understand and then
transforming the response in a way your cloud can understand.

Well, with these four contracts, you'll be able to add support to any cloud that exists (no more excuses not to use cloud X with NodeJS).
Well, with these four contracts, you'll be able to add support to any cloud that exists (no more excuses not to use
cloud X with NodeJS).

## Why you create this library?

The real reason I created this library was because I wanted to add API Gateway and SQS support at the same time to save some money.
But, [@vendia/serverless-express](https://github.com/vendia/serverless-express) was not supported, so I [created a PR](https://github.com/vendia/serverless-express/pull/483), but until I finished this library, that PR was never accepted.
The real reason I created this library was because I wanted to add API Gateway and SQS support at the same time to save
some money. But, [@vendia/serverless-express](https://github.com/vendia/serverless-express) was not supported, so
I [created a PR](https://github.com/vendia/serverless-express/pull/483), but until I finished this library, that PR was
never accepted.

So I build my own library based on that library with better APIs so I never have to wait for the maintainer to accept my PR just to extend the library's functionality :)
So I build my own library based on that library with better APIs so I never have to wait for the maintainer to accept my
PR just to extend the library's functionality :)

# Credits

Honestly, I just refactored all the code that the @vendia team and many other contributors wrote, thanks so much to them for existing and giving us a brilliant library that is the core of my current company.
Honestly, I just refactored all the code that the @vendia team and many other contributors wrote, thanks so much to them
for existing and giving us a brilliant library that is the core of my current company.

[build-img]:https://github.com/H4ad/serverless-adapter/actions/workflows/release.yml/badge.svg

Expand Down
1 change: 0 additions & 1 deletion src/adapters/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
//#region Imports

import { BinarySettings } from '../../@types';
import { BinarySettings } from '../@types';
import {
AdapterContract,
AdapterRequest,
FrameworkContract,
HandlerContract,
ResolverContract,
ServerlessHandler,
} from '../../contracts';
import { ILogger } from '../../core';
import { ServerlessRequest, ServerlessResponse } from '../../network';
} from '../contracts';
import { ServerlessRequest, ServerlessResponse } from '../network';
import { ILogger } from './index';

//#endregion

Expand Down
1 change: 1 addition & 0 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './base-handler';
export * from './constants';
export * from './current-invoke';
export * from './event-body';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { IncomingMessage, ServerResponse } from 'http';
import type { Express } from 'express';
import { FrameworkContract } from '../contracts';
import { FrameworkContract } from '../../contracts';

//#endregion

Expand Down
1 change: 1 addition & 0 deletions src/frameworks/express/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './express.framework';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { IncomingMessage, ServerResponse } from 'http';
import type { FastifyInstance } from 'fastify';
import { FrameworkContract } from '../contracts';
import { FrameworkContract } from '../../contracts';

//#endregion

Expand Down
1 change: 1 addition & 0 deletions src/frameworks/fastify/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './fastify.framework';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { IncomingMessage, ServerResponse } from 'http';
import type { Server } from 'hapi';
import { FrameworkContract } from '../contracts';
import { FrameworkContract } from '../../contracts';

//#endregion

Expand Down
1 change: 1 addition & 0 deletions src/frameworks/hapi/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './hapi.framework';
4 changes: 0 additions & 4 deletions src/frameworks/index.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/frameworks/koa/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './koa.framework';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { IncomingMessage, ServerResponse } from 'http';
import type Application from 'koa';
import { FrameworkContract } from '../contracts';
import { FrameworkContract } from '../../contracts';

//#endregion

Expand Down
1 change: 0 additions & 1 deletion src/handlers/base/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
//#region Imports

import util from 'util';
import { BinarySettings, SingleValueHeaders } from '../@types';
import { BinarySettings, SingleValueHeaders } from '../../@types';
import {
AdapterContract,
AdapterRequest,
FrameworkContract,
ResolverContract,
ServerlessHandler,
} from '../contracts';
} from '../../contracts';
import {
ILogger,
isBinary,
setCurrentInvoke,
waitForStreamComplete,
} from '../core';
import { ServerlessResponse } from '../network';
import { BaseHandler } from './base';
} from '../../core';
import { BaseHandler } from '../../core/base-handler';
import { ServerlessResponse } from '../../network';

//#endregion

Expand Down
1 change: 0 additions & 1 deletion src/handlers/index.ts → src/handlers/default/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from './base';
export * from './default.handler';
4 changes: 0 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
export * from './@types';
export * from './adapters';
export * from './contracts';
export * from './core';
export * from './frameworks';
export * from './handlers';
export * from './network';
export * from './resolvers';
export * from './serverless-adapter';
4 changes: 2 additions & 2 deletions src/network/request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ATTRIBUTION: https://github.com/dougmoscrop/serverless-http
import http from 'http';
import { IncomingMessage } from 'http';
import { AddressInfo } from 'net';
import { SingleValueHeaders } from '../@types';
import { NO_OP } from '../core';
Expand All @@ -14,7 +14,7 @@ export interface ServerlessRequestProps {
remoteAddress?: string;
}

export class ServerlessRequest extends http.IncomingMessage {
export class ServerlessRequest extends IncomingMessage {
constructor({
method,
url,
Expand Down
Loading

0 comments on commit 8bf07eb

Please sign in to comment.