Amazon Web Services Feed
Building your App from Idea to MVP: Part 2
By Nader Dabit, Sr. Developer Advocate, AWS Amplify, originally published on Y Combinator’s Startup School
In part one of the Building a Mobile App series, you learned how to unpack your mobile app idea. In part two, will learn how to build a full stack serverless JAMstack ECommerce store using Gatsby, AWS Amplify, and JAMstack ECommerce.
While this post focuses on the specific use case of building an ECommerce application, the services and features I will showcase are the building blocks for most all real-world production applications, so I hope you will find it useful.
Laying the groundwork
For this site, I’ve chosen to use Gatsby in order to get the benefits of a static site, including better performance, better SEO, and cheaper / easier scalability. Next.js (React) and Nuxt (Vue) are other options that would do the job just as well, but I’ve gone with Gatsby because of my previous experience with it as well as the robust developer community and documentation available at the time of this writing.
The app we will be building has the following features:
- Ability to query inventory from an API
- At build time, create navigation based on inventory categories
- At build time, create pages for each inventory item and category pages for each nav item along with corresponding views
- Shopping cart / checkout
- Admin panel for creating / updating inventory
- Downloading of images at build time to serve from the public folder vs dynamic fetching
Based on these features, we can assume that the app will have the following requirements from an API / service standpoint:
- Authentication (sign up, sign in)
- Dynamic group authorization (only Admin users can view and update inventory)
- API with create, update, and delete operations
- Public API access for querying the API
- Private API access so that only Admin users can create / update / delete inventory
- Image / asset hosting
To build out these features on both the front and the back end we will be using the Amplify Framework:
- Amplify CLI for creating and configuring AWS services
- Amplify Client libraries for interacting with the services
- Amplify Console to host and view the app and features after they are deployed.
Let’s start building!
To follow along with this tutorial, you need to have an AWS account.
Getting started
To get started, clone the Gatsby JAMstack ECommerce starter project that will serve as the base of the application we’ll be building:
Next, change into the directory and install the dependencies using npm or yarn:
Next, start the project to get an idea of how the app will look:
When the app loads, you should be able to go to http://localhost:8000/ and see something like this:
Great, we’re now up and running!
You may be wondering where the inventory is coming from. Starting off, the inventory is hard-coded in the inventory file located at providers/inventory.js.
This is not ideal though because keeping up with everything locally is hard to scale. Instead, we propose to make the inventory dynamic and be able to add and update inventory via and admin panel using some type of content management system.
To do so, we’ll need to set up an API. To start, create the Amplify project so we can begin migrating the inventory provider to a real back end provider.
Installing Amplify and initializing an Amplify project
Before you can use Amplify, you’ll first need to have or create an AWS Account.
Next, install the Amplify CLI globally from the command line:
If the CLI is installed, you should be able to run the amplify command and see some output and help options.
Now that the CLI is successfully installed, we now need to configure the CLI. To do so, run the configure command:
This will walk you through the steps to create and configure AWS user credentials locally. For a guided walkthrough of these configuration steps, check out this video.
Creating the Amplify project
After the CLI has been configured you can create a new Amplify project:
When prompted for an AWS profile, choose the profile you created in the configuration step.
After the initialization has been completed, you should now see 2 artifacts created for you in your project directory:
src/aws-exports.js – This file will hold the key value pairs of the resource information for the services created by the CLI.
amplify directory – This will hold the back end code we write for things like GraphQL schemas and serverless functions managed by the AWS services we’ll be using.
Now that we have the base project set up, let’s also go ahead and install the AWS Amplify client library:
Creating the back end services
Now we are ready to go and can start creating the services we’ll be integrating into the app. Let’s first start with authentication.
Authentication
The authentication setup for this app will need to accomplish the following things:
- Enable users to sign up and sign in
- Detect Admin users based on a predetermined list of admins and place them in the Admin group once they sign up.
We can do this with a combination of Amazon Cognito (managed authentication service) and AWS Lambda (functions as a service).
We’ll create an authentication service that will call (trigger) a Lambda function when someone signs up (post-confirmation). In that function we can determine whether or not they will be allowed Admin access based on their email address.
To create the service, we’ll use the Amplify add command:
Now, let’s edit the code for the post-confirmation Lambda trigger. In amplify/backend/function/function_name/src/add-to-group.js, use the following code:
Update the adminEmails array to include the emails you’d like to allow Admin access.
This function will add a user to the Admin group if their email is included in the adminEmails
array.
Storage
Next, let’s create the image storage service using Amazon S3:
API & database
The last thing we need to create is an API and a database to store our data. This API needs to allow both authenticated and unauthenticated access.
Authenticated Admin users should be able to create and update items in the database while unauthenticated access will allow us to query the API at build time to fetch the data needed for the application.
To allow this, we’ll create an AWS AppSync GraphQL API & Amazon DynamoDB NoSQL database using the CLI:
This should open the GraphQL schema located at amplify/backend/api/postershop/schema.graphql. Here, update the schema to be the following:
This GraphQL schema has a few additional directives that you might not see on a traditional schema:
@model – This directive will scaffold out a DynamoDB database, addition CRUD (Create, Read, Update, Delete) & List GraphQL schema operations, and GraphQL resolvers mapping between the operations and the database.
@auth – This directive allows us to set up authorization rules on either a GraphQL type or field.
These directives are part of the GraphQL Transform library of Amplify. To learn more about this library and these directives, check out the documentation here.
In the schema we’ve created, we want to have two authorization types:
- Admin users can perform all operations
- Public access to read items
The services should now be configured and can be deployed to AWS. To do so, we can run the push command:
$ amplify push --y
All of the services have now been deployed and we can start integrating them into the the client application!
To view the AWS services that have been created at any time, open the Amplify console with the following command:
$ amplify console
Client integration
Now that the back end services are deployed, the next thing we need to do is configure the Gatsby project to recognize the Amplify project. To do so, open gatsby-browser.js and add the following code:
Client authentication
Once the client app is configured, implement authentication for the admin panel. To do so, open src/pages/admin.js and import the Auth
class from Amplify:
// src/pages/admin.js
import { Auth } from 'aws-amplify'
Next, modify the signUp
, confirmSignUp
, signIn
, and signOut
methods to the following:
Authentication is now enabled and users can begin signing up and signing in to view the inventory.
In the bottom right navigation, click on Admins to view the admin panel to sign up and sign in.
In this component, we use a few different methods on the Auth class like signUp
and signIn
. Auth has over 30 different methods for handling user authentication. To learn more, check out the documentation here or the API here.
Next, let’s test it out:
Client API integration
Now that we have authentication working, let’s use the API to create and update data in our app. To do so, we’ll be first working with the inventory provider located at src/templates/ViewInventory.js. Here, let’s update it to fetch data from our real API.
First, import GraphQL query and the APIs needed from AWS Amplify:
You’ll notice that when we run the app and console.log the items coming back from the API, there is an empty array. This is because we have yet to create any real items in our database.
To add the ability to create items, we’ll need to make some updates to src/components/formComponents/AddInventory.js.
First, update the imports to add the following:
Now, you’ll notice that you can create items and when we view the inventory, they show up!
Client Storage integration
One odd thing you’ll notice is that the images do not show up in the inventory view. This is because we are attempting to render an image key from S3 that is not yet signed. We can fix this by opening the image component at src/components/image.js and adding image signing from S3.
We will check to see if the image is a locally downloaded image (if the image path includes downloads). If it does not, then we know it is a remote image from S3 and we will fetch the signed URL for the image.
First, import the Storage
class from Amplify:
Next, update the fetchImage
function to this:
Now, we should see the images rendered in the list.
We next need to enable the editing and deleting of items. You’ll notice that if you edit an item in the Admin view and refresh, the changes do not persist. To fix that, open src/templates/ViewInventory.js and make the following changes.
First, import the updateProduct
and deleteProduct
mutations:
Next, update the saveItem
and deleteItem
methods to the following:
Now when we save an item, the updates also go to the database!
Build-time API integration
Finally, we need to change the build step to use the new API we’ve created instead of the hard-coded inventory data we’ve created. When we run gatsby develop or gatsby build, we will use the public API access to enable the system to query the data from the API and use it for the app.
We also want to include in the build step a way to download the images locally in our project so we are not fetching remote images, instead we are rendering a local copy of the image that we will be downloading and storing in a local downloads directory in the public folder.
For this to work, first create at least 4 items in your inventory from the admin panel.
Next, create downloadImage.js in the utils folder. This function will allow us to download images locally using the file system (fs
) module:
Now open gatsby-node.esm.js. Add the following imports and statements at the top of the file:
Next, create a new function called fetchInventory
to fetch inventory from our new API and place the function anywhere in gatsby-node.esm.js.
This function will also map over all of the inventory items and download the images locally at build time using the downloadImage
function that we created in the previous step:
Finally, in exports.sourceNodes
and exports.createPages
update the calls to getInventory
with the new fetchInventory
functions:
Run the develop
command to test it out:
Running a new build will fetch the data from the GraphQL API and create a new navigation based on the updated product categories and also build out a new static version of the site.
Conclusion
At this point, you are up and running with an MVP of a real-world and scalable ECommerce application running on AWS!
From here, you may want to dive deeper on the Amplify documentation to learn more about the APIs and services we’ve used as well as the other APIs that we’ve not yet worked with.
So far we’ve set up the following features:
- Authentication (Amazon Cognito)
- API (AWS AppSync)
- Storage (Amazon S3)
You might also be interested in learning about:
- Predictions (ML / AI)
- Interactions (chat bots, Amazon Lex)
- Analytics (Amazon Pinpoint)
- REST APIs (AWS Lambda + Amazon API Gateway)
Next steps
Here are a few things you can do to continue improving this app.
Hosting – deploy to the Amplify Console
If you host your app in GitHub, BitBucket, GitLab, or AWS CodeCommit, you can easily deploy the entire site to live hosting and add a custom domain in just a few minutes using the Amplify Console. To see a quick video of how to do this with a Gatsby site in less than one minute, check out this video.
Configure server-side logic to process the payments with Stripe. You can do this easily from where we currently are by adding a serverless function and API using Amplify and the API category:
If you’d like to see an example of the function code needed to interact with stripe, check out this code snippet.
Also, consider verifying totals by passing in an array of IDs into the function, calculating the total on the server, then comparing the totals to check and make sure they match.
Update inventory items as they are purchased
To keep the inventory up to date, you probably want to decrement the inventory as a purchase is made. To do this, you could send an update request to decrement the database before an order was confirmed.
To make this even more secure, you could use a DynamoDB Transaction to only process the order if there was enough in the inventory and decrement the number of items if there are any in the inventory in a single operation.
To learn more about Amplify, check out these resources: