Published on

Amplify Studio: An Identity Crisis

Authors

Good Intentions

I have really tried to like Amplify Studio, and I want to see the offering succeed. However, the developer experience has been frustratingly poor. On the one hand are tactical problems, where things just break in ways that leave you thinking "surely this would have been caught with basic QA". On the other hand, there are strategic problems with what Amplify Studio is trying to be.

I'll start by articulating what I feel Amplify Studio should focus on from a strategic perspective. I'll then do a deep dive into a specific issue that I think captures my frustration with Amplify as an offering. I hope that example properly motivates why I feel that the offering needs to narrow its focus.

Do One Thing Well

In my opinion, Amplify Studio does not have a clear product focus. I feels like it is trying to be the following three things, and doing all of them poorly:

  1. A UI alternative to the cli offering
  2. A low-code UI builder for rapid prototyping
  3. A headless CMS for non-technical or semi-technical users

UI Alternative

As a UI alternative to the cli, it doesn't support a number of Appsync model annotations that are essential for real world applications. Feature parity aside, a minimum expectation for Amplify Studio is that it should not break when using an Amplify cli supported feature (or at least should break gracefully). I like the data modeler when it works, but I can still get the job done using the cli. I'd much rather Amplify cli be wholly responsible for simplifying the creation of boilerplate functionality for CRUD apps (database, file storage, auth, api, etc...), and have Amplify Studio solve the problem of content management, which is a gap in AWS's existing offerings.

UI Builder

I haven't tried the Figma prototyping tool yet, but given how rough Amplify Studio's core functionality is, I'm frustrated the offering even exists. I've never seen a low-code tool for translating design into code that was worth the trouble, and I don't think Amplify is going to crack this problem.

CMS

If Amplify Studio needed to focus on just one thing, I would like to see more focus on the headless CMS functionality. As a headless CMS, Amplify Studio's functionality is fairly limited. Why can't I explore DynamoDB models that don't conform to Amplify's Datastore requirements? Why don't users with content level access to Amplify Studio have more information about application usage if there's already an integration Pinpoint? I'd like to be able to bring in non-technical users to start pushing out content without building an admin UI for them. They should be able to see engagement metrics for the app and for specific pieces of content.

Quality Issues

A developer's first experience with a new platform is important. There was a time not too long ago when one of the 3 sample graphql API's you could create with Amplify cli completely broke the Data model UI in Amplify Studio because of the way the cli modeled the relationships. One of the sample apps for doing auth with Gatsby looks to be abandonware (doesn't work), but still features prominently as one of the 4 starter apps you can use to explore the framework.

gatsby project

It is very easy to break all of the Content UI/Datastore by making pretty vanilla changes to your graphql schema that are allowed by the cli, but not by the Data modeler UI. The list goes on, but getting started with Amplify is a rough experience.

But let's say we push past the initial experience and chalk it up to a learning curve. Can you still build a reasonable CRUD application using Amplify Studio? Let's look at a common feature of most applications, a user profile. Surely helping developers build this should be a straightforward thing for a tool like Amplify?

Getting users signed up either through traditional username/password flows or one of the supported Oauth providers is actually quite easy in Amplify Studio. The studio provides a nice abstraction over AWS Cognito, and the SDKs make it easy to leverage that functionality in your app. So far, so good. But what if you want to create a user profile? Well the first thing you could try is just leveraging custom attributes as part of the Cognito service. The SDK again provides a nice API for achieving this both before and after signup. But there are limitations to custom attributes, not least of which is that they can't be removed or changed once added to the user pool. Those attributes are also not visible in Amplify Studio. Finally, your application may need to expose some user profile information as part of other data models, like the displaying author information along side of a blog post.

So, how do you go about creating a user profile as just another data model in Amplify Studio? At a minimum we want only one profile per user and to protect the profile so that a user is only able to update their own profile. In theory there are two approaches that could work here.

  1. Have the client check for the user profile on an auth event and then create the profile if not found
  2. Use Cognito postConfirmation trigger to invoke a Lambda function to create the profile

Client Creation

Let's say you aren't comfortable with Lambda functions and just want to do profile creation client side. You open the Data modeler in Amplify Studio and try to create a Profile model. There's an option to "Enable owner authorization", which feels like it might work. So you create the model and you notice that the id field is mandatory. You can't change the primary key. Ut oh...

model error

The problem with this approach is that if you defined your data model in the Amplify Studio, you can't garuntee only one profile per user. Maybe for a prototype you can get away with it, but you shouldn't rely on client side logic to enforce unique constraints.

So next you might think, even though Amplify Studio won't let me change the primary key, I can update the schema definition using Amplify cli. The promise of Amplify after all is freedom of diving to a deeper abstraction layer when the need arises. So you make the following modification:

type Profile @model @auth(rules: [{ allow: owner, ownerField: username }]) {
  username: String! @primaryKey
  email: String
  favoriteColor: String
}

Things are looking good, with the primaryKey specified as the username (which will correspond to the username in Cognito), a user should only be able to create one profile and have ownership over said profile. So we run amplify push and pop back over to Amplify Studio.

The model appears in the Data modeler. We then pop back over to the Content screen to add some seed data, and we see this:

model error

Looks like we've broken the Datastore feature of Amplify with this change. What's frustrating here is that I am perfectly fine with Datastore requiring a GUID as the primary key if it is a requirement for Datastore's sync functionality to work. Not all of my data models are going to meet that requirement, and instead of working with the models that are supported, the entire Content UI breaks. While you can implement some exclusion criteria using the client SDKs to determine which models get synced, there's currently no way to blacklist specific models from the Datastore globally.

Cognito Trigger

But let's say you really want to use Datastore in your app instead of raw graphQL queries. And you want to use Amplify Studio's Content screen to explore your data. You decide that maybe a Lambda function triggered by Cognito is the way to go. In many ways this is a better approach, since your clients no longer need implement their own profile creation logic. However, you will need to account for a potential delay between account creation and the profile being available in your app, so in some ways you're just shuffling around the complexity. As luck would have it, there's even a tutorial on how to set this up.

So you follow the tutorial as written, sign up a test user, and hop back into Amplify Studio to look at your profile data. However, now you get a sync error when selecting the User model from the tutorial. It's better than the whole screen being broken, but still frustrating. You take a look at your tables in DynamoDB for models that do work and see fields that were not mentioned in the article: _version, _lastChangedAt. You also know Amplify will look for an owner field if you enable owner authorization. So you modify the Lambda function's param's Item object to look something like this:

      Item: {
        id: { S: event.request.userAttributes.sub },
        __typename: { S: "User" },
        email: { S: event.request.userAttributes.email },
        createdAt: { S: date.toISOString() },
        updatedAt: { S: date.toISOString() },
        _version: { N: "1" },
        _lastChangedAt: { N: Date.now().toString() },
        owner: { S: event.username },
      },

Thankfully this works, and you're able to move on. But you're left wondering why what should be a set of common, straight forward tasks created so many problems for Amplify Studio.

Conclusion

Amplify as a complete offering holds a lot of potential. I use many of the underlying services that Amplify wraps as part of my job and could stitch together all of the component services if needed. I would still prefer to use Amplify because I like the convenience of using a consolidated client SDK for building apps and the longer I can go without building an admin UI managing app content, the better.

I would love to see Amplify Studio evolve into a more focused CMS. There is a lot more that it could be doing to help non-technical users manage content and gain application insights. I'd like Amplify cli to focus on bridging AWS infrastructure to a consolidated set of client SDKs (javascript, iOS, Android), so it has a reason to exist outside of CloudFormation or AWS CDK. This would result in a great developer experience, that would allow developers to rapidly build applications with the confidence that the underlying infrastructure is just AWS.