AWS Amplify beyond the quickstart

Tips & tricks to integrate amplify in a real-world project

Aug 6 · 10 min read

If you know a little bit the React ecosystem, the arrival of Create React App (CRA) has probably been a relief for you. We all know how complicated it can be to set up modern tooling (including webpack, eslint, babel, hot reload server, …). CRA offers all this in a simple command line: Simply run 2 or 3 commands and voilà! Of course, it won't fit for every project. But when you just want to prototype something, you will save many hours. And you're free to eject your app at any time if you need more configuration capabilities.

Well, I am not going to speak about CRA today, but I find the analogy with AWS Amplify quite relevant.

Ready to go?

I will start with the basics and go deeper progressively, so feel free to skip to the section that interests you the best:

  1. It does everything, even the coffee, right?
  2. So what’s the deal, should I use it?
  3. Integrating into a real-world project
  4. Final thoughts

This article is the logical continuation of previous articles I wrote about serverless architectures and the Serverless framework. If you feel a bit lost at some point, you should have a quick look at these two articles:

1. It does everything, even the coffee, right?

Sorry to disappoint you, but the coffee category does not exist yet (I'm sure it's in the roadmap though). However, it's doing plenty of other cool things! After experiencing with it on a side project for 5 months, if I had to sum up Amplify in a sentence this is what I would say:

Amplify is a full-stack serverless framework

Before Amplify, AWS was already providing a great Backend as a Service (BaaS) toolset, meaning you were able to create a complete serverless backend without managing anything (including API, storage, database, authentication, authorization, …). But when it comes to integrating with your frontends (web or mobile), you were left alone with no pre-designed architectures recommendations. This is, I think, the main innovation brought by Amplify.

So, what is amplify made of?

  • A CLI, this is where it all starts. Run npm install -g @aws-amplify/cli and start your project. The CLI allows you to create a project and add resources from various categories in it (api, auth, hosting, storage, …). Under the hood, it creates CloudFormation stacks and manages them.
  • An API as a service: simply configure your data model using GraphQL Schema Definition Language (SDL), and Amplify will take care of creating and managing all the resources for you (DynamoDB tables, AppSync resolvers, Lambda functions, …). It also offers subscription using WebSockets to add some real-time features to your app.
  • A user management system, on top of Cognito User Pool and Cognito Federated Identities. Handle MFA, social providers and all the boring stuff in a snap.
  • A static website hosting service. Running amplify push in the configured environments will build your frontend app, deploy it on S3 and configure a CloudFront distribution for you. This is very similar to Netlify.
  • A UI library. Amplify provides UI components for React, React Native, Angular, Ionic, and Vue. Don’t expect too much on this, but I find the Authenticator component quite useful when building a prototype.
  • A CI/CD platform. Yep, with Amplify Console you can setup a proper CI/CD pipeline that follows the git flow, create feature environments on-the-fly, configure domains and restrict access, well all your DevOps!
  • Analytics (user tracking), push notifications, AR/VR, machine learning, chatbots, and probably more to come. I feel that AWS is using Amplify to showcase lots of new features.

2. So what's the deal, should I use it?

Well, it depends on your use case.

Pros

  • Focus on your business, do not reinvent the wheel. What is the point of building your own authentication system while you could use an existing one that already handles MFA, social providers, and all the enterprise features?
  • Prototype cool things with Amplify and eject features one by one when you have to. Amplify allows you to do high-quality proof of concepts at no cost, so why not take advantage of it? As an example, you could build your API with generated AppSync resolvers and replace them one by one by custom resolvers when required (similarly to Create React App).
  • Good multi-environment design for good DevOps. You can bootstrap new environments on the fly. Also, your amplify environments can follow your git workflow.
  • Serverless and cheap as hell. In most cases, the free-tier should be enough. My 4 environments (production, staging, and 2 users) only cost me a 2–3$ a month in total, and most of it is the CI/CD, which won't scale in the same proportion that resources (like compute or storage).

Cons

  • A steep learning curve. Integrating all these modern technologies implies understanding their inner mechanism if you want to go beyond the examples. However, once you get the experience, it's as easy as pie!
  • Limitations of the framework. While it is heavily maintained by AWS, some small issues can be really blocking and prevent you from using it.
  • Strong AWS lock-in. Don't go for Amplify if you're thinking about migrating to another cloud provider, it’s obvious.

My recommendation would be to try it on a side project to understand it better and gain experience, and then decide by yourself if it fits your application.

3. Integrating into a real-world project

So you decided to give Amplify a try? OK, let's move on to the serious stuff now! In this section, I will share how to integrate Amplify with Serverless framework, how to manage environments and a bunch of other tips. This is not an introduction to Amplify, so have a look at the quick start before reading this part.

To illustrate my words, let's say we want to create a blogging app with Amplify. Also, let's name it Podium (yeah, I like bad puns). Authentication will be managed through Cognito, and we will use AppSync and DynamoDB to store the blog posts and comments. The frontend will be a React SPA, but we plan to add mobile apps later. Lastly, we will store post images on S3.

Also, please note that this isn't a step-by-step tutorial. The goal is to see on a high-level how we can integrate the different pieces of our project, not to get very specific on details.

Code organization

Well, I am a monorepo advocate. So this will be our repo structure:

  • cli/ We will create a small CLI to help us automate tasks.
  • commons/ Shared Node.js library that will be used in the CLI and when packaging our Serverless services.
  • fixtures/ Contains all our testing data (JSON files to seed our DynamoDB tables, and images to seed our S3 bucket).
  • mobile/ could contain our mobile apps in the future (and reference the web/amplify/ folder).
  • services/ Serverless services, for everything we can't do with Amplify.
  • web/ Our React application. Will contain a amplify/ subfolder. Every amplify command should be run inside the web/ folder (which will be our main frontend).

GraphQL Schema

We will keep it super simple. Two models: Post and Comment.

web/amplify/backend/api/podium/schema.graphql

Podium CLI

To ease development, we can create a custom CLI using tools like oclif. Some commands that could be useful:

  • podium setup-env A command to run after amplify push to customize our environment with features that aren't handled by Amplify natively. For instance, create a user group "admin", change the API authentication type to AWS_IAM and customize auth and unauth roles policies, … This command will be used in ou CI/CD.
  • podium seed:{all|cognito|dynamodb|s3} Seeds our environment with data from the fixtures/ folder. seed:cognito will create some standard users and add them to the admin group. seed:dynamodb will fill DynamoDB tables with JSON files, and seed:s3 will upload local files to the Amplify S3 bucket.
  • podium flush:{all|cognito|dynamodb|s3} Flush our environment (delete Cognito users, flush DynamoDB, empty S3 bucket). This is nice to do before a seed to ensure you have a clean environment (in particular when running tests).

Nothing complicated here, you can use the AWS SDK to create/update/delete these resources. The only tricky thing is that you need to resolve Amplify resource names, like bucket or table names, and they often contain some ID that change depending on the environment. To overcome this, we will add the following file in the commons/ folder (it will be used by serverless as well later).

content of the commons/ directory

If this looks like an ugly trick, it's because it is one. We are retrieving the resource names from various amplify files, sometimes by parsing them with regular expressions 🤢. But if you are ready to accept this, I promise the rest will be super smooth 👌. For instance, our Post table name can be generated like this: Post-${getApiConfig().id}-${getCurrentEnv()} so you can easily code to seed your tables.

Serverless integration

Now, you want to let the user upload images so they can add them in their post. Nothing easier with the Storage category. But you might also want to run some backend code to ensure the image conforms to your ToS (no sexual content). If you're used to serverless design, this is what you might want to do:

  1. Upload the image on S3 using Amplify: Storage.put(`${postId}_filename.jpg`, blob). Note that we are including the post ID so we can use it later.
  2. Automatically trigger a lambda that will download the image and check its compliance. If it is not, we want to unpublish the post.

Amplify does offer a function category, but we're going to be much more limited than with a framework that focuses on event-driven functions, like Serverless. Let's create a podium-images service:

Content of services/images/ directory
  • As you can see, we reused our functions from commons/amplify.js to resolve resource names within the Serverless context, in the serverless-helpers.js file.

The web app

I don't have much advice concerning the web app, because here it is a standard React + Apollo application. But still, a few things I learned:

  • Keep the default Apollo fetch policy to cache-first. Sometimes it seems easier to change that parameter to fix update issues, but in general, it is just a workaround. Instead, learn how to use fetchMore and refetch.
  • Do not directly use auto-generated queries and mutations. To fit all your needs, you will probably have to set a high depth (like 8), meaning you will always query a huge quantity of useless information. Instead, you can just copy/paste generated queries/mutations and keep only the information you need.
  • Amplify UI components aren't amazing. The Authenticator can be useful because it covers a lot of different actions (login, register, confirm identity, reset password, …). But if you feel you are doing too much customization, take some time to rebuild your own components from scratch.
  • Last but not least, you might wonder how to merge paginated items when you run fetchMore. For instance, if you run a getPost query, the comments will be paginated and you might want to get them all progressively. Here is a small helper that will help you to achieve that:

CI/CD

Now that we have a full-stack app working like gangbusters, let's have a look to the DevOps side.

First, I highly recommend following the multi-environment workflow provided by Amplify. In particular, it allows you to bootstrap a new environment on the fly to test a feature (super useful in your CI/CD). You can also develop with isolated environments for every developer.

Once you created at least 2 main environments (like staging on develop and production on master), let's automate deployment with Amplify Console. Here again, no step-by-step tutorial but it's super easy to configure your repository, branches, and domains. You can define environment variables and override them in some environment, you can restrict access to some environment, …

However, you might wonder how to deploy your Serverless services. Here is my amplify.yml file (must be at the root of your repo):

As you can see, we install the common lib and the CLI first. Then we run amplify push (using the amplifyPush script available by default). Finally, we deploy our podium-images Serverless service.

Also, note that in the frontend postBuild step, we run podium setup-env —-yes. This applies all the configuration that we can't do natively with amplify (like creating an Admin group).

Indeed, we could run tests before deployment, and plenty of other cool things. But this seems like a good start.

4. Final thoughts

You might like Amplify or not, I believe it is showing the path of tomorrow's app development. Backend development will become less and less necessary with the adoption of Backend as a Service. I am not saying it will disappear, but it will focus only on your business.

The future I predict to backends as we know it

My wishlist to the team

  • Make amplify console work with multiple AWS accounts, because we don't want to develop on our production account. The ideal would be to configure the CI/CD in an isolated account, and it would create resources in different accounts.
  • Allow to use docker in the CI, so we can build docker images. I know docker is not in the scope of Amplify, but this could prevent from using Amplify Console if you need to run your own image. For instance, our image validation algorithm could run in a Fargate container deployed with Serverless, and we would want to build and deploy in the CI/CD the image from the Dockerfile in the repo.
  • Improve the multi-frontend workflow. Ideally, the amplify/ folder should be at the root of our repo, so we can use the amplify CLI everywhere. This is already tracked here.
  • Stop using unique IDs in CloudFormation stacks. We should have custom parameters only in parameters files. To go further, maybe the CloudFormation stacks should be generated on-the-fly when running a amplify push. But this will be achievable only when the framework will have reached a good stability level.
  • Improve documentation with real-world examples. I bet many people let their API unsecured because the documentation is unclear on many points. Creating custom resolvers is not a piece of cake either.

That's all for today. I know many details are omitted, so if something isn't crystal clear feel free to ask in the comments. And please share your tricks as well.

Happy coding 🤓

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade