Todos REST API with MuleSoft and AWS DynamoDB

By Adam McQuistan in MuleSoft  08/29/2022 Comment

Introduction

In this article I present a walk through of the canonical Todos REST API which utilizes AWS DynamoDB to store todos and exposes Create, Read, Update, and Delete (CRUD) interfaces to manage todos implemented with MuleSoft's Anypoint Platform. DynamoDB is a fully managed No SQL document database provided by AWS with millisecond latency at any scale with proper data modeling. MuleSoft Anypoint Platform is the complete platform for integration and APIs.

Contents

Provisioning AWS DynamoDB Resources with Cloud Development Kit

For provisioning the AWS DynamoDB data store along with an IAM Identity for minimally permissioned access to from MuleSoft Anypoint Platform I am using the AWS Cloud Development Kit (CDK) Infrastructure as Code technology. Using CDK IaC follows engineering best practices to provide a constistent and repeatable working example.

To follow along readers will need to have the AWS CDK installed along with its dependencies. Please refer to AWS CDK's installation documentation. For completeness I show the steps I took to build this project from scratch but, if you are following along you may consider it a better use of your time to simply clone the source code from my GitHub repo and refer to that while reading along.

To start I create a new directory named mulesoft-dynamodb-todos which will contain two subdirectories. One subdirectory is named aws which will contain the CDK project while the other subdirectory is named mulesoft and is for the MuleSoft Anypoint Studio project.

mkdir mulesoft-dynamodb-todos
cd mulesoft-dynamodb-todos
mkdir aws mulesoft

Next I change directories into the aws directory and create a CDK TypeScript project.

cd aws
cdk init app -l typescript

Inside lib/aws-stack.ts I add CDK Constructs to define a mulesoft-todos DynamoDB table as well as create an IAM Group with permissions to list DynamoDB tables plus grant read/write permissions on the mulesoft-todos table. Then an IAM User is created and assigned to the previously mentioned IAM Group before finally assigning programmatic access keys. The IAM Identity access key is then saved security in secrets manager.

// lib/aws-stack.ts

import { Stack, StackProps, CfnOutput, Aws, SecretValue } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ddb from "aws-cdk-lib/aws-dynamodb";
import * as iam from "aws-cdk-lib/aws-iam";
import * as secretsMgr from "aws-cdk-lib/aws-secretsmanager";

export class AwsStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const tbl = new ddb.Table(this, 'todos', {
      tableName: 'mulesoft-todos',
      partitionKey: {
        name: 'userID',
        type: ddb.AttributeType.STRING
      },
      sortKey: {
        name: 'created',
        type: ddb.AttributeType.NUMBER
      }
    });

    const todosTblGrp = new iam.Group(this, 'todos-tbl', {
      groupName: 'mulesoft-todos'
    });
    todosTblGrp.addToPolicy(new iam.PolicyStatement({
      actions: ['dynamodb:ListTables'],
      effect: iam.Effect.ALLOW,
      resources: [
        `arn:aws:dynamodb:${Aws.REGION}:${Aws.ACCOUNT_ID}:table/*`
      ]
    }));
    tbl.grantReadWriteData(todosTblGrp);

    const todosUser = new iam.User(this, 'todos-user', {
      groups: [todosTblGrp],
      userName: 'mulesoft-todos-usr'
    });

    const todosAccessKey = new iam.AccessKey(this, 'todos-user-keypair', {
      user: todosUser
    });

    const keyPairSecret = new secretsMgr.Secret(this, 'todos-user-keypair-secret', {
      secretName: 'todos-user-keypair-secret',
      secretObjectValue: {
        'keyID': SecretValue.unsafePlainText(todosAccessKey.accessKeyId),
        'keySecret': SecretValue.unsafePlainText(todosAccessKey.secretAccessKey.toString())
      }
    });
    keyPairSecret.node.addDependency(todosAccessKey);

    new CfnOutput(this, 'todos-keypair-secret-arn', {
      value: keyPairSecret.secretArn
    });
  }
}

Then over in bin/aws.ts I instantiate the AwsStack pass it the App instance and name the stack.

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { AwsStack } from '../lib/aws-stack';

const app = new cdk.App();
new AwsStack(app, 'AwsStack', {
  stackName: 'mulesoft-dynamodb-todos-stack'
});

Now to deploy I can use an NPM script from the package.json file as shown below.

$ npm run cdk deploy --all --require-approval=never

The output from deploying should display the Amazon Resource Number (ARN) for the secret associated with the IAM User's access key pair. Use the following AWS CLI command the retrieve the access key pair (replacing $SECRET_ARN with the value from the deployment output).

$ aws secretsmanager get-secret-value --secret-id $SECRET_ARN > creds.json

 

API First Design: Creating the API Spec in Design Center

To create an API Specification for the Todos API I log into MuleSoft's Anypoint Platform, open the Design Center App, and click create new followed by New API Specification.

In the resulting dialog I name the project todos-api, leave the default to RAML 1.0, and click Create API button.

 

Add two folders by clicking the plus sign add button to the right of the files label on the top left of the screen then select "New Folder" before finally naming one folder "examples" while naming the next one "datatypes". Within the examples folder add a new RAML file as a Data Type named TodoDataType.raml

Within TodoDataType.raml I define the Todo data model as shown below.

#%RAML 1.0 DataType

type: object
properties:
  userID: string
  created: number
  todo: string
  completed: boolean

Another datetype representing an update input payload for an existing Todo must be created named TodoUpdateDatatype.raml for optionally updating todo and/or completed fields.

#%RAML 1.0 DataType

type: object
properties:
  todo?: string
  completed?: boolean

Next I add another RAML file but this time it goes in the examples folder with the name TodoExample.raml and place the following in it.

#%RAML 1.0 NamedExample
value:
  userID: "657"
  created: 1661470771
  todo: "Mow the lawn"
  completed: false

The previous example is of a full Todo object. There will be a need in this API to update an existing Todo's todo or completed status so, another example RAML file named UpdateTodoExample.raml as seen below.

#%RAML 1.0 NamedExample
value:
  todo: "Feed to dogs"
  completed: false

I add one more example to represent multiple todos in a file named TodosExample.raml within the examples folder. This example will be used for the response of listing todos.

#%RAML 1.0 NamedExample
value:
  todos:
    -
      userID: "657"
      created: 1661470771
      todo: "Mow the lawn"
      completed: false
    -
      userID: "287"
      created: 1661470583
      todo: "Organize the garage"
      completed: true
  count: 2
  lastEvaluatedKey: null

Now over in the todos-api.raml file I add the following API spec definition detailing the Todos REST API.

#%RAML 1.0
title: todos-api

types:
  Todo: !include /datatypes/TodoDataType.raml
  TodoUpdate: !include /datatypes/TodoUpdateDataType.raml
  ListTodosResponse:
    properties:
      todos: Todo[]
      count: number
      lastEvaluatedKey: object?

traits:
  user-id-required:
    headers:
      x-user-id:
        type: string
    responses:
      400:
        description: Header x-user-id required.

/todos:
  is: [user-id-required]
  get:
    responses:
      200:
        body:
          application/json:
            type: ListTodosResponse
            examples:
              output: !include /examples/TodosExample.raml
  post:
    body:
      application/json:
        type: Todo
        examples:
          input: !include /examples/TodoExample.raml
    responses:
      201:
        body:
          application/json:
            example:
              output: "Todo created"
  /{created}:
    is: [user-id-required]
    get:
      responses:
        200:
          body:
            application/json:
              type: Todo
              examples:
                output: !include /examples/TodoExample.raml
    put:
      body:
        application/json:
          type: TodoUpdate
          examples:
            input: !include /examples/UpdateTodoExample.raml
      responses:
        200:
          body:
            application/json:
              type: Todo
              examples:
                output: !include /examples/TodoExample.raml
    delete:
      responses:
        200:
          body:
            application/json:
              example:
                output: "Todo deleted"

In the types section I define and reference the Todo and TodoUpdate datatypes created earlier. Then I create a final datatype definition inline representing the response for the GET /todos resource endpoint. After the types section comes the traits section where I establish a header representing a user's ID which will be required on every request in this API so that todos can be associated with a given user. Each resource definition uses the "is" field to specify that the user ID header is required.

Next the /todos resource endpoint is provided with a GET method to fetch todos associated with a user along with a POST method to create new todos for a user. A sub-resource is added, /todos/{created}, which requires the todo created timestamp in seconds since epoch for each todo to be accessed or modified. This sub-resource provides a GET method to fetch the individual todo along with a PUT method to update either the todo title or the completed status, then lastly there is a DELETE method for removing a user's todo.

With all files saved I click the publish button on the top right of Design Center then click Publish to Exchange then enter 1.0.0 for Asset vertion and v1 for API version before clicking the final Publish to Exchange.

 

Importing the API Spec into New Anypoint Studio Project

At this point I can switch over to Anypoint Studio to pull in the API Spec I developed in Design Center utilizing APIKit to scaffold my API Spec into a respective set of flows. In Anypoint Studio I create a new Mule Project with a name of mulesoft-dynamodb-todos then browse to the location of the mulesoft directory I created at the start of this article.

 

In the API Implementation section I click the add plus sign button within the "Import a Published API" tab and select "from Exchange" in the context menu. In the dialog that comes up I enter the search term "todos" to retrieve my todos-api, select it on the left menu of available modules and click add to projet and then finish.

 

After creating the new project I open the todos-api.xml file under src/main/mule directory. I can see that APIKit generated a main flow which uses APIKit Router to route incoming request to each resource + method combination as a subflow. I rename todos-api.xml file to interface.xml since it will serve as the interface to this API project.

 

Installing and Configuring the DynamoDB Connector

From this section onward I shift to providing an implementation for the previously defined Todos REST API interface. To start I open the mulesoft-dynamodb-todos.xml file under src/main/mule and rename it to implementation.xml. The data associated with the Todos will be stored in DynamoDB so, I must add the DynamoDB connector to the Anypoint Studio project. With the newly renamed implementation.xml file still open in Anypoint Studio I click the Search in Exchange button on the right under the Mule Palette tab. In the resulting dialog I enter the search term "dynamodb", select it from the results and click add before finally clicking finished.

 

Connecting the Mule project to DynamoDB will require using the sensitive IAM User keypair credentials provisioned in the earlier CDK section and will require using the "Mule Secure Configuration" module. I add this security module from Exchange in the same way I added the DynamoDB Connector.

Now over in src/main/resources I add a secure.yaml file which will contain the credentials, in encrypted format, to be used by the DynamoDB connector.

 

Next I create a global.xml file within the src/main/mule directory which will be a central location for project connector configuration elements. With the global.xml file open and the "Global Elements" view selected I click the Create button creating a "Secure Properties Config" element.

 

In the Secure Properties Config dialog I enter secure.yaml for the file name then enter ${secure.key} for the key and change the Algorithm to Blowfish.

 

Now I right click the mulesoft-dynamodb-todos project root directory under the Package Explorer and select Run As > Run Configurations ...

Within the run configurations view I add an environment variable named "secure.key" and set its value to "Develop3r". I need to use this "Develop3r" secure key to encrypt the IAM User access key ID and access key secret saved in the creds.json earlier while provisioning the AWS resources with CDK. To encrypt these values I utilize the Secure Properties Tool executable Jar provided by MuleSoft.

First I encrypt the access key ID as follows.

java -cp secure-properties-tool.jar com.mulesoft.tools.SecurePropertiesTool \
  string encrypt Blowfish CBC Develop3r "INSERT-ACCESS-KEY-ID"

Next I do the same but for the access key secret.

java -cp secure-properties-tool.jar com.mulesoft.tools.SecurePropertiesTool \
  string encrypt Blowfish CBC Develop3r "INSERT-ACCESS-KEY-SECRET"

Over in the newly added secure.yaml file within src/main/resources add the following secure configuration entries for accessKeyID and accessKeySecret under dynamodb section with the encrypted values just created and wrapped in the ![...] syntax.

dynamodb:
  accessKeyID: "![INSERT-ENCRYPTED-VALUE-HERE]"
  accessKeySecret: "![INSERT-ENCRYPTED-VALUE-HERE]"

 

Alright back in the global.xml file's Global Elements view I create another global configuration element for the DynamoDB Connector.

 

Then in the Global Elements Properties dialog for the DynamoDB Connector I set the access key ID and access key secret using ${secure::property-selector} syntax.

To ensure that these highly sensitive values do not get leaked as viewable plain text I update the mule-artifact.json to include these properties within the secureProperties field effectively ensuring they are only ever presented in masked form.

{
  "minMuleVersion": "4.4.0",
  "secureProperties": [
    "secure.key",
    "dynamodb.accessKeyID",
    "dynamodb.accessKeySecret"
  ]
}

 

Implementing POST /todos API

Alright its time to put together the first API implementation to expose the /todos endpoint with a POST method for creating a todo associated with a user. Over in implementation.xml on the Message Flow view I add a Transform Message component to the canvas creating a new flow along with it. I rename the flow to createTodoFlow. After the Transform Message component I add a DynamoDB PUT Item operator in and follow that with a Set Payload operation.

 

In the Transform Message operation I change the output to application/json and assemble a payload for the DynamoDB Put Item API parsed from the request such that the userID field is pulled from the request header and the other fields are expected in the request body.

 

Now over in the properties view of the DynamoDB Put Item operator I set the table name to mulesoft-todos.

For the last createTodoFlow update in the Set Payload operator I set the value to "Todo created".

Lastly in interface.xml I replace the Transform Message operator with a Flow Reference hooked up to the createTodoFlow.

The final createTodoFlow XML definition is as follows.

	<flow name="createTodoFlow" doc:id="059e5988-4d60-4b47-b68a-b9d5b2ec79c7" >
		<ee:transform doc:name="Request Transform" doc:id="6aed9ca9-ea18-4148-80b8-f4daa8c0b305" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	userID: {S: attributes.headers['x-user-id']},
	created: {N: payload.created as String},
	todo: {S: payload.todo},
	completed: {Bool: if (payload.completed?) payload.completed else false}
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<dynamodb:put-item doc:name="Put item" doc:id="2ba07f87-f565-4707-868c-8885be6a4edc" config-ref="Amazon_DynamoDB_Configuration" tableName="mulesoft-todos"/>
		<set-payload value="Todo created" doc:name="Set Payload" doc:id="6f094389-7697-461c-b341-ff640c885ffe" />
	</flow>

At this point I run the project to test it for the first time and fire off the following HTTP POST request using my favorite HTTP CLI Client HttPie.

http POST :8081/api/todos "x-user-id:657" \
  created:=1661734763 todo="Mow the lawn" completed:=false userID=657

 

Implementing GET /todos API

The next method to implement for the /todos resource is the GET method which will return a list of todos along with some metadata about the count returned and the last key retrieved if repeat trips to the DynamoDB table is required. Back over in implementation.xml I drag another Transform Message operator onto the canvas below the previous one. I name the new flow listTodosFlow then add a DynamoDB Query operator after the Transform Message then follow it with another Transform Message.

 

The DynamoDB Query operator needs updated by setting the table name to "mulesoft-todos", the key condition expression to the keyExpression from the prior transform, set select to ALL_ATTRIBUTES, and lastly the query parameters attribute values to the attrs field of the transform.

 

In the response Message Transform I use dataweave to transform the DynamoDB Query response into the format specified in the interface definition.

Next I update the get:/todos flow subflow in the interface.xml swapping out the Message Transform with a Flow Reference to the listTodosFlow from the implementation.xml file I just composed.

The final createTodoFlow XML definition is as follows.

	<flow name="listTodosFlow" doc:id="f5e7283e-40d0-472b-80c6-1224afca167a" >
		<ee:transform doc:name="Request Transform" doc:id="96475b95-0e99-4890-ab4c-c359289006b1" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	keyExpression: 'userID = :userID',
	attrs: { ":userID": {"S": attributes.headers['x-user-id']}}
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<dynamodb:query doc:name="Query" doc:id="cf5cb4cb-c7b9-4c8b-93db-38ccfa0f5478" config-ref="Amazon_DynamoDB_Configuration" keyConditionExpression="#[payload.keyExpression]" tableName="mulesoft-todos" attributeValues="#[payload.attrs]" select="ALL_ATTRIBUTES"/>
		<ee:transform doc:name="Response Transform" doc:id="49d1d161-86cd-4fcc-bd09-d3f8d9338ecb" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	items: payload.items default [] map {
		userID: $.userID.s,
		created: $.created.n as Number,
		completed: $.completed.bool as Boolean default false,
		todo: $.todo.s
	},
	count: payload.count,
	lastEvaluatedKey: payload.lastEvaluatedKey
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
	</flow>

Lastly I test the endpoint with another request fired off from the CLI.

$ http :8081/api/todos "x-user-id:657" -b
{
    "count": 2,
    "items": [
        {
            "completed": false,
            "created": 1661734763,
            "todo": "Mow the lawn",
            "userID": "657"
        },
        {
            "completed": false,
            "created": 1661470771,
            "todo": "Mow the lawn",
            "userID": "657"
        }
    ],
    "lastEvaluatedKey": null
}

 

Implementing GET /todos/{created} API

Here focus is spent implemeting the ability to use the DynamoDB Get Item API to fetch a todo. A Message Transform is added to the implementation.xml canvas to generating a new flow to be named getTodoFlow. After the request Message Transform the DynamoDB Get Item operator is added followed by a response Message Transform. The input request Message Transform maps the header x-user-id to the userID partition key and the created URI parameter into the sort key to be passed to the Get Item operator.

 

The Get Item operator only needs the table name updated to mulesoft-todos. The response Message Transform again uses data weave to reshape the response to the format of the API Spec.

Over in the get:\todos\{created} subflow of the interface.xml I replace the Transform Message with a Flow Reference wired up to the getTodoFlow. Again, the complete getTodoFlow XML definition is given below.

<flow name="getTodoFlow" doc:id="035d4639-e729-48ef-aeb0-9f935686370a" >
		<ee:transform doc:name="Request Transform" doc:id="f2dae583-ff45-42d1-923a-6cc4f4ce45c6" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	userID: {"S": attributes.headers['x-user-id']},
	created: {"N": attributes.uriParams.created}
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<dynamodb:get-item doc:name="Get item" doc:id="c55499cc-46a9-4c41-a616-c40ce84e55e6" config-ref="Amazon_DynamoDB_Configuration" tableName="mulesoft-todos"/>
		<ee:transform doc:name="Response Transform" doc:id="ee5b6a00-7699-4b57-8b53-6fe190788154" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	userID: payload.item.userID.s,
	created: payload.item.created.n as Number,
	completed: payload.item.completed.bool as Boolean,
	todo: payload.item.todo.s
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
	</flow>

Then just like before I test the new endpoint implementation.

$ http :8081/api/todos/1661470771 "x-user-id:657" -b
{
    "completed": false,
    "created": 1661470771,
    "todo": "Mow the lawn",
    "userID": "657"
}

 

Implementing PUT /todos/{created} API

Fantastic progress is being made thus far, only two more REST endpoints to provide implementations for witht he focus in this section being on the the updating of existing todos. Similar to the other endpoint implementations I drop a new Transform Message operator at the bottom of the canvas in implementation.xml to created a new Flow scope which I name updateTodoFlow. Then after the request Transform Message I add a DynamoDB Update Item operator followed by a response Transform Message.

 

In the request Message Transform I employ a custom function to parse the input payload into an update expression suitable for the Update Item API based off if todo and/or completed is present in the update payload request. Then the body of the transform returns a key property to specify the exact Todo to update along with an update field for the exact properties of a todo to update.

Next in the Update Item operator I set the appropriate table name and return values to ALL_NEW. I also use dataweave expressions to parse the previous transform's payload key, update expression and, attribute values.

 

For the response transform I use a similar mapping from the response of Update Item to the expected return format of the API interface definition.

 

Of course the last step is now to tie the put:\todos\{created} subflow to the newly created implementation via a Flow Reference where the placeholder Transform Message operator was initially.

Here is the complete updateTodoFlow XML definition.

	<flow name="updateTodoFlow" doc:id="22800e35-030c-4bb9-b23b-038743f5fd44" >
		<ee:transform doc:name="Request Transform" doc:id="5d5477c6-27f7-44b4-a513-b7637ac48a0a" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
fun parseUpdate(payload) = do {
	if (payload.todo? and payload.completed?)
	  {
	  	updateExpression: 'SET todo = :todo, completed = :completed',
	  	attrs: {
	  		":todo": {"S": payload.todo},
	  		":completed": {"Bool": payload.completed}
	  	}
	  }
	else if (payload.todo?)
	  {
	  	updateExpression: 'SET todo = :todo',
	  	attrs: {
	  		":todo": {"S": payload.todo}
	  	}
	  }
	else
	  {
	  	updateExpression: 'SET completed = :completed',
	  	attrs: {
	  		":completed": {"Bool": payload.completed}
	  	}
	  }
}
---
{
	key: {
		"userID": {"S": attributes.headers['x-user-id']},
		"created": {"N": attributes.uriParams.created}
	},
	update: parseUpdate(payload)
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<dynamodb:update-item doc:name="Update item" doc:id="b6641a3a-bf97-4a09-b763-923b1fd2d592" config-ref="Amazon_DynamoDB_Configuration" tableName="mulesoft-todos" returnValues="ALL_NEW" updateExpression="#[payload.update.updateExpression]" attributeValues="#[payload.update.attrs]">
			<dynamodb:key ><![CDATA[#[payload.key]]]></dynamodb:key>
		</dynamodb:update-item>
		<ee:transform doc:name="Response Transform" doc:id="9ea4482b-e8c9-4e70-840d-3c281e9dc691" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	userID: payload.attributes.userID.s,
	created: payload.attributes.created.n as Number,
	completed: payload.attributes.completed.bool as Boolean,
	todo: payload.attributes.todo.s
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
	</flow>

Now I can send a PUT request off using my HTTP Client to test the functionality.

$ http PUT :8081/api/todos/1661470771 "x-user-id:657" todo="Take out the trash" -b
{
    "completed": false,
    "created": 1661470771,
    "todo": "Take out the trash",
    "userID": "657"
}

Implementing DELETE /todos/{created} API

One last API implementation to go. I head over to the implementation.xml file and drop a final Transform Message operator onto the bottom of the canvas and name the resulting flow deleteTodoFlow. Following the Transform Message operator I add a DynamoDB Delete Item operator followed by a Set Payload.

 

The transform message builds a key definition to match to the Todo of interest similar to earlier flows. The Delete Item operator just needs the table name set to mulesoft-todos and the set payload needs a message of Todo deleted.

Of course the final implementation step is to connect the delete:\todos\{created} interface definition to the deleteTodoFlow implementation via a Flow Reference.

Here is the final deleteTodoFlow XML.

	<flow name="deleteTodoFlow" doc:id="8bd139a2-497a-4ef4-b3f5-80eee5bfb712" >
		<ee:transform doc:name="Request Transform" doc:id="11d1fdc0-42e0-46c8-923a-016ec14d7e35" >
			<ee:message >
				<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
	userID: {"S": attributes.headers['x-user-id']},
	created: {"N": attributes.uriParams.created}
}]]></ee:set-payload>
			</ee:message>
		</ee:transform>
		<dynamodb:delete-item doc:name="Delete item" doc:id="971b30e9-c143-456e-b455-fcf60c1c1bde" config-ref="Amazon_DynamoDB_Configuration" tableName="mulesoft-todos"/>
		<set-payload value="Todo deleted" doc:name="Set Payload" doc:id="6ca831c7-ff77-495f-8b8c-0c1727a7270b" />
	</flow>

Lastly, I test the implementation with a HTTP request.

$ http DELETE :8081/api/todos/1661470771 "x-user-id:657" -b

Todo deleted

Deploying to CloudHub

For completeness (and to fully take advantage of my free 1 month trial of Anypoint Platform) I end by deploying this Todos REST API to CloudHub. In the design perspective of Anypoint Studio right click the project in Project Explorer then pull up the Anypoint Platform submenu and click "Deploy to CloudHub". You may be asked to login.

Once logged into Anypoint Platform the Deploying Application dialog should open up then over in the Properties tab I add the "secure.key" property before deploying the application.

Now in a browser I head over to Anypoint Platform to checkout the app in Runtime Manager and test the API running from CloudHub.

Conclusion

In this article I presented a detailed walk through of how to design and implement a REST API to manage user Todos saved in the popular NoSQL fully managed AWS Database DynamoDB using the MuleSoft DynamoDB Connector.

As always, I thank you for reading and please feel free to ask questions or critique in the comments section below.

Share with friends and colleagues

[[ likes ]] likes

Community favorites for MuleSoft

theCodingInterface