Last active
March 17, 2023 12:22
-
-
Save timoschinkel/23fa06d504bce4a0acd28c4fe593c78c to your computer and use it in GitHub Desktop.
AWS lambda generic middleware enabled handlers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; | |
export class ApiGatewayCorrelationContextMiddleware implements IMiddleware<APIGatewayProxyEvent, APIGatewayProxyResult> { | |
public async process(event: APIGatewayProxyEvent, next: Handler<APIGatewayProxyEvent, APIGatewayProxyResult>): Promise<APIGatewayProxyResult> { | |
return next(event); | |
} | |
} | |
class SomeMiddleware implements IMiddleware<APIGatewayProxyEvent, APIGatewayProxyResult> { | |
public async process(event: APIGatewayProxyEvent, next: EventHandler<APIGatewayProxyEvent, APIGatewayProxyResult>): Promise<APIGatewayProxyResult> { | |
return next(event); | |
} | |
} | |
const apiGatewayHandler = new MiddlewareHandler<APIGatewayProxyEvent, APIGatewayProxyResult>( | |
new SomeMiddleware() | |
); | |
/* Usage: */ | |
export const MyApiGatewayTriggeredHandler = apiGatewayHandler.handle(async () => { | |
console.log('We are here!'); | |
return { | |
statusCode: 200, | |
body: 'OK', | |
}; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe('EventHandler', () => { | |
describe('MiddlewareHandler', () => { | |
type Request = { middlewares: string[] }; | |
type Response = { middlewares: string[] }; | |
class SimpleMiddleware implements IMiddleware<Request, Response> { | |
private readonly identifier: string; | |
constructor (identifier: string) { | |
this.identifier = identifier; | |
} | |
public async process(event, next): Promise<Response> { | |
event.middlewares.push(this.identifier); | |
return next(event) | |
.then(response => { | |
response.middlewares.push(this.identifier); | |
return response; | |
}); | |
} | |
} | |
describe('handle()', () => { | |
it('should pass through all middleware defined in constructor', async () => { | |
const handler = new MiddlewareHandler<Request, Response>( | |
new SimpleMiddleware('one'), | |
new SimpleMiddleware('two') | |
); | |
// Call the handler | |
const response = await handler.handle(async (request) => request)({ middlewares: [] }); | |
expect(response.middlewares).toEqual(['one', 'two', 'two', 'one']); | |
}); | |
it('should pass through all middleware defined in constructor and middleware defined with `use`', async () => { | |
const handler = new MiddlewareHandler<Request, Response>( | |
new SimpleMiddleware('one') | |
).use(new SimpleMiddleware('two')); | |
// Call the handler | |
const response = await handler.handle(async (request) => request)({ middlewares: [] }); | |
expect(response.middlewares).toEqual(['one', 'two', 'two', 'one']); | |
}); | |
}); | |
describe('use()', () => { | |
it('should create a new instance of handler', async () => { | |
const handler = new MiddlewareHandler<Request, Response>( | |
new SimpleMiddleware('one') | |
); | |
const withUse = handler.use(new SimpleMiddleware('two')); | |
expect(handler).not.toEqual(withUse); | |
}); | |
}); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type EventHandler<I, O> = (event: I) => Promise<O>; | |
export interface IMiddleware<I, O> { | |
process(event: I, next: Handler<I, O>): Promise<O>; | |
} | |
export class MiddlewareHandler<I, O> { | |
private readonly middleware: IMiddleware<I, O>[]; | |
constructor(...middleware: IMiddleware<I, O>[]) { | |
this.middleware = middleware; | |
} | |
public use(middleware: IMiddleware<I, O>): MiddlewareHandler<I, O> { | |
return new MiddlewareHandler<I, O>(...this.middleware, middleware); | |
} | |
public handle(implementation: EventHandler<I, O>): EventHandler<I, O> { | |
return async (event) => { | |
if (this.middleware.length > 0) { | |
// pass through middleware | |
const current = this.middleware[0]; | |
return current.process( | |
event, | |
new MiddlewareHandler<I, O>(...this.middleware.slice(1)).handle(implementation) | |
); | |
} | |
return implementation(event); | |
}; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { SQSEvent } from 'aws-lambda'; | |
const sqsHandler = new MiddlewareHandler<SQSEvent, void>( | |
new SqsCorrelationContextMiddleware() | |
); | |
class SomeMiddleware implements IMiddleware<SQSEvent, void> { | |
public async process(event: SQSEvent, next: EventHandler<SQSEvent, void>): Promise<void> { | |
return next(event); | |
} | |
} | |
/* Usage: */ | |
export const MySqsTriggeredHandler = sqsHandler.handle(async () => { | |
console.log('We are here!'); | |
// resolve, in case of an error, we can throw anything and AWS will requeue | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment