{"id":16490520,"url":"https://github.com/mlopezperez/serverless-talk","last_synced_at":"2025-10-04T00:58:55.499Z","repository":{"id":40732759,"uuid":"215427639","full_name":"mlopezperez/serverless-talk","owner":"mlopezperez","description":"Code examples from Serverless Framework talk ","archived":false,"fork":false,"pushed_at":"2023-03-04T04:57:06.000Z","size":2085,"stargazers_count":0,"open_issues_count":11,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-09T03:02:08.091Z","etag":null,"topics":["aws","aws-lambda","lambda-functions","s3","serverless","serverless-framework","sns","sqs"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlopezperez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-16T01:15:19.000Z","updated_at":"2022-09-01T07:33:28.000Z","dependencies_parsed_at":"2024-10-28T19:31:53.654Z","dependency_job_id":null,"html_url":"https://github.com/mlopezperez/serverless-talk","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mlopezperez/serverless-talk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlopezperez%2Fserverless-talk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlopezperez%2Fserverless-talk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlopezperez%2Fserverless-talk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlopezperez%2Fserverless-talk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlopezperez","download_url":"https://codeload.github.com/mlopezperez/serverless-talk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlopezperez%2Fserverless-talk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278250086,"owners_count":25955842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-03T02:00:06.070Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-lambda","lambda-functions","s3","serverless","serverless-framework","sns","sqs"],"created_at":"2024-10-11T13:48:15.490Z","updated_at":"2025-10-04T00:58:55.483Z","avatar_url":"https://github.com/mlopezperez.png","language":"TypeScript","readme":"# Serverless Framework\n\n## Init a serverless project\n\n- Create a folder for your project.\n- `git init` if you want to keep a repo!\n- `serverless create --template aws-nodejs-typescript`\n- Voilá! You have the basic scaffolding!\n- Now you can try with `aws-csharp` as `template`\n\n## Let's add some basic configuration\n\n- Notice the `serverless yml` file.\n- Notice the `webpack-plugin` configuration inside. We're using webpack! (Less things to learn!)\n- Let's add the `region` and the `stage` parameters under the `provider` section.\n\n```yml\n  region: ${opt:region, 'eu-west-1'}\n  stage: dev\n  memorySize: 512\n```\n\n### Variables\n\n- `${}` syntax:\n  - `${opt:name}` for options from command line\n  - `${self:section:name}` to reference the same file\n  - `${env:varName}` for environment variables.\n- Variables from other files can be referenced\n\n## Now... let's deploy\n\n- Run `serverless deploy`\n- Go to your AWS console!\n\n## Adding a bucket and a new function\n\n- We create the source code of `handler.ts` in a new folder `src/fetchAndUpload`\n\n```typescript\nimport { APIGatewayProxyHandler } from 'aws-lambda';\nimport fetch from 'node-fetch';\nimport { S3 } from 'aws-sdk'\nimport { v4 as uuid } from 'uuid';\nimport 'source-map-support/register';\n\ninterface IFetchImageRequest {\n  imageUrl: string;\n}\n\nexport const fetchAndUpload: APIGatewayProxyHandler = async (event, context) =\u003e {\n  console.log('event', event);\n  console.log('context', context);\n  const input: IFetchImageRequest = JSON.parse(event.body);\n  console.log('input:', input);\n  console.log('url:', input.imageUrl);\n\n  const s3 = new S3();\n\n  await fetch(input.imageUrl)\n    .then(\n      response =\u003e {\n        console.log('fetch success, response', response);\n        return response;\n      }).then(\n        response =\u003e {\n          console.log('buffering response');\n          return response.buffer();\n        }\n      ).then(async buffer =\u003e {\n        const uploadParms = {\n          Bucket: process.env.UPLOAD_BUCKET,\n          Key: `${uuid()}.jpg`,\n          Body: buffer,\n        };\n        console.log('uploadParms', uploadParms);\n        await s3.putObject(uploadParms).promise();\n        console.log('object put in bucket');\n      });\n\n  return {\n    statusCode: 200,\n    body: JSON.stringify({\n      message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',\n      input: event\n    }, null, 2),\n  };\n}\n```\n\n- We need some extra dependencies\n\n```sh\nyarn add uuid\nyarn add --dev @types/uuid\nyarn add node-fetch\nyarn add --dev @types/node-fetch\n\n```\n\n- We add the new function to `serverless.yml`\n\n```yml\nfetchAndUpload:\n    handler: src/fetchAndUpload/handler.fetchAndUpload\n    events:\n      - http:\n          method: post\n          path: fetch\n```\n\n- Example request body:\n\n```json\n{\n  \"imageUrl\": \"https://pisces.bbystatic.com/image2/BestBuy_US/images/products/6220/6220794_sa.jpg\"\n}\n```\n\n## Configure the S3 bucket\n\n- Custom section for the name\n\n```yml\ncustom:\n  bucketName: ${self:provider.stage}-fetch-and-upload\n```\n\n- Configure the environment variable so it can be read from code\n\n```yml\n  environment:\n    UPLOAD_BUCKET: ${self:custom.bucketName}\n```\n\n- Add the bucket resource\n\n```yml\nresources:\n  Resources:\n    S3BucketImagesUpload:\n      Type: AWS::S3::Bucket\n      Properties:\n        BucketName: ${self:custom.bucketName}yml\n```\n\n- Add the IAM policy in provider section\n\n```yml\n  iamRoleStatements:\n    - Effect: \"Allow\"\n      Action:\n        - s3:*\n      Resource:\n        - 'arn:aws:s3:::${self:custom.bucketName}/*'\n```\n\n## Subscribing a lambda to S3 event\n\n- Create lambda code, with `S3Event`\n- Configure function with `s3` event referencing the bucket\n\n```yml\n  publishEvent:\n    handler: src/publishEvent/handler.publishEventFromS3\n    events:\n      - s3:\n          bucket: ImagesUpload\n          event: s3:ObjectCreated:*\n```\n\n- Add the permission for the bucket to invoke the lambda. This is done in the resources. This is not in the `iamStatements` because is the bucket invoking the lambda, and not the lambda using the bucket\n\n```yml\nPublishEventLambdaPermissionImagesUploadS3:\n      Type: 'AWS::Lambda::Permission'\n      Properties:\n        FunctionName:\n          'Fn::GetAtt':\n            - PublishEventLambdaFunction\n            - Arn\n        Principal: 's3.amazonaws.com'\n        Action: 'lambda:InvokeFunction'\n        SourceAccount:\n          Ref: AWS::AccountId\n        SourceArn: 'arn:aws:s3:::${self:custom.bucketName}'\n```\n\n## Publish to SNS topic\n\n- Modify the code of the lambda\n\n```typescript  \n       import { S3Event, Context } from 'aws-lambda';\nimport { SNS } from 'aws-sdk';\n\ninterface IEventPayload {\n    objectKey: string;\n    objectSize: number;\n    timestamp: string;\n}\n\nexport const publishEventFromS3 = async (event: S3Event, context: Context) =\u003e {\n    console.log('s3 event handled!', event);\n    console.log('s3 event context', context);\n\n    const sns = new SNS({ region: 'eu-west-1' });\n    await Promise.all(event.Records.map(async r =\u003e {\n        try {\n            console.log('processing record', r);\n\n            const payload: IEventPayload = {\n                objectKey: r.s3.object.key,\n                objectSize: r.s3.object.size,\n                timestamp: r.eventTime\n            };\n            const message = JSON.stringify(payload);\n            console.log('message', message);\n\n            const inputRequest: SNS.PublishInput = {\n                Message: message,\n                TopicArn: process.env.UPLOAD_EVENT_TOPIC_ARN\n            }\n            console.log('request', JSON.stringify(inputRequest));\n\n            let response = await sns.publish(inputRequest).promise();\n            console.log('done!', response);\n        } catch (e) {\n            console.log('exception!', e);\n        }\n    }));\n}\n```\n\n- Configure topic name in custom section\n\n```yml\ntopicName: ${self:provider.stage}--upload-publish\n```\n\n- Configure env variable with topic ARN using pseudo-parameters plugin\n\n```yml\ntopicArn: 'arn:aws:sns:#{AWS::Region}:#{AWS::AccountId}:${self:custom.topicName}'\n```\n\n- Configure topic resource\n\n```yml\nUploadEventTopic:\n      Type: AWS::SNS::Topic\n      Properties:\n        TopicName: ${self:custom.topicName}\n```\n\n- Configure `iamStatement` so lambdas can publish in SNS (and create the topic first)\n\n```yml\n - Effect: \"Allow\"\n      Action:\n        - sns:Publish\n        - sns:CreateTopic\n      Resource:\n        - 'Fn::Join':\n          - ':'\n          - - 'arn:aws:sns'\n            - Ref: 'AWS::Region'\n            - Ref: 'AWS::AccountId'\n            - ${self:custom.topicName}\n```\n\n## Subscribe SQS to SNS and trigger lambda\n\n- Define queue and deadletter queue\n\n```yml UploadEventDeadLetterQueue:\n      Type: AWS::SQS::Queue\n      Properties:\n        QueueName: ${self:custom.queueName}-DeadLetters\n        MessageRetentionPeriod: 1209600 # 14 days in seconds\n```\n\n- Define policy for SNS to publish to SQS\n\n```yml\nUploadEventQueuePolicy:\n      Type: AWS::SQS::QueuePolicy\n      Properties:\n        Queues:\n          - Ref: UploadEventQueue\n        PolicyDocument:\n          Version: \"2012-10-17\"\n          Statement:\n            - Effect: Allow\n              Principal: \"*\"\n              Action:\n                - sqs:SendMessage\n              Resource: \"*\"\n              Condition:\n                ArnEquals:\n                  aws:SourceArn: ${self:custom.topicArn}\n```\n\n- Define subscription\n\n```yml\n UploadEventSubscription:\n      Type: AWS::SNS::Subscription\n      Properties:\n        TopicArn: ${self:custom.topicArn}\n        Endpoint:\n          Fn::GetAtt:\n            - UploadEventQueue\n            - Arn\n        RawMessageDelivery: true\n        Protocol: sqs\n```\n\n- Define lambda handler code. In this case we'll just read the event\n\n```typescript\nimport { SQSEvent, Context } from 'aws-lambda'\n\nexport const processQueue = async (event: SQSEvent, context: Context) =\u003e {\n    console.log('event', event);\n    console.log('context', context);\n    // Here we can, for example, store info in a DynamoDB table\n}\n```\n\n- Define function in `serverless.yml` linked to SQS event\n\n```yml\n    processQueue:\n    handler: src/processQueue/handler.processQueue\n    events:\n      - sqs:\n          batchSize: 1\n          arn:\n            Fn::GetAtt:\n              - UploadEventQueue\n              - Arn\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlopezperez%2Fserverless-talk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlopezperez%2Fserverless-talk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlopezperez%2Fserverless-talk/lists"}