Slide 1

Slide 1 text

Middy.js A powerful Node.js middleware framework for your lambdas Luciano Mammino ( ) @loige loige.link/middy-gcs 29/01/2020, Dublin 1

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

Hello, I am Luciano! Hello, I am Luciano! 2

Slide 4

Slide 4 text

Hello, I am Luciano! Hello, I am Luciano! Fullstack Dev & Cloud Architect 2

Slide 5

Slide 5 text

Hello, I am Luciano! Hello, I am Luciano! Fullstack Dev & Cloud Architect Blog: Twitter: GitHub: loige.co @loige @lmammino 2

Slide 6

Slide 6 text

Hello, I am Luciano! Hello, I am Luciano! Fullstack Dev & Cloud Architect Blog: Twitter: GitHub: loige.co @loige @lmammino 2

Slide 7

Slide 7 text

Ask questions here: loige.link/middy-gcs loige.link/middy-gcs loige.link/middy-gcs-qa @loige 3

Slide 8

Slide 8 text

What is serverless What is serverless Compute as functions (FaaS) Compute as functions (FaaS) Event-based model Event-based model Managed resources Managed resources @loige loige.link/middy-gcs-qa 4

Slide 9

Slide 9 text

Why serverless is good Why serverless is good Focus on business logic Focus on business logic Scalability Scalability Pay-per-use model (compute time * memory) Pay-per-use model (compute time * memory) Forces you to think micro-services Forces you to think micro-services @loige loige.link/middy-gcs-qa 5

Slide 10

Slide 10 text

My Serverless experience My Serverless experience Open source A semi-automated weekly newsletter ( ) A middleware framework for AWS Lambda ( ) Enterprise Trading, Billing engine, Market data aggregator solutions ( ) Big data pipeline to make network metadata searchable ( ) Organised various workshops around Europe ( ) Fullstack bulletin middy.js.org ESB Vectra.ai Serverlesslab.com @loige loige.link/middy-gcs-qa 6

Slide 11

Slide 11 text

♀ ♀ DISCLAIMER DISCLAIMER Serverless is more than just FaaS Serverless is more than just FaaS ... but here we will focus mostly on AWS Lamda ... but here we will focus mostly on AWS Lamda @loige loige.link/middy-gcs-qa 7

Slide 12

Slide 12 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa 8

Slide 13

Slide 13 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code 8

Slide 14

Slide 14 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code 8

Slide 15

Slide 15 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code ⚡ event 8

Slide 16

Slide 16 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code ⚡ event 8

Slide 17

Slide 17 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code ⚡ event = 8

Slide 18

Slide 18 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code ⚡ event profit = 8

Slide 19

Slide 19 text

Quickest intro to Lambda (ever)! Quickest intro to Lambda (ever)! @loige loige.link/middy-gcs-qa code ⚡ event profit = * real life coding with Lambda is a little bit more complicated™ than this... 8

Slide 20

Slide 20 text

exports.myLambda = async function ( event, context ) { // get input from event and context // return output or throw an error } Anatomy of a Node.js lambda on AWS Anatomy of a Node.js lambda on AWS @loige loige.link/middy-gcs-qa 9

Slide 21

Slide 21 text

exports.myLambda = async function (event, context) { const name = event.queryStringParameters.name const message = `Hello ${name || 'World'}!` return ({ statusCode: 200, headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message }) }) } Hello World! Hello World! @loige loige.link/middy-gcs-qa 10

Slide 22

Slide 22 text

exports.MyLambda = async function (event, context) { // decrypt environment variables with KMS // deserialize the content of the event // validate input, authentication, authorization // REAL BUSINESS LOGIC // (process input, generate output) // validate output // serialize response // handle errors } Real World Real World vs vs Hello World Hello World @loige loige.link/middy-gcs-qa 11

Slide 23

Slide 23 text

exports.MyLambda = async function (event, context) { // decrypt environment variables with KMS // deserialize the content of the event // validate input, authentication, authorization // REAL BUSINESS LOGIC // (process input, generate output) // validate output // serialize response // handle errors } Real World Real World vs vs Hello World Hello World @loige loige.link/middy-gcs-qa 11

Slide 24

Slide 24 text

exports.MyLambda = async function (event, context) { // decrypt environment variables with KMS // deserialize the content of the event // validate input, authentication, authorization // REAL BUSINESS LOGIC // (process input, generate output) // validate output // serialize response // handle errors } Real World Real World vs vs Hello World Hello World @loige loige.link/middy-gcs-qa 11

Slide 25

Slide 25 text

exports.MyLambda = async function (event, context) { // decrypt environment variables with KMS // deserialize the content of the event // validate input, authentication, authorization // REAL BUSINESS LOGIC // (process input, generate output) // validate output // serialize response // handle errors } Real World Real World vs vs Hello World Hello World LOTS of BOILERPLATE @loige loige.link/middy-gcs-qa 11

Slide 26

Slide 26 text

♀ ♀ In a real project with many lambdas, In a real project with many lambdas, we might end up with a lot of code duplication! we might end up with a lot of code duplication! @loige loige.link/middy-gcs-qa 12

Slide 27

Slide 27 text

Can we avoid a bit of clutter by separating " Can we avoid a bit of clutter by separating "pure pure business logic business logic" from the " " from the "boilerplate boilerplate"? "? @loige loige.link/middy-gcs-qa 13

Slide 28

Slide 28 text

A solution... A solution... @loige loige.link/middy-gcs-qa 14

Slide 29

Slide 29 text

npm install @middy/core Note: using (beta) version 1.0.0 @loige loige.link/middy-gcs-qa 15

Slide 30

Slide 30 text

npm install @middy/core Note: using (beta) version 1.0.0 Give it moar love @loige loige.link/middy-gcs-qa 15

Slide 31

Slide 31 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } @loige loige.link/middy-gcs-qa 16

Slide 32

Slide 32 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } 1. define handler @loige loige.link/middy-gcs-qa 16

Slide 33

Slide 33 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } 2. "middify" the handler @loige loige.link/middy-gcs-qa 16

Slide 34

Slide 34 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } 3. attach middlewares @loige loige.link/middy-gcs-qa 16

Slide 35

Slide 35 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } 4. export "middyfied" handler @loige loige.link/middy-gcs-qa 16

Slide 36

Slide 36 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } @loige loige.link/middy-gcs-qa 17

Slide 37

Slide 37 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } Business logic @loige loige.link/middy-gcs-qa 17

Slide 38

Slide 38 text

Usage Usage const middy = require('@middy/core') const { middleware1, middleware2, middleware3 } = require('some-middlewares') const originalHandler = async (event, context) => { /* your business logic */ } const handler = middy(originalHandler) handler .use(middleware1()) .use(middleware2()) .use(middleware3()) module.exports = { handler } Business logic Boilerplate @loige loige.link/middy-gcs-qa 17

Slide 39

Slide 39 text

Middy comes with a built-in collection Middy comes with a built-in collection of useful middlewares of useful middlewares Some examples: Cache Database manager HTTP error handler HTTP content negotiation HTTP CORS HTTP JSON body parser HTTP Multipart body parser HTTP Security headers Secrets manager SSM Validator Warmup @loige loige.link/middy-gcs-qa 18

Slide 40

Slide 40 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } 19

Slide 41

Slide 41 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } Dependencies 19

Slide 42

Slide 42 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } Handler (business logic) 19

Slide 43

Slide 43 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } Define the input schema (uses ) JSON-schema 19

Slide 44

Slide 44 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } Attach middlewares 19

Slide 45

Slide 45 text

const middy = require('@middy/core') const urlEncodedBodyParser = require('@middy/http-urlencode-body-parser') const validator = require('@middy/validator') const httpErrorHandler = require('@middy/http-error-handler') const processPaymentHandler = async (event, context) => { const { creditCardNumber, expiryMonth, expiryYear, cvc, nameOnCard, amount } = event.body // do stuff with this data ... return ({ statusCode: 200, body: 'payment processed correctly' }) } const inputSchema = { // define validation schema here ... } const handler = middy(processPaymentHandler) .use(urlEncodedBodyParser()) .use(validator(inputSchema)) .use(httpErrorHandler()) module.exports = { handler } Export enhanced handler 19

Slide 46

Slide 46 text

Why? Why? @loige loige.link/middy-gcs-qa 20

Slide 47

Slide 47 text

Why? Why? Simplify code @loige loige.link/middy-gcs-qa 20

Slide 48

Slide 48 text

Why? Why? Simplify code Reusability input parsing input & output validation output serialization error handling... @loige loige.link/middy-gcs-qa 20

Slide 49

Slide 49 text

Why? Why? Simplify code Reusability input parsing input & output validation output serialization error handling... Focus (even) MORE on business logic Testability @loige loige.link/middy-gcs-qa 20

Slide 50

Slide 50 text

How it works How it works @loige loige.link/middy-gcs-qa 21

Slide 51

Slide 51 text

Execution order Execution order @loige 22 loige.link/middy-gcs-qa

Slide 52

Slide 52 text

Execution order Execution order @loige 22 loige.link/middy-gcs-qa

Slide 53

Slide 53 text

Execution order Execution order 1. middleware1 (before) @loige 22 loige.link/middy-gcs-qa

Slide 54

Slide 54 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) @loige 22 loige.link/middy-gcs-qa

Slide 55

Slide 55 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) @loige 22 loige.link/middy-gcs-qa

Slide 56

Slide 56 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) 4. handler @loige 22 loige.link/middy-gcs-qa

Slide 57

Slide 57 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) 4. handler 5. middleware3 (after) @loige 22 loige.link/middy-gcs-qa

Slide 58

Slide 58 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) 4. handler 5. middleware3 (after) 6. middleware2 (after) @loige 22 loige.link/middy-gcs-qa

Slide 59

Slide 59 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) 4. handler 5. middleware3 (after) 6. middleware2 (after) 7. middleware1 (after) @loige 22 loige.link/middy-gcs-qa

Slide 60

Slide 60 text

Execution order Execution order 1. middleware1 (before) 2. middleware2 (before) 3. middleware3 (before) 4. handler 5. middleware3 (after) 6. middleware2 (after) 7. middleware1 (after) @loige 22 loige.link/middy-gcs-qa

Slide 61

Slide 61 text

When an error happens... When an error happens... Flow is stopped First middleware implementing `onError` gets control It can choose to handle the error, or delegate it to the next handler If the error is handler a response is returned If the error is not handled the execution fails reporting the unhandled error @loige loige.link/middy-gcs-qa 23

Slide 62

Slide 62 text

Writing a middleware Writing a middleware const myMiddleware = (config) => { // might set default options in config return ({ before: (handler, next) => { // might read options from `config` }, after: (handler, next) => { // might read options from `config` }, onError: (handler, next) => { // might read options from `config` } }) } module.exports = myMiddleware @loige loige.link/middy-gcs-qa 24

Slide 63

Slide 63 text

Inline middlewares Inline middlewares const middy = require('@middy/core') const handler = middy((event, context, callback) => { // do stuff }) handler.before((handler, next) => { // do something in the before phase next() }) handler.after((handler, next) => { // do something in the after phase next() }) handler.onError((handler, next) => { // do something in the on error phase next() }) module.exports = { handler } @loige @loige loige.link/middy-gcs-qa 25

Slide 64

Slide 64 text

In summary In summary Serverless is cool, it helps you to build apps quickly and with a greater focus on business logic, rather than on infrastructure! Middy helps you to keep focusing on your business logic first You can add extra behaviours with very minimal changes to your core logic by introducing dedicated middlewares You can easily share common functionality through middlewares @loige loige.link/middy-gcs-qa 26

Slide 65

Slide 65 text

If you like Middy If you like Middy Use it Use it Give feedback: Give feedback: Contribute (I am looking for co-maintainers) Contribute (I am looking for co-maintainers) Version 1.0 (stable) coming soon Version 1.0 (stable) coming soon github.com/middyjs/middy/issues github.com/middyjs/middy/issues @loige loige.link/middy-gcs-qa 27

Slide 66

Slide 66 text

Your turn... Your turn... @loige loige.link/middy-gcs-qa 28

Slide 67

Slide 67 text

THANKS! THANKS! A special thank you to all the amazing Middy users and ! contributors @loige loige.link/middy-gcs loige.link/middy-gcs 29