Giter Site home page Giter Site logo

alexa-dynamodb-crud's Introduction

How to perform Create, Read, Update, and Delete Operations to an AWS DynamoDB from an Alexa Skill

Introduction

In the current day and age of software development, almost every application requires some sort of API / database integration. With NoSQL databases being used more and more, being able to send Create, Read, Update, and Delete or CRUD operations to an AWS DynamoDB is a necessary skill to have as a developer. While it seems daunting, DynamoDB is incredibly easy to work with, and this article serves to remove the fear of interacting with DynamoDB in Node.JS.

You can follow along with a starter project that has all of the basic files setup for you, or skip right ahead to the finished project. If you run into any issues and need help, feel free to message me on Twitter and I will be more than happy to help you out!

Note: I am going to assume you already have ask-cli configured and proper IAM permissions. If you do not and need to know how to set this up, follow the Setup section of this other Alexa Skill article I wrote

Table Structure and Setup

Here is the structure of my DynamoDB Table. I named mine alexa-skill-table and each document, let's call them a Contact, is going to be formatted as such:

{
  "username": "John",
  "phone_number": "1234567890"
}

Also, you are going to need to update the config.js file located at lambda/custom/config.js and replace all the fields to the fields that are associated with your particular IAM Policy.

const config = {
	DYNAMODB_CONFIG_SETTINGS: {
		accessKeyId: 'YOUR_ACCESS_KEY_ID',
		secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',
		region: 'YOUR_REGION'
	}
};

Finally, before we get started coding, make sure to take a glance over the entire project's files and get a better understanding of the structure. The only files we are going to be changing is api.js which will handle all of our CRUD DynamoDB calls and index.js which where all of our Intent handlers are located. Let's start off by adding the createContact and updating the CreateContactIntent handler in api.js and index.js.


Update createContact and CreateContactIntent

const createContact = async function(contactName) {
	let params = {
		TableName: 'alexa-skill-table',
		Item: {
			username: { S: contactName },
			phone_number: {S: 'n/a' }
		}
	};
	await dynamodb
		.putItem(params)
		.promise()
		.then((res) => {
			return true;
		})
		.catch((err) => {
			return false;
		});
};

Above is what the new createContact function should look like, and it is visibly a fairly basic function, as with all of them will be. Every DynamoDB operation consists of this structure with different params and different leading method names, in this case putItem(params). You will see that very little changes in each API call because of this, showing how easy and intuitive DynamoDB is! Also, notice how we return a boolean value here as we don't need any information besides the confirmation of whether the operation successfully executed.

Now that we have the API call finished, lets update the corresponding CreateIntentHandler in index.js.

const CreateContactIntent = {
  canHandle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;
    return request.type === Requests.INTENT_REQUEST && request.intent.name === Intents.CREATE_CONTACT_INTENT;
  },
  async handle(handlerInput) {
    const { request } = handlerInput.requestEnvelope;
    const contactSlot = request.intent.slots.Contact;
    let res = await API.createContact(contactSlot.value);
    var speechText = `${res ? 'Successfully' : 'Unsuccessfully'} created Contact ${contactSlot.value}`;
    return handlerInput.responseBuilder.speak(speechText).getResponse();
  }
};

The only part of the Intent handler we have to change is the handle property, which invokes our createContact API call and stores the boolean value of whether it performed the action successfully. Finally, we use string interpolation to either have Alexa say "Unsuccessfully created Contact John" or "Successfully created contact John".

With the basic Create operation successfully added, you can try this for yourself by running ask deploy at the project directory and going to the Skill's Developer interface. Go ahead and say Alexa, Ask Voice Phonebook to add John as a contact and head over to the DynamoDB table and see the new document!

Now that we have the ability to Create a Contact, we are also going to need the ability to Delete a Contact, which is done in the next step.


Update deleteContact and DeleteContactIntent

const deleteContact = async function(contactName) {
  let params = {
    TableName: 'alexa-skill-table',
    Key: {
      username: { S: contactName }
    }
  };
  return await dynamodb
    .deleteItem(params)
    .promise()
    .then((res) => {
      return true;
    })
    .catch((err) => {
      return false;
    });
};

As before, we are going to start off by updating the deleteContact function. It looks very similar to the createContact function, but params has the Key property instead and we are calling deleteItem(params) not putItem(params). Also, as this operation does not return anything, we only care whether or not the operation was performed successfully, hence why we return a boolean value.

With the API operation finished, let's go ahead and update the DeleteContactIntent as well.

const DeleteContactIntent = {
	canHandle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		return request.type === Requests.INTENT_REQUEST && request.intent.name === Intents.DELETE_CONTACT_INTENT;
	},
	async handle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		const contactSlot = request.intent.slots.Contact;
		let res = await API.deleteContact(contactSlot.value);
		var speechText = `${res ? 'Successfully' : 'Unsuccessfully'} deleted Contact ${contactSlot.value}`;
		return handlerInput.responseBuilder.speak(speechText).getResponse();
	}
};

This is practically identical to the CreateContactIntent, with just different wording in speechText.

Go ahead and re-deploy the skill and ask Alexa, Ask Voice Phonebook to delete John as a Contact, and head back to your DynamoDB Interface and see that John was removed successfully.

Things change up here a bit, as we are now going to implement the Update functionality to change the phone_number property of a Contact.


Update updateContact API Handler

const updateContact = async function(contactName, updatedNumber) {
	let params = {
		TableName: 'alexa-skill-table',
		Key: {
			username: { S: contactName }
		},
		UpdateExpression: 'set phone_number = :n',
		ExpressionAttributeValues: {
			':n': { S: updatedNumber }
		},
		ReturnValues: 'UPDATED_NEW'
	};

	return await dynamodb
		.updateItem(params)
		.promise()
		.then((res) => {
			return true;
		})
		.catch((err) => {
			return false;
		});
};

This time our API handler looks a little different with the params object. When updating an entry, we now have to provide the UpdateExpression, ExpressionAttributeValues, and ReturnValues properties when sending our request to our table. Then, we change our table operation to updateItem(params) and we are all set. As with the other API calls, we send back a boolean value.

Again, now we have to update the UpdateContactIntent to handle our API call.

const UpdateContactIntent = {
	canHandle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		return request.type === Requests.INTENT_REQUEST && request.intent.name === Intents.UPDATE_CONTACT_INTENT;
	},
	async handle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		const contactSlot = request.intent.slots.Contact;
		const phoneNumberSlot = request.intent.slots.PhoneNumber;
		let res = await await API.updateContact(contactSlot.value, phoneNumberSlot.value);
		var speechText = `${res
			? 'Successfully'
			: 'Unsuccessfully'} updated Contact ${contactSlot.value}'s phone number to ${phoneNumberSlot.value}`;
		return handlerInput.responseBuilder.speak(speechText).getResponse();
	}
};

We have more code going on here because we have to send the new phone_number value to our API call. This should still look familiar though as it is essentially the same code with very few changes. Then, we return a different expression depending on whether or not the database update operation was successfully performed.

You can re-deploy the skill and watch the database interface to see the John Contact get updated when you say Alexa, Update John to 1235552212.

Finally, we will implement the Read operation.


Update readContact API Handler

const readContact = async function(contactName) {
	let params = {
		TableName: 'alexa-skill-table',
		Key: {
			username: { S: contactName }
		}
	};
	return await dynamodb
		.getItem(params)
		.promise()
		.then((res) => {
			return res.Item.phone_number.S;
		})
		.catch((err) => {
			return false;
		});
};

This is very similar besides the getItem(params) operation as well as what we return. Unlike the other cases, we need to get the phone_number property back to the ReadContactIntent, so we return res.Item.phone_number.S to do so. Now, lets update ReadContactIntent.

const ReadContactIntent = {
	canHandle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		return request.type === Requests.INTENT_REQUEST && request.intent.name === Intents.READ_CONTACT_INTENT;
	},
	async handle(handlerInput) {
		const { request } = handlerInput.requestEnvelope;
		const contactSlot = request.intent.slots.Contact;
		let res = await API.readContact(contactSlot.value);
		let speechText = '';
		if (res) {
			speechText = `${contactSlot.value}'s phone number is ${res}`;
		} else {
			speechText = `Unsuccessfully read ${contactSlot.value}'s phone number`;
		}
		return handlerInput.responseBuilder.speak(speechText).getResponse();
	}
};

As before, very similar to the other Intents, but our speechText is very different depending on whether or not the operation is successful. After changing the ReadContactIntent, go a head and re-deploy to test it!

Conclusions

The skill is now finished! Feel free to tinker with the en-US.json model to add additional phrases, utterances, and intents that you want to get a better handle behind Alexa Skills.

Now that you have successfully performed CRUD operations to an AWS DynamoDB from an Alexa Skill, the possibilities are endless for other skills you can create, such as the following:

  • Multi-player Trivia game with progress tracking
  • Decision-based RPG
  • Shopping lists
  • Many, many more

If you have any questions feel free to shoot me a message on Twitter and I will be more than happy to respond you!

If you enjoyed this article, go check out my other articles, such as How to build an Alexa Skill to add Objects to your Bucket via Voice

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.