<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Seth M</title>
    <description>The latest articles on DEV Community by Seth M (@openstepmedia).</description>
    <link>https://dev.to/openstepmedia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F121676%2F26d356c0-60fb-4c79-b3bd-c0e222625334.jpeg</url>
      <title>DEV Community: Seth M</title>
      <link>https://dev.to/openstepmedia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/openstepmedia"/>
    <language>en</language>
    <item>
      <title>Real-time S3 File zipping With Lambda and WebSockets in NodeJS</title>
      <dc:creator>Seth M</dc:creator>
      <pubDate>Fri, 05 Mar 2021 21:23:24 +0000</pubDate>
      <link>https://dev.to/openstepmedia/real-time-s3-file-zipping-with-lambda-and-websockets-in-nodejs-5815</link>
      <guid>https://dev.to/openstepmedia/real-time-s3-file-zipping-with-lambda-and-websockets-in-nodejs-5815</guid>
      <description>&lt;p&gt;It's always amazing to me that AWS S3 hasn't offered a bulk download service from their console. For the systems I'm working on, downloading a set of files is a common activity. &lt;/p&gt;

&lt;p&gt;Our original design for bulk downloading was setting up an EC2 machine with the aws cli installed, rabbitmq installed, and a small nodejs service.  The node service would consume the messages off rabbitmq, use the aws cli to retrieve files from s3, zip them, and return them to an s3 location.  This solution worked ok, but what if there was a way to do it more reliably and more cost effectively?&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Labmda and API Gateway to the rescue
&lt;/h2&gt;

&lt;p&gt;We settled on the following strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Lambda would be responsible for zipping&lt;/li&gt;
&lt;li&gt;AWS API Gateway using WebSockets would be used for triggering and managing the zipping process&lt;/li&gt;
&lt;li&gt;Upon completing creation of the .zip file, get a pre-signed url of the new .zip file and pass it back to the websocket client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are some terrific articles and examples that I'll list below, but we ended up using &lt;a href="https://github.com/orangewise/s3-zip/blob/master/aws_lambda.md"&gt;this example&lt;/a&gt; from s3-zip as our baseline.&lt;/p&gt;

&lt;p&gt;To wrap the zip lambda function in a websocket we used the aws &lt;a href="https://github.com/aws-samples/simple-websockets-chat-app"&gt;simple-websockets-chat-app&lt;/a&gt; as a blueprint.&lt;/p&gt;

&lt;p&gt;Finally, to make deployment easier, we created the SAM yaml files for pushing our code to AWS Cloudformation.&lt;/p&gt;

&lt;h2&gt;
  
  
  S3 Zip Lambda Function
&lt;/h2&gt;

&lt;p&gt;The payload to the Lamda function will be a set of files to zip and the destination instructions for the resulting .zip file&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/openstepmedia/aws-s3zip-lambda-demo"&gt;Source code&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const payload = {
  region: 'us-east-1',
  bucket: 'demobucket', &amp;lt;-- all assets in same bucket
  folder: 'audio/', &amp;lt;-- must have trailing slash
  files: [
    'file1.mp3',  &amp;lt;-- files in above folder
    'file2.mp3'
  ],
  zipBucket: 'demobucket', &amp;lt;-- bucket for resulting .zip file
  zipFolder: 'temp/', &amp;lt;-- must have trailing slash
  zipFileName: 'demo1.zip',
  signedUrlExpireSeconds: 60 * 60 * 10 &amp;lt;-- expiration time of signedUrl of s3object
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Gateway WebSocket
&lt;/h2&gt;

&lt;p&gt;I've setup a simple API Gateway using CloudFormation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  DemoDevS3ZipWebSocket:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: DemoDevS3ZipWebSocket
      ProtocolType: WEBSOCKET
      RouteSelectionExpression: "$request.body.action"

  Deployment:
    Type: AWS::ApiGatewayV2::Deployment
    DependsOn:
    - ZipRoute
    Properties:
      ApiId: !Ref DemoDevS3ZipWebSocket

  Stage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      StageName: demo
      Description: Demo Deployment
      DeploymentId: !Ref Deployment
      ApiId: !Ref DemoDevS3ZipWebSocket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing to note here is the RouteSelectionExpression value. The "action" parameter will trigger our "onzip" route.&lt;/p&gt;

&lt;p&gt;To actually facilitate the zipping, we are using &lt;a href="https://www.npmjs.com/package/s3-zip"&gt;s3-zip &lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;When the 'onzip' route is invoked, the params are parsed from the event.body.params :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const params = JSON.parse(event.body).params
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do a quick validation to make sure that there are actual files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  if (!(files.length &amp;gt; 0)) {
    return {
      statusCode: 500,
      body: JSON.stringify({
        statusCode: 'error',
        message: 'No files to zip'
      })
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to communicate back through the WebSocket, we needed to use the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ApiGatewayManagementApi.html"&gt;ApiGatewayManagementApi&lt;/a&gt;. The constructor requires the deployed WebSocket address as the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const stage = event.requestContext.stage
  const domainName = event.requestContext.domainName


  // Allows the Lambda function to communicate with the websocket client
  const api = new AWS.ApiGatewayManagementApi({
    endpoint: 'https://' + domainName + '/' + stage
  })

  // event.requestContext.connectionId contains the Websocket id
  const apiParams = {
    ConnectionId: event.requestContext.connectionId,
    Data: null
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the ApiGatewayManagementApi object is available, we can communicate back through the WebSocket by calling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiParams.Data = 'some msg -or- JSON.stringify(obj)'
await api.postToConnection(apiParams).promise()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This call will trigger a WebSocket '&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/onmessage"&gt;onmessage&lt;/a&gt;' event.&lt;/p&gt;

&lt;p&gt;Next, the main try/catch block will invoke s3zip and AWS.S3.upload to stream the content into a zip file using the s3zip archive method.  Using the archive method allows the process to bypass the need for downloading or storing the target files in any kind of temporary location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  try {
    const body = s3Zip.archive({ region: region, bucket: bucket }, folder, files)
    const zipKey = zipFolder + zipFileName
    const zipParams = { params: { Bucket: zipBucket, Key: zipKey } }
    const zipFile = new AWS.S3(zipParams)

    const promise = new Promise((resolve, reject) =&amp;gt; {
      zipFile.upload({ Body: body })
        .on('httpUploadProgress',
          async function (evt) {
            evt.statusCode = 'progress'
            evt.pctComplete = (100 * evt.loaded / totalBytes)
            apiParams.Data = JSON.stringify(evt)
            // communicate with the Websockets, returning the pctComplete
            await api.postToConnection(apiParams).promise()
          })
        .send(async function (e, r) {
          if (e) {
            e.statusCode = 'error'
            reject(e)
          } else {
            r.statusCode = 'success'
            r.Files = files
            r.Folder = folder
            r.SignedUrl = s3.getSignedUrl('getObject', {
              Bucket: zipBucket,
              Key: r.Key,
              Expires: signedUrlExpireSeconds
            })
            resolve(r)
          }
        })
    })

    // wait for s3zip process to complete
    const res = await promise

    // message push back through websocket with zip results
    apiParams.Data = JSON.stringify(res)
    await api.postToConnection(apiParams).promise()

    return {
      statusCode: 200,
      body: JSON.stringify(res)
    }
  } catch (e) {
    // send error messages back to websocket client
    apiParams.Data = JSON.stringify(e)
    await api.postToConnection(apiParams).promise()
    return {
      statusCode: 500,
      body: JSON.stringify(e)
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the upload to s3 happens, the 'httpUploadProgress' event from the s3 upload method will emit and return a value for event.loaded. We push that event object back through the WebSocket using &lt;code&gt;await api.postToConnection(apiParams).promise()&lt;/code&gt;.  What's nice is that this allows our web frontend to give the user a real-time progress display on the zipping process.&lt;/p&gt;

&lt;p&gt;So far, we haven't seen any issues. We've tested out this process on 5GB worth of target files and haven't seen any issue.&lt;/p&gt;

&lt;p&gt;When the zip file is complete, the &lt;code&gt;send()&lt;/code&gt; method is invoked. In the &lt;code&gt;send()&lt;/code&gt; method we do a quick call to &lt;code&gt;getSignedUrl()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r.SignedUrl = s3.getSignedUrl('getObject', {
  Bucket: zipBucket,
  Key: r.Key,
  Expires: signedUrlExpireSeconds
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a successful zip, the signedUrl is generated, and the lambda function will send a message through the websocket. On the calling side, a simple parse can extract the signedUrl and trigger the browser to begin the physical download, as if the user clicked on the .zip file.&lt;/p&gt;

&lt;p&gt;The javascript on the caller .html page looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;socket.onmessage = function (event) {
  if (event.data) {
    var data = JSON.parse(event.data)

    // zip progress
    if (data.statusCode === 'progress') {
      var pct = Math.round(data.pctComplete)
      progressElement.text('Processing ... ' + pct + '%')
    }

    // zip completed
    if (data.statusCode === 'success') {
      if (Object.prototype.hasOwnProperty.call(data, 'SignedUrl')) {
        window.location.assign(data.SignedUrl)
      } else {
        console.error('api did not returned SignedUrl')
      }
      socket.close()
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that's it. This satisfied our requirement for having a Lambda based zip function with a progress meter using NodeJS&lt;/p&gt;

&lt;p&gt;Have a look at the README in the source. It will give instructions for loading both the AWS Api Gateway Websocket and Lambda function into AWS from the command line using SAM and CloudFormation.&lt;/p&gt;

&lt;p&gt;There is also a test script for connecting to the websocket and confirming that the SignedUrl for the zipped assets is being returned correctly.&lt;/p&gt;

&lt;p&gt;Plus there is a simple web page with the javascript to show you how to connect to the websocket and invoke the 'onzip' route.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

&lt;p&gt;The source code is available from github.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/openstepmedia/aws-s3zip-lambda-demo"&gt;https://github.com/openstepmedia/aws-s3zip-lambda-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>zip</category>
      <category>lambda</category>
      <category>websocket</category>
    </item>
    <item>
      <title>Using AWS Step Functions with nodejs</title>
      <dc:creator>Seth M</dc:creator>
      <pubDate>Wed, 25 Nov 2020 00:08:50 +0000</pubDate>
      <link>https://dev.to/openstepmedia/using-aws-step-functions-with-nodejs-11i5</link>
      <guid>https://dev.to/openstepmedia/using-aws-step-functions-with-nodejs-11i5</guid>
      <description>&lt;p&gt;A recent project required some scalability and specifically needed the ability to put long-running jobs in a queue. The jobs were doing some heavy lifting around transcoding, but really, we needed to come up with a plan for managing this job queue. We've had some experience with tools like rabbitmq and kue/redis, but ultimately, because we're a small team, we wanted to see if we could avoid having to manage all these moving pieces.&lt;/p&gt;

&lt;p&gt;Since we use AWS for some of our infrastructure, we chose to use AWS Step Functions as our queue management tool. The core of our transcoding engine had to run in its own process on a windows machine on EC2, so we decided to wrap up the process inside of an API engine that would then hook into the AWS Step Function.&lt;/p&gt;

&lt;p&gt;There are plenty of resources on describing what &lt;a href="https://aws.amazon.com/step-functions/getting-started/" rel="noopener noreferrer"&gt;AWS Step Functions&lt;/a&gt; are, so I won't go into depth on that. In short, AWS offers the ability to put messages into a queue where they will sit until they are removed from the queue by a consuming application.&lt;/p&gt;

&lt;p&gt;This tutorial will focus on demonstrating how to integrate AWS Step Functions as a way to queue large running processes wrapped up in a web API.  For our API, we're very comfortable with using &lt;a href="https://adonisjs.com/docs/4.1/installation" rel="noopener noreferrer"&gt;AdonisJS&lt;/a&gt;, a fantastic nodejs web framework that has been heavily influenced by the Laravel world.&lt;/p&gt;

&lt;p&gt;I'll dive right in and assume that you were either able to download the &lt;a href="https://github.com/openstepmedia/adonisjs-stepfunction-demo" rel="noopener noreferrer"&gt;source code&lt;/a&gt; to this tutorial or were able to setup a basic AdonisJS project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an AWS Activity
&lt;/h2&gt;

&lt;p&gt;Our example is just a demonstration that will require a single activity step. The goal here is to demonstrate sending data from a web form and adding it to the AWS State Machine that will then act as a queue. A consuming application will then grab the information sitting in the queue and then output what it finds to our console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-activities.html" rel="noopener noreferrer"&gt;Activities&lt;/a&gt; are the small tasks that are the "workers" in the state machine.&lt;/p&gt;

&lt;p&gt;Let's call our Activity: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;DemoWorkerActivity&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the State Machine
&lt;/h2&gt;

&lt;p&gt;With the ARN of the Activity, we can create a State Machine document like the one below. There is only one step, so we'll call that step "DemoWorkerRun". Inside of the step will be a single activity "DemoWorkerActivity". &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
    "Comment": "Demo State Machine - to show how my application interacts with the queue",
    "StartAt": "DemoWorkerRun",
    "States": {
        "DemoWorkerRun": {
            "Type": "Task",
            "Resource": "arn:aws:states:us-east-1:XXXXXXXXXX:activity:DemoWorkerActivity",
            "End": true
        }
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the state machine is created, AWS will give you a resource ARN for that:&lt;/p&gt;

&lt;p&gt;In this example the state machine identifier is:&lt;/p&gt;

&lt;p&gt;"arn:aws:states:us-east-1:XXXXXXXXXX:stateMachine:DemoWorkerStateMachine"&lt;/p&gt;

&lt;h2&gt;
  
  
  Core nodejs components:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;QueueService&lt;/em&gt; - this service will be responsible for kicking off a state machine "execution" with an initial payload.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;DemoWorker&lt;/em&gt; - the worker will act as the consumer of the activity the lives within the state machine. It is the point of contact between the payload and your underlying application.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  QueueService
&lt;/h2&gt;

&lt;p&gt;In our adonis project, we'll create a Services folder and create the app/Services/QueueService.js class. This class will start a state machine execution with the an initial custom payload. This class requires the aws-sdk to be installed. You can install it using the adonis tools:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;adonis install aws-sdk&lt;/code&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;'use strict'&lt;br&gt;
const AWS = require('aws-sdk')&lt;br&gt;
const Env = use('Env')&lt;/p&gt;

&lt;p&gt;class QueueService {&lt;br&gt;
  /**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Returns result of StepFunctions.startExecution&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;@see &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StepFunctions.html#startExecution-property" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StepFunctions.html#startExecution-property&lt;/a&gt;&lt;br&gt;
*/&lt;br&gt;
async enqueue (name, payload) {&lt;br&gt;
const awsConfig = new AWS.Config({&lt;br&gt;
  accessKeyId: Env.get('AWS_ACCESS_KEY'),&lt;br&gt;
  secretAccessKey: Env.get('AWS_SECRET_KEY'),&lt;br&gt;
  region: Env.get('AWS_REGION')&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;// Initialize the AWS SDK &lt;br&gt;
const StepFunctions = new AWS.StepFunctions(awsConfig)&lt;/p&gt;

&lt;p&gt;// Executing the State Machine requires a custom name, some custom input and the ID of the State Machine from AWS&lt;br&gt;
var params = {&lt;br&gt;
  name: name,&lt;br&gt;
  input: JSON.stringify(payload),&lt;br&gt;
  stateMachineArn: Env.get('AWS_STATEMACHINE_ARN')&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Execute StepFunction&lt;br&gt;
// @see &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StepFunctions.html#startExecution-property" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/StepFunctions.html#startExecution-property&lt;/a&gt;&lt;br&gt;
let result = null&lt;br&gt;
try {&lt;br&gt;
  result = await StepFunctions.startExecution(params).promise()&lt;br&gt;
} catch (e) {&lt;br&gt;
  console.log('Error:', e)&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;return result&lt;br&gt;
}&lt;br&gt;
}&lt;br&gt;
module.exports = QueueService&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  DemoWorker.js&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Let's create a new folder app\Workers and add DemoWorker.js. This class is responsible for consuming the job that is pending inside the state machine. It requires the npm module 'step-function-worker'&lt;/p&gt;

&lt;p&gt;Install it into your adonis project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;adonis install step-function-worker&lt;/code&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;'use strict'&lt;/p&gt;

&lt;p&gt;const StepFunctionWorker = require('step-function-worker')&lt;br&gt;
const AWS = require('aws-sdk')&lt;br&gt;
const Env = use('Env')&lt;br&gt;
const Logger = use('Logger')&lt;/p&gt;

&lt;p&gt;/*&lt;br&gt;
|--------------------------------------------------------------------------&lt;br&gt;
| DemoWorker&lt;br&gt;
|--------------------------------------------------------------------------&lt;br&gt;
| @see &lt;a href="https://github.com/piercus/step-function-worker" rel="noopener noreferrer"&gt;https://github.com/piercus/step-function-worker&lt;/a&gt;&lt;br&gt;
|&lt;br&gt;
| For this class to work, a StepFunction activity must first be configured on AWS&lt;br&gt;
*/&lt;/p&gt;

&lt;p&gt;class DemoWorker extends StepFunctionWorker {&lt;br&gt;
  constructor () {&lt;br&gt;
    const options = {}&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Configure the connection to AWS
options.awsConfig = new AWS.Config({
  accessKeyId: Env.get('AWS_ACCESS_KEY'),
  secretAccessKey: Env.get('AWS_SECRET_KEY'),
  region: Env.get('AWS_REGION')
})

// Function that interacts directly with the AWS StepFunction this must be defined
// The call back is responsible for letting the State Machine know it can either
// continue to the next step in execution or fail
options.fn = async (input, cb, heartbeat) =&amp;amp;gt; {
  // Respond to StepFunction state machine
  Logger.info('Custom Worker function:', input)
  cb(null, input)
}

// the ID of the Step Function Activity arn:aws:states:us-east-1:XXXXXXXXXXXXX:activity:DemoWorkerActivity
options.activityArn = Env.get('AWS_ACTIVITY_ARN_DEMOWORKERACTIVITY')

super(options)
this._initCallbacks()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;_initCallbacks () {&lt;br&gt;
    this.on('task', this.task)&lt;br&gt;
    this.on('ready', this.ready)&lt;br&gt;
    this.on('error', this.error)&lt;br&gt;
    this.on('failure', this.failure)&lt;br&gt;
    this.on('success', this.success)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;/**&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Called when the worker "wakes up"&lt;/li&gt;
&lt;li&gt;The StepFunctionWorker parent class will pass in the payload&lt;/li&gt;
&lt;li&gt;@param {*} task
*/
task (task) {
// task.input contains the payload from the web
Logger.info('DemoWorker task:', task.input)
}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ready () {&lt;br&gt;
    Logger.info('DemoWorker is ready')&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;failure (failure) {&lt;br&gt;
    Logger.info('DemoWorker failure:', failure)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;success (output) {&lt;br&gt;
    // output.input will contain the payload from the web&lt;br&gt;
    Logger.info('DemoWorker success:', output.input)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;error (err) {&lt;br&gt;
    Logger.info('DemoWorker error:', err)&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;module.exports = DemoWorker&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Demo Time&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;I'll skip ahead to our demo. We'll create a form where we will enter in a name for the State Machine Execution.  You can customize the name given to the execution job. In our project we used the ObjectID of a MongoDB record that was responsible for maintaining the state of the queued job. Using a naming convention that helped us trace the execution object back to the object in our system that initialized the payload, made it easier to follow our system flow.&lt;/p&gt;

&lt;p&gt;Let's fire up out adonis project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;adonis serve --dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I can now access &lt;a href="http://localhost:3333" rel="noopener noreferrer"&gt;http://localhost:3333&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There I have the following form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0tdb6wu4sdvwo6t10pxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0tdb6wu4sdvwo6t10pxp.png" alt="Step Function web form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After submitting the form let's take a look at the State Function execution job that has been queued up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv5qljw4z4936sy4zifq2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv5qljw4z4936sy4zifq2.png" alt="AWS State Machine - Pending Execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have to fire up our DemoWorker to watch for our custom Activity to get executed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;adonis stepfunctionworker --name=DemoWorker&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As soon as the DemoWorker starts, it pulls the pending execution job off the State Function queue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn4xn3a0vmo3kejti64dh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn4xn3a0vmo3kejti64dh.png" alt="DemoWorker output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take another look at the State Machine and see that the execution completed succesfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcdxfygshrij0ltjedyca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcdxfygshrij0ltjedyca.png" alt="AWS State Machine - Successful Execution"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And done! We've accomplished our goal of creating an AWS State Machine and having our custom application initiate and complete a small, but complete job.&lt;/p&gt;

&lt;p&gt;Source code is at: &lt;a href="https://github.com/openstepmedia/adonisjs-stepfunction-demo" rel="noopener noreferrer"&gt;https://github.com/openstepmedia/adonisjs-stepfunction-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>aws</category>
      <category>stepfunctions</category>
      <category>adonisjs</category>
    </item>
  </channel>
</rss>
