You'll learn how to build highly scalable cloud APIs with AWS and integrate them with Next.js and style them using Tailwind CSS.
Hybrid Cloud Development with Next.js, AWS, and Tailwind
AI Generated Video Summary
The Workshop covers building a multi-user blogging app using Next.js, Amplify, and Tailwind. It focuses on core functionality like authentication, data storage, API access, and file storage. The workshop also explores server-side rendering (SSR) options and scalability using cloud providers like AWS. Topics include configuring Amplify CLI, creating AWS services, implementing GraphQL API and authorization, and generating dynamic pages with Next.js.
1. Introduction to the Workshop
This is the workshop materials that we're gonna go by. We're gonna just kind of walk through this step by step and build out this app. Everything in this workshop should be copy and pasteable. The actual completed project is available if you get stuck. My name is Nader Dabit, I've been a senior developer advocate on the AWS Amplify team for a couple of years now. This is my first workshop as a non-employee of AWS, but I'm still passionate about this technology. You can follow along and build this out step by step or watch me build it first and then build it yourself. You can submit issues or pull requests if you have any questions or issues.
So as long as everyone can see this, that's great. This is the workshop materials that we're gonna go by. And it's kind of broken up into two main parts. There's the actual readme, which is kind of like the tutorial and documentation itself for the workshop. And this is what we're gonna be following along with. So everything in this readme, of course, is just kind of displayed here. And the way that this is gonna work is that we're gonna just kind of walk through this step by step and build out this app.
Now, everything in this workshop should be copy and pasteable. So all the code should work, as expected, I guess you could say. So, of course, when you're doing a workshop like this, it's often recommended to not copy and paste it, to actually type it out. But if you do run into like a bug or something like that, and you can't figure out if you did something wrong, you can always kind of resort to that. And then the other thing that is here is the actual completed project. So you can actually go into this Amplify Next folder right here, and you should see a completed version of the project. So if you do get stuck somewhere between actually copying and pasting the code and running it yourself, or going into this completed project, you should kind of be able to kind of not get stuck. Or if you do get stuck, you should be able to get unstuck. So, take that into consideration kind of when you're building this out.
And with that being said, I guess I'll go ahead and give an introduction and kind of talk about what I'm doing and who I am and stuff like that. So, my name is Nader Dabit. I've been a senior developer advocate on the AWS Amplify team for a couple of years now. And the name of the team is AWS Front End Web & Mobile. And I started there in 2018, in January. And I actually worked there up until last Friday. So last Friday was my last day at AWS. And now I'm working at a new company called Edge & Node, which is kind of in the GraphQL and Web3 and crypto DeFi space. But I'm still going to be doing a lot of AWS stuff. I'm not like not doing that anymore as well. So I'm kind of like now going to be doing this new world of things, which is kind of like the decentralized DeFi, Web3, and kind of like even crypto space a little bit, but I'm also going to be continuing to do AWS stuff. I still have my book React Native in action and a full stack serverless, full stack serverless B2 is coming out in a couple of months. So yeah, I'm going to be keeping up with all that stuff, but yeah, this is kind of my first workshop I'm giving where I'm not an employee of AWS, but I still am really, really excited and kind of like passionate about this technology. And I kind of have seen the potential of it, the growth of it, all the big companies like picking it up and it's exciting to be part of this space as well. So just wanted to kind of throw that out there that I'm not really like going anywhere. So what I'm going to be doing is kind of like switching between my terminal, the workshop and my text editor and building along and you can follow along.
And there's a couple of ways to follow along. And we were talking about this earlier in the Slack or Discord earlier. You can either follow along and try to build this out step by step with me, or you can actually take this workshop and watch me build out the first hour or the full three hours and then go and build it yourself after that. It's kind of up to you. It's completely up to you. You can follow along, you can do this later after you kind of get an idea of how this all works. You can actually like get ahead of me. So if you're building it, I'm going too slow because I'm trying to kind of, with over a hundred people, it's kind of like you have to just draw a line somewhere where you're going to be a little bit too fast for some people, a little bit too slow for other people. So I'm not expecting everyone to kind of like follow along exactly, but feel free to go ahead or feel free to fall behind, and I would not worry about it because again, everything should be able to be copy and pasted. And hopefully the value that you get out of this is kind of being able to ask questions and me explaining the things from my perspective of being within this technology for a little over three years. And then the other thing is that since this is an open source project, you can actually just submit issues or pull requests. So if you have any questions or issues, you can submit these and hopefully like, I can answer those within like 24 hours or something like that. I'm gonna be keeping a close eye on the workshop for the next week at least, as far as issues are concerned. So if you've got a run into any issues, feel free to open up an issue, feel free to also reach out to me on Twitter and ask me any questions that you might have if you run into any issues. Cool, so the prerequisites for this are essentially gonna be kind of like already your basic development environment if you're a front end or a full stack developer so you're gonna need those JS installed which will automatically have NPM installed. Other than that, you just need to have an AWS account and you need to have the AWS account activated. Now for me, for the last three years, I've always been using my AWS account as an employee. So today I'm using my own personal AWS account. So hopefully, like, if I run into any issues, or you'll run into the same issue, but I've already tested this stuff out, you shouldn't run into any issues. But I guess what I'm saying is like me and you, we're all like working on the same, the same stuff with the same credentials and all that stuff. You know what I'm saying? So I'm not operating like from the perspective of an AWS employee anymore, which is which is fun. Okay, cool. So yeah, the one thing I would recommend is once you've kind of like, you know, once you're ready to start building, just go ahead and sign into your AWS account. And you should see a dashboard like this. And we're gonna be kind of like, you know, going back and forth a little bit there. So that's, that's that before we get started. Does anyone have any questions? And if you do, feel free to ask them and I'm going to see if I can open up, I have the discord open. Looks like I have the Q&A open and I have the chat open. So I don't know where the best place to ask questions are, but maybe discord would be a decent place for me to keep an eye on. Someone asked, will a recording of this session be available, and it looks like it will be, which is good. I'm not sure, but we'll see.
Okay, cool, so let's talk about what we're gonna be building. We're gonna be building a full stack app with Next.js, Tailwind, and AWS. And the AWS technologies that we're gonna be working with are, AWS Amplify, and under the hood, it's gonna be Amazon DynamoDB. It's gonna be AWS AppSync, which is a GraphQL API service. We're gonna be working with Amazon S3 for image storage, and we're gonna be working with Amazon Cognito, which is a manage authentication service, something like Auth0, if you've ever used that. And we're gonna be using the Next.js features that a lot of people really enjoy and that they're excited about.
2. Introduction to Next.js and SSR
We're gonna be using Next.js features that people enjoy and we'll combine static site generation, SSR, and dynamic data fetching. Amplify supports SSR on the client, but not on the hosting side. Options for SSR hosting include Bristol or waiting for Amplify SSR support. We'll use the serverless framework for easy deployment. Different hosting options have different pricing. Serverless framework and Amplify Hosting SSR will be in the free tier. We'll be building a multi-user blogging app with features like Markdown support, image upload, editing and deleting posts, and fine-grain access control. Similar core functionality can be used to build different types of applications.
And we're gonna be using the Next.js features that a lot of people really enjoy and that they're excited about. So for a while, if you were a React developer or a front-end developer, we had a big explosion of this single-page application type of app and everything that was happening in that whole, I would say, like, renaissance of maybe front-end web development, where we had all of these great single-page application frameworks coming out, like Angular, we had Ember, React, Vue, all these other ones. The next thing that we saw happen in this whole ecosystem is the JAMstack, which was kind of like static site generation mixed with a little bit of dynamic. And then what you're kind of seeing now is kind of like this progression past JAMstack into kind of like, Guillermo Rauch, the guy that founded Next.js in Brazil, kind of had a really interesting talk about it at Next.conf, kind of like talking about what's coming past JAMstack. And it's kind of this hybrid approach, and that's kind of what we're going to be taking advantage of. Where we're doing a combination of static site generation, SSR. We're also gonna be doing a dynamic data fetching on the client, and kind of when you build something, you can take advantage of the things that work the best for the use case that you're working with. So instead of saying, we're gonna be 100% bought into a single-page app, or we're gonna be bought in 100% to a static-site-generated website, we're gonna instead combine these things and kind of see where they work best and kind of take advantage of those things.
A couple of people are asking about SSR. So SSR is a topic within, I guess whatever technology stack that you're working with, and there's always the question of what is supported, what isn't supported, and things like that. When you're working with Amplify and you want to work with SSR, there are two main areas where you kind of have to take into consideration. One is SSR hosting. So when you're actually deploying the Next.js site, you want to have the API routes and SSR and all that stuff working. So that's like the hosting side. And then the other is the client library side where you actually want to be able to make the client side or even the server side, API calls to the different AWS services, like AppSync, like API gateway, and all those things. And you wanna make sure that those are working on the server. So as of like today, like as we speak, Amplify supports SSR on the client completely, meaning that you can, when I say the client, I mean the client libraries, which kind of again are often used on the server. So the client libraries, I guess you would say, are supported SSR, meaning that you can call an API endpoint from an API route. You can call an API endpoint or authentication from an SSR route and from the client, and it's completely like built in where we have, we deal with, you know, the sessions and the cookies and all of that stuff for you. Then the other thing is the SSR support for the hosting. As of like right now, Amplify hosting does not support SSR. There is actually a preview version of this that you can use if you kind of sign up for it. So it's coming out in the next couple of weeks, maybe the next month or something like that. In the meantime, if you want to deploy a NextJS app and take advantage of SSR like we're doing, you have a couple of options. You can use, of course, something like Bristol, which is kind of like built in with SSR support, which is great. A lot of people though that are like building on AWS like to stay on AWS. So there's also other AWS options. One option of course is to wait a couple of weeks until SSR support on Amplify hosting is there. The other option that we're gonna be using today is going to be the serverless framework, which allows us to kind of really deploy this super easily to AWS using like literally like one or two lines of configuration. And it's pretty simple to do that. And then the last option is if you want to actually deploy this into a container and Amplify also supports that. So if you wanted to deploy NextJS to a container, you run Amplify add hosting, and you kind of like set that up. You get to create your Docker file and all that stuff. Let me see here. I think I have a video or blog post or something going over this. Yeah, I have a blog post. So if you're looking to kind of deploy NextJS to a container, you can use this. But what we're gonna be using is a container is kind of just, it's great, but it's just kind of like a little bit overkill, I think for what we're doing. So instead we're just gonna use a serverless framework, super simple and really good VX.
How does the different hosting options affect pricing is a question that we got. They're all gonna be priced a little bit differently. Fargate, which is the container is not serverless, so you're gonna have to pay... That would have some type of pricing involved. I think there's a free tier there as well, but at the end of the day, it's not serverless. I tend to go for the serverless options these days because you're not gonna pay for those unless they're actually getting a lot of usage. So the serverless framework is a serverless option and it's gonna be a completely free tier. That's what we're using here. Amplify Hosting SSR, that's gonna be in a free tier as well, in a couple of weeks.
Okay, cool, so I just answered that question. Now, I'm gonna probably hold off on a couple of questions but keep them coming and I'll probably address those. What we're gonna do is we're gonna work until, for another 35 minutes. Right now it's 11.25 AM and Central U.S. time. I'm gonna work until 12 o'clock. So for the next 35 minutes, we're gonna work, then we're gonna take a 15 minute break. And during that 15 minute break, I'll try to address as many questions as possible.
So what we're gonna be building is a multi-user blogging app that allows you to create blog posts with Markdown. It allows you to upload images and put them as part of your blog posts. It allows you to edit and delete your existing blog posts. It allows you to view all of the blogs that have been created, all the posts that have been created by any user. Like if you went to Medium or something, you would have like the main feed of all the different posts. We're gonna be building something like that you could think of. And then we're gonna also have a separate view where when you're signed in, you can only see your own images, I'm sorry, your own posts, and you can also edit and delete your own posts. We're gonna be implementing all of the different things that make this happen, including security, authentication, authorization, fine-grain access control, all the stuff that you would typically need for any type of application. And I think like, if you think about, if you go back really far and think about when Rails became like really a big thing, and it was kind of like a super awesome revelation and kind of mind shift and full stack development. One of the reasons was like, you can actually use Rails and generate a project and do all of the stuff that you would need to do for almost any project. So when you think of like most apps, let's think about Twitter or Facebook or Instagram or many of these types of applications. When you're building out an app like this, they all are, of course, different apps, but they have a similar core set of functionality that you can learn and then use and take and build all types of stuff.
3. Building Core Functionality and Scalability
Building apps with core functionality like authentication, data storage, API access, and file storage can cover 80-90% of most app ideas. The remaining 10-20% is what makes your app unique. Understanding how to build these core features and ensuring scalability is crucial. Using a cloud provider like AWS allows your app to handle scaling effortlessly. Amplify simplifies cloud computing and is a popular choice. The workshop focuses on a modern stack with Nex.js and Tailwind.
So when you think of like most apps, let's think about Twitter or Facebook or Instagram or many of these types of applications. When you're building out an app like this, they all are, of course, different apps, but they have a similar core set of functionality that you can learn and then use and take and build all types of stuff. So that functionality is this. It's typically gonna be some type of authentication and authorization layer. You have some type of data storage. You have some type of API access for that data. And then you also have some type of file storage. So you can kind of bucket those things into off data and storage. Now, if you can understand how to take advantage and build using these building blocks, you can actually build out probably 80 or 90% of the apps that most people have in their head. And then the other 10 to 20% are gonna be like the things that might differentiate your app from everyone else. So I think like understanding how to build with these core building blocks, set you up for success and allows you to build all the stuff that you might wanna build. So that's kind of like what I like to include in most of my workshops is, how are we gonna understand how to build all of these fundamental core features and how can we actually build them in a way that is scalable? And when you're building something, a lot of times when you're getting started and just like learning how to do stuff, the last thing you wanna worry about is scale because like in reality, like we're just maybe building out a demo app and we're just gonna be playing around with it. But what if you could spend the same amount of time and effort building out something that could or could not scale? Like it doesn't matter, like if it does scale, but if it did, it would continue to work and not blow up. And I think that's the value that you get out of using something like a cloud provider like AWS, because what we're building is like really, it's really gonna be something we can get up and running in a few hours. But if you decided to kind of take this and run with it and you ended up with a million users, it would continue to work. And I think that since we're using the same underlying technologies that Netflix and Amazon and Airbnb and all of these big companies are using, it's really interesting that we can kind of build these attractions on top of it to make it a little bit easier for people to get started with. And I think Amplify is kind of like the ultimate, that's the ultimate tool that we're kind of working towards. At least I was on that team and that team is still working towards to kind of lower that barrier to entry to cloud computing. So that kind of gives you a general philosophy around this workshop and the ideas behind it and stuff. And I also chose like a modern stack. So Nex.js, Tailwind, continuing to gain a lot of momentum. I think you know when you build with tools that are popular and continue to gain momentum, you get opportunities in your career and stuff. So that's kind of why I chose those.
4. Creating Next.js App and Installing Dependencies
This part gives an overview of the user interface and the features of the app. It also mentions the use of CloudFront for distribution and the support for incremental static site generation. The next step is to create a new Next.js app and install the necessary dependencies, including AWS Amplify and the Amplify UI component library. Markdown support is also discussed for formatting blog posts.
So this kind of gives you a general idea around the user interface. This is actually outdated. There's actually a place to upload a cover image now. So you create a post, you can write your post and mark down, you can preview the posts. You can like view an individual posts. We're gonna have a profile view where you can kind of like see your user profile. You can sign out and kind of see, you know, your username and email address. So just like many apps. And then we have this view here, which is kind of like the my posts view where we're only viewing our own posts and we're able to edit, delete, or view an individual post.
All right, so with that being said, are there any major questions before we keep going? Someone asks, are we also going to use CloudFront for distribution and the workshop? Yes, the serverless framework that, a plugin that we're using uses CloudFront along with Lambda Edge to kind of deploy all of the different features that you would expect out of Next.js. And if you, we can kind of take a quick look at this component. And if we go to features we can kinda get a good idea around the features. So I'll go ahead and paste this in. Um, we have SSR rendered at the edge, we have API routes dynamic routes, pretty much everything. The only thing that is missing, if you go to this link here, you'll see a few of the new features for Next.js 10.0, which are in progress aiming for a 1.19 release. So not everything, but almost everything is supported. So get static props, get service up props, and get static paths. We're going to be using all of that stuff. Does, will, amplify support incremental static site generation for Next.js? Yeah, that's kind of part of the SSR support that we're working with. That support is actually already available for preview. So if you go into the discord, you could probably jump in on that and try it out. It'll probably be available for actual production released in the next couple of weeks.
5. Installing Dependencies and Configuring Amplify
We install the necessary dependencies for the app, including React markdown and uuid. We then proceed to install Tailwind and the Typography plugin. Next, we initialize the Tailwind configuration and update the Tailwind-config file. We empty out the globals.css file and add the necessary Tailwind classes. With Next.js and Tailwind configured, we move on to the Amplify CLI, which allows us to create, update, delete, and modify AWS services. The CLI works in tandem with the Amplify client libraries, which are specifically designed to interact with AWS backends.
I can highlight it, press a button, and it automatically like formats it for me. And then React markdown allows us to just render that markdown and display it in a nice way. And then uuid is a library for creating unique identifiers. In our case, we're going to be creating an ID to kind of like give a unique ID for a post. So let's go ahead and install that, and you can install this using either NPM or Yarn. I'm doing NPM install here, but I'm going to instead on my computer do like Yarn add and just paste all these dependencies.
Awesome. All right, so we've installed our initial dependencies. Now we're going to install all the Tailwind dependencies. So we're going to be installing a Tailwind as well as a couple of other plugins. One of them is the Typography plugin, and we're going to be kind of using this plugin to allow us to style our Markdown Editor as like a parent class. So we don't have to kind of like write a bunch of CSS for that. Meaning that like if there's an H1 or an H2 or some code, we want it to kind of be looking nice with some CSS. And this plugin allows you to do that pretty easily. So I'm going to go ahead and install those. I'm going to be using Yarn. Of course, you can use NPM as well. Yarn is like a lot faster. At least it has been for me.
All right, so still in that same directory. The next thing we want to do is initialize a Tailwind configuration, and this basically is going to create a file called Tailwind.config.js, and maybe one other file. This allows us to kind of get up and running with Tailwind pretty easily, so npx Tailwind init dash p. So, looks like we're getting a post-CSS config and a Tailwind config. The only file we're going to be playing around with at all is going to be this Tailwind config. We haven't opened our text editor yet, so let's go ahead and do that, because we're going to need to go ahead and let us go ahead and configure something. But there's a lot of bugs here. That is cyberpunk. Indeed. It's my favorite theme. So, our project is here, and then when we ran that command, we got this file here, and we got this file here. Tailwind-config and PostCSS-config. We're going to be updating the Tailwind-config. And here we want to add the plugin that we're going to be working with, which is this typography plugin. And all you have to do is just plug that into that array. The array was already there, we just kind of added one item. And we're good to go. So we can go ahead and close that file. We won't be using that again.
The next thing we can do is completely empty out our styles slash globals.css. So we're going to delete all that. And we're going to add these three Tailwind classes. Our Tailwind imports, whatever these are. Basically, it's kind of like going to do a reset, and then it's going to give us some base, all the different classes and stuff that have to do with Tailwind. And then when we want to use Tailwind, we can just basically, I think, import that into our project. Alright, and since Next.js I think already imports this, if we go to pages slash index app, there we go. So it's already imported into AppJS, so we don't really have to do that. So we're done configuring Next.js. We have our base projects going, so now we need to move to the Amplify stuff.
The way that we're going to be interacting with AWS is via the Amplify CLI. Amplify is kind of made up now of a few pieces, a few parts, and they can all be used individually or they can be used together. So the three main parts I think are going to be the Amplify command line interface, or the CLI. The CLI is what we're going to be using to create, update, delete, and modify AWS services. So if we wanted to create a database, we can run a command from the CLI and it would create that database in our account. If we wanted to create an authentication service, we could run a command line, a command and it's going to do that, it's going to create that authentication service. So that's kind of like the flow where we're kind of working from our CLI and we'll run like an Amplify, like do something, and then it'll do that, and it'll also create like a local artifact that we can use in our local project. So it's great for full stack development. And then the CLI is going to create artifacts, like, let's say we create an API endpoint, it's going to actually create a file locally with that API endpoint, so we can then just import that using the Amplify client library. So that's the other part. So the CLI, you create services, the client libraries is what you use to interact with the services. So if you've ever worked with HTTP requests, you've probably worked with something like Fetch or Axios. If you've ever worked with GraphQL, you probably worked with something like the Apollo Client. Those are examples of like HTTP client libraries that send requests to different endpoints. The Amplify client libraries, you could kind of bucket in that same category of tools in the sense that they're created specifically to interact with AWS backends. We're going to be interacting with things like a GraphQL endpoint. We're going to be interacting with things like an S3 bucket. We're going to be interacting with things like an Amazon Cognito user pool, which is kind of like an authentication service. And those credentials are going to be stored locally. And we just kind of use the client libraries to interact with those. And the CLI and the client libraries work pretty well together, because when the CLI creates or updates something, then it creates the local in point for those things that were created, and then you can consume those and use the client libraries. So that's kind of an overview of the CLI, but we're going to learn a lot more when we use them.
6. Installing and Configuring Amplify CLI
To use the Amplify CLI, install it first. For Mac users who are not on an M1, it is recommended to use curl for a faster installation. If you are on an M1, use npm install -g. The Amplify CLI is much faster than the npm version.
But to use the Amplify CLI, once you have it installed, you'll be able to just run Amplify and you'll see a bunch of different commands that you can use. But it needs to be installed first. And if you're using a Mac and you're not on an M1, I would recommend using curl, because this is going to install a much faster version of the CLI. And I don't know for sure if this M1 has been fixed yet or not, like where you can use this command. But I know the last time I checked, this command only works on Mac and Linux. That's not a Mac. That's not the M1. And then it should also work on Windows. If you are on an M1, then you want to use the npm install dash G. So this is kind of the only time you would use the npm installation is if you're like on an M1. If not, I would recommend the curl. So you would just like run this command. So if I copy this and just go to my command line and paste it, it's just going to install the Amplify CLI. And this CLI is like four times faster, I think, than the npm version. So, you know, executing that command, you should have the Amplify binary and you should be ready to go to the next step.
7. Creating AWS Services and Configuring CLI
We create AWS services from our CLI by signing in as a user and creating functions on behalf of that user. To associate a user locally, we use a key value pair with an access key ID and a secret access key. We create the key-value pair by running Amplify Configure, which opens the AWS management console. After specifying the region and giving the user a name, we proceed to the IAM console to create the user and obtain the access key ID and secret access key. These credentials are stored locally in the .aws folder. The Amplify CLI walkthrough requires copying and pasting the access key ID and secret access key.
So I mentioned that we're going to be creating AWS services, you know, from our CLI. Now, how does that actually work in, you know, in real life? Well, if I was signed out of my AWS account and then I signed in as like, you know, this user, and I wanted to go and, like, create a Lambda function or something, I am essentially creating this function on behalf of the signed in user, meaning that I've authenticated myself and I am creating this. And if I wasn't authenticated, then I wouldn't be able to do that. And if someone else was authenticated, it would be creating on behalf of that person. So we are signed into this dashboard and we are creating things based on the fact that we are that person.
How can we associate a user that is in a signed in console but do that locally? The way that we do that is we use a combination of a key value pair where we have an access key ID and a secret access key that kind of acts on behalf of a user. And, to make that work, we need to do two things. We first need to actually go into our AWS management console here and create that user. And then we then need to go back to our CLI and set that user somewhere locally so when we run Amplify Add, it can then go look up that user.
Now, the way that that essentially is going to be done, the CLI is going to do that for us. But let me kind of walk through and just show you where this stuff is added in your local machine. And you don't have to follow along with this. I'm just kind of giving you an idea around where this stuff is. So if I go to, like, the root of my computer and I kind of examine, you know, well, let me actually open this in a new window. If I examine all of the different files and folders on my computer, I'll see that I have this.aws folder. This was created when I ran Amplify Configure for the very first time. If you've never run Amplify Configure, this isn't going to be there unless you've already used maybe the AWS CLI. If you have run Amplify Configure, the CLI is going to create this folder. And if we go in there, we're going to see two new files. We're going to see a config and a credentials file. This is going to kind of hold the credentials for the user. So these are kind of, like, very secure credentials. You never want to expose these and let anyone see them. And the config is going to be kind of the name of that user. So the config might have, like, a value of Natter's CLI user. And then in credentials, there would be a reference to Natter's CLI user with the key-value pair. So we want to, like, get – we want to create that key-value pair for the first time or maybe you want to add additional ones. So the way that we do that is we run Amplify Configure. So let's go ahead and do that. Amplify Configure is going to first open up your AWS management console. And if you're not signed in, it's going to kind of want you to go ahead and sign in. And then once you're signed in, you can go back to the terminal and continue. So it's kind of like saying sign in to your AWS admin account and then come back.
Next we want to specify the region where we want to create this user. I would just choose a region that is closest to you. So, for instance, Asia Pacific. We have Canada, US East, European Union. So I don't know the locations for all of these, but US East is kind of like the US East Coast, US West is the US West Coast. EU central is Frankfurt, Germany. So it doesn't matter a whole lot. If you pick somewhere that's a little bit further than another place, it's not a big deal, especially for this workshop, it's not a huge deal. I would just choose one that you feel is closest. For me that's going to be like US East one because I'm on the East Coast in Mississippi.
Next I want to go ahead and give the user a name. I would just kind of make this. Just give me whatever you want, give us a name that resonates with what we're doing here. So I think I set a name of Amplify CLI user. So I'll just go ahead and do that. So after we set that user, it's going to open up in the IAM console, and this is going to be the identity and access management. This is going to walk us through creating that user, which is then going to give us that key value pair. So when this opens up, it's going to automatically set up everything for us. We don't really need to change anything. We just need to go ahead and just click next, next, next, and don't change anything because it's automatically going to kind of get it ready for us. So don't worry about, you know, what's going on here. But let's just kind of like give you just a brief overview of what is going on here. There's two ways to create a user. One is programmatic access, which is going to be that key value pair that I mentioned earlier. The other is console access. So let's say you wanted to like create a user for your friend and you want them to be able to log in to your account for whatever reason, but you didn't want to give them your actual username and password. You could actually create their own username and password and that would be console access, but we're not doing that. We're only doing programmatic access. Next, we want to go ahead and attach permissions. So for Amplify, it uses dozens of different services. So you could either walk through and create a permission list for whatever services you wanted that user to be able to access. But most of the time, we just say give them administrator access because there's a lot of different services that need to be accessed and this way it makes sure that there are no issues. Next, I'm going to go ahead and click Review and then I'm going to see that we have the username, programmatic access, administrator access, and then I can click Create User. Now this is going to go ahead and give me the access key id and that secret access key. This is what we need to now copy and paste in our Amplify CLI walkthrough. So to do that, I can go ahead and copy the access key id first, so it's copied to my clipboard. I'm going to go now to my CLI.
8. Configuring Amplify CLI and Initializing Project
To configure the AWS CLI, run amplify-configure and follow the prompts. Once configured, you don't need to do it again. After configuring, run Amplify Init to create an Amplify project. Choose the project name, environment, text editor, app, framework, source directory, and distribution directory. For the remaining questions, choose the defaults. Select the AWS profile you created and initialize the project. We'll take a 15-minute break and answer questions. An alternative to CloudFront is the container option.
9. Using Amplify with AWS and Recommendations
If you're working with the team, do you have to create IAMs with full access to every dev? Why Tailwind versus Styled Components? Can Amplify be used in conjunction with NextAuth? The free tier will 100% be enough to cover all of the workshop. I recommend using Amplify for commercial projects or traditional stack, CloudFront, ECS, EC2, and so on. If I want to build an SPA dashboard, would you recommend using something other than Next.js? Using the differences between GraphQL API versus Datastore.
And I don't even know if she would be able to use for cell because I believe they also use CloudFront. If you're working with the team, do you have to create IAMs with full access to every dev? You really only have to create an IAM role scope to whatever services that you want to use. It's kind of up to you. If you know that this developer is only going to be making updates to, say, S3 or whatever, you can create a policy that's scoped to just those services and you should be good to go.
Why Tailwind versus Styled Components? That's a good question. I think Styled Components is extremely, extremely good and powerful and popular. I think it's a very good option. I think if you look at the ecosystem right now, the top three, maybe even the top four, you could say would be Next.js— I'm sorry, as far as CSS stuff— would be Tailwind, Chakra UI, Styled Components, and maybe even something like Emotion. I think any of those are great. I'm starting to just get used to Tailwind because it's so different than anything I've ever used and it seems to be gaining momentum. I got used to using Tailwind and it became very, very efficient with it, so I kind of have enjoyed it. But I'm not like one of those people that's like 100% pro using one thing over the other all the time, I'm the type of person that's going to always be trying new things and seeing what works for me. I think right now the two that I use on all my projects are either Tailwind or Emotion. But I don't think you can go wrong with anything that's gotten to the point of popularity of style components.
Can Amplify be used in conjunction with NextAuth? I'm not sure. I would say that you can probably use Amplify... What is the... I'm sorry... Federated identities with NextAuth, meaning that you could basically use it as an authentication layer with Cognito-federated identities, which is not going to work as good as some of the stuff we're doing. But if you're using Amplify, a lot of people like NextAuth because they can do stuff like Google and Facebook authentication. You can actually do that really easily with Amplify as well, and it integrates well into the entire system. I think one of the benefits of using something like Amplify or Firebase is that the authentication and the API and the storage layers all are integrated together. That means that if I make an API call and I'm signed in for my client application, then I don't have to worry about packaging up the headers and sending the proper access or ID token, parsing out on the server, checking to make sure that it's actually verified, and then passing that on to the API and then doing all that work. All that's done for you when you're using Amplify. You just make the API call, it automatically sends the access token in the header, AppSync verifies the token, passes it to the resolver in the context, and you automatically have that user's identity if they're signed in, if they're not, it's not there. And it's just super, super simple and integrated and you don't have to write any of that business logic.
The free tier will 100% be enough to cover all of the workshop, and then some. Yes. If you're worried about anything, like, costing you, it's not, even if you left this app running for the next year, it's not going to cost you anything, at the end of the workshop, if you go down here to removing services, this is going to show you how to remove individual services or if you actually would like to make, might make more sense for this workshop and you're done with it, you can just delete everything by running amplify delete. This will delete all the services in your account that you've created for this workshop.
Someone said, I had quite a few issues with AWS exports. It was quite hard to get it generated when sharing a project with a colleague, is there some issues with that or is it related to some bad config? The Amplify console should walk you through how to do most of that. Like if you go to, I don't think I have any projects deployed, but we'll actually have this one, actually it looks like. If you go to edit backend you should be able to kind of like copy this and share between different projects. So, I'm not sure exactly the issues you ran into, but if you had some issues that that looked like they might be related to like some bug or something, you're probably open to get help issue.
I recommend using Amplify for commercial projects or traditional stack, CloudFront, ECS, EC2, and so on. I think it just depends on the project. We have a ton of really, like at this point there are hundreds of thousands of production workloads in the sense that they're 30-day actives and they've been making API calls consistently for 30 days. But we also have like really pretty large names at this point using this stuff like BMW, Ticketmaster, Aldo, and a bunch of other pretty large companies. I think the answer to like is this ready for commercial projects? I'm never going to say that one thing should be used for everything. I think it just depends. If Amplify supports the services that you need to build out something, I think it's the fastest and easiest way to kind of get up and running with AWS. But if there's something that Amplify doesn't support, it's harder to extend Amplify than it is to extend something like CDK. So, typically the way I look at it, coming from someone that doesn't even work in AWS anymore, if I was to build something on AWS, my kind of like, the order in which I would choose tooling today would be Amplify if I can use it. If Amplify doesn't fit the use case, in the sense that the services that I need are not supported. My next choice is going to be CDK for the infrastructure, but I'm still going to be using Amplify client libraries, just because they're the easiest ways to kind of interact with the services. So, infrastructure, Amplify, CLI and then CDK. I'm not a huge fan of CloudFormation just because it's so complicated and so verbose, but CDK generates CloudFormation and I'm loving CDK as my second option.
AWS, error monitoring, we don't have anything that's as good as Century or anything like that, unfortunately, no. If I want to build an SPA dashboard, would you recommend using something other than Next.js? Single page app dashboard? That seems to be a pretty solid use case, just for a typical like Create React app. The one thing that you get out of Next.js that I really like, that you don't really get out of Create React app is the routing. The file-based routing is just so simple, and it's just one less thing to deal with. At the same time, it's probably not going to be as performant for a single page app as something like just a React router or something. Because once the app loads on a single page app, you never have to kind of like really fetch that bundle again, it's there. So for stuff like brochure sites and marketing sites and blogs and stuff that needs to be SEOable, Next.js seems to be like the go-to for that. But for actual apps, single page apps and stuff, I would still probably use something like React Router and just straight up React for that, unless you need something that Next.js offers you at this point.
Someone asked, using the differences between GraphQL API versus Datastore. Datastore is built for two main reasons. One is that offers slightly easier API to work with for people that aren't used to working with GraphQL. So you don't need a GraphQL at all really to use Datastore. So that's one benefit I would say over what we're doing with GraphQL API. And the other is, the real reason that it's created is for people that need offline like functionality out of the box and they don't want to build it themselves. So like for most web apps, you don't really need offline functionality. I think it's a lot more often something you see in a mobile app, a native iOS, native Android, Flutter, React Native. And even then it's not even always necessary. So for me, and this isn't really the view of the Amplify team, they like to tend to say, use Datastore unless you run into some limitation. I'm kind of the opposite. I'm like, use the Amplify API category unless you run into a limitation that you need offline or something and then use Datastore. The Amplify API category is used in massive, massive production apps at scale. And it's super solid.
10. Adding API and Authorization
To start off, we're going to start with a public API and then later on, we're going to integrate authentication and authorization.
There's not a lot of bugs or anything that I've ever seen in it. And it just works really well with AppSync. So I would probably only use Datastore if you need the offline stuff. And maybe down the road, six months or a year from now, Datastore will get even better. But I would only use it for the offline stuff, honestly.
Which one would you suggest for the best next step, S3 and CloudFront, Amplify or ECS with Fargate? I think once Amplify supports SSR, then S3 and CloudFront or Amplify are going to be kind of the exact same thing. And I would probably choose Amplify at that point because you're going to have a much better user experience and developer experience on top of S3 and CloudFront. Today though, I would probably choose S3 and CloudFront just because Amplify doesn't support SSR on the hosting side like I mentioned for a few more weeks. But what we're doing today in the workshop is going to be essentially S3 and CloudFront. Since we're using the Next.js Serverless component, that actually uses S3, CloudFront and Lambda.Edge under the hood. The only reason I would use ECS with Fargate would be if there was some limitation that I had that something like Lambda.Edge isn't a good fit for. I think one of those might be if you need a persistent RDS or SQL database connection. Lambda.Edge is like ephemeral. It's going to be created and spun up and it's kind of like a temporary thing. And it's not like a single point of … it's not like a single location where this thing is running, whereas ECFs with Fargate is kind of like that single place. So I would kind of like only use ECFs with Fargate if I really had a reason to.
Someone was asking about elastic Beanstalk. I don't really have a ton of experience with elastic Beanstalk, to be quite honest. But I know a lot of people kind of bucket, or they often will bucket amplify in elastic Beanstalk in the same maybe category, but I think we've like really had a ton more growth than Beanstalk lately. So I don't know if that is because we offer a better service or a different service or what, but I don't know enough about elastic Beanstalk to answer that.
Can this workshop setup work on AWS GovCloud? I don't believe so. Lambda and Lambda edge. So Lambda is gonna be basically a function that's living in a single region and a single like location. So if you just deploy a regular Lambda function, it's gonna be like sitting in U.S. East One and you're only gonna have that one way to invoke it. Lambda edge is basically gonna be distributed across over 200 different points of presence and any time that someone tries to invoke that function, it's gonna be given to them at the closest edge location and that way it's just a lot faster. So that's kind of the main difference. Lambda edge is kind of part of like the whole cloud front edge computing type of service and stuff and it's starting to become extremely, extremely well utilized and popular. I think a lot of people are using it under the hood like Brussell and stuff like that. And also caching I believe as well. So lambda edge, I believe is cached. I need to learn more about actually how a lot of the stuff works under the hood. I don't know the exact answer to that, as far as how the caching works.
Someone asked is there an estimated date for SSR and Amplify. If I go to, let's see here, someone on our team kind of opened up like a GitHub issue or something where you can like sign up. So you can basically sign up and try it out today if you just send your name and your account ID to this email address and you can go ahead and try it out now but I think it's released to the public in maybe a couple of weeks, I don't know the exact date.
Alright, cool. So let's go ahead and continue since it's 17 minutes after the hour now. And we'll take another break at probably 10 minutes after the hour of next hour and we'll just take like a 10 minute break. We have two breaks. So after Amplify Annette has completed, you're going to see Oh yeah, we don't have an src folder. Okay, so we're going to see two new files and folders. We're going to see this Amplify folder. We're going to see this AWS exports dot j s file. Amplify folder is going to hold all of the amplify related stuff that we're going to need. So we have this back end folder and this is really, I would say the only The only folder we're going to be working in and the Amplify folders the back end folder at some point. But there's nothing in there right now for us. Just kind of there and then the other files the AWS exports dot j s file. And this right now only has a property setting the region. But like once we start adding services like amplify add storage or amplify api or off it's going to start populating this this this object with additional key value pairs. So I might say something like graphical endpoint and then it'll be like the endpoint and it'll have like maybe the API key and it'll have like the API key. So if we go ahead and our text editor again we should kind of like see you know same thing. So we have that so now that we've had our amplify configuration done we create our project we can now start adding services. Any time that we want to see the services that we've enabled we can run amplify status and this will just give us like a list of all the different services that have been created right now we don't have any of course. And we can also actually open up the amplify console which I had done earlier. We can open our project up in the actual user interface by running amplify console and choosing amplify console. And we're not really gonna be working here right now too much but you can do stuff like see if you've had authentication added. Let's say you add a function you'll be able to see the logging for that function and stuff like that. So that's just to kind of like for reference but what we want to do now is we're going to start adding some functionality to our app. So the first thing we're going to do is we're gonna add an API then we're gonna add authentication and then we're gonna build out user profile. So to get started we're going to add an API. So to do that we can run an Amplify Add API, we want to choose GraphQL, and we're gonna call this next blog or whatever you'd like to call it. For the default authorization type we're gonna start with API key. Now there's... again when we talk about these types of applications like Instagram or Twitter or Facebook, when you view Facebook as a user from an incognito window or not signed in, you can actually still see certain things like posts and stuff like that. If you go to Twitter, you can read tweets publicly, but if you sign in you get like a different set of permissions. So most apps have this idea or this concept of multiple authorization types. And that's what we want to build today. We want to be able to show both public access, private access, we want to be able to kind of manage the interface based on that user's identity and stuff like that. But to start off, we're just going to start with a public API and then later on, we're going to integrate authentication and authorization.
11. Setting Up a Public API with GraphQL
12. Testing the API and Performance Comparison
The workshop generates GraphQL resolvers that map operations to the database. The code is generated locally, allowing updates and custom business logic. After deploying the API, we can test it in the AWS console. The GraphQL API opens in a graphical editor interface. We can populate queries and mutations manually or use predefined ones. GraphQL subscriptions are fully supported in AppSync and have been battle-tested at scale. Mutations and queries can be executed to create and retrieve data. AppSync offers better performance compared to REST API with Lambda, as it directly maps GraphQL operations to the database.
And then the last thing it does, it generates the actual GraphQL resolvers that map the operations into the database. You don't really have to kind of like think about all this stuff or know about it in practice getting started, but just so you kind of know what's going on so it doesn't seem like complete magic. All of this code though is generated locally so you can update it as well. So if you needed to kind of either update a resolver or create your own resolve or your own custom business logic, you can do that as well.
Alright, so the next thing we'll do is let's go ahead and continue on to the workshop. So what we wanna do is go ahead and test out the API. So once this is been done being deployed, we can go into the AWS console, we can open up sync and we can start using it. So what we wanna do is we wanna send a mutation and also a query. So let's go back to our command line I guess. And once everything is done being deployed, you should see something like this where you're given a success message as well as a GraphQL endpoint and a GraphQL API key. Now I know everyone's computer isn't the same speed so I'm gonna wait maybe one minute or 30 seconds and I'm gonna actually go make a cup of coffee and hopefully when I come back everyone's caught up. So I'll be back in one second.
Okay so I'm gonna move over to my shadow and I'm gonna look through here so I don't lose myself or I don't cross this line. I'm gonna look at my phone for a minute and then I'm gonna look at myself and I'm gonna look at the comments and then I have to go back to editing and do another little thing. So this reminds me, when I was editing I was registering my phone. So basically I'm going to do this because actually my phone is registered inside of Instagram and I've got a little folder that's my desktop vlog. And I'm not gonna drag it in and I don't wanna get any ads sent to it. So I'm gonna move over to my editing and I'm gonna go back over again and I'm gonna look at my button, and I'm gonna go back to my share button for Edit Maryland. And I see here that I did a Shadow edit and that's my button. All right. I updated my picture as well while I was waiting. All right, cool.
What we're gonna do is go ahead and open up the GraphQL API in the AWS console. So when we ran that, when we ran Amplify push, it created this AppSync API and we can go ahead and play around with it and test it out. So when we're on Amplify console API it will prompt us for GraphQL. We choose that. And then the CLI should go ahead and open up that Amplify AppSync API in the AWS dashboard and it should look just like this. Now I'm gonna go ahead and close this on the side and here we are and we have our own graphical editor interface. If you've ever worked with like Postman and the rest world, you've probably been able to kind of like have a test environment for sending updates to an endpoint. That's kind of how a GraphiQL is. GraphiQL editor is kind of like a community project that a lot of companies kind of just use and a lot of projects use everyone kind of is kind of built less standardized way of interacting with their GraphQL APIs and it's done via this GraphiQL editor. Now there's two ways to populate the actual queries and mutations itself. You can actually go down here and click through and it'll automatically populate the stuff, you can choose mutation, subscription, whatever, or you can kind of write it out yourself. So you could say query list posts, like write all that yourself. If you look at the workshop though, I have a couple of mutation in a query already defined here. So I'm going to go ahead and copy the query and the mutation and just paste it here and we're going to try it out.
Someone asked are GraphQL subscriptions fully supported? I thought there were issues with those around serverless. Yeah, that is actually true. That is a good point. If you build subscriptions in a serverless environment, a traditional service environment with API gateway, then I wouldn't say that there's issues. It just takes a lot of work to kind of get them going. But with AppSync since this is a GraphQL service that is built using the GraphQL spec, we actually have full blown support for subscriptions. And I would say very early on, we had Ticketmaster as one of our early customers that had very, very high scaling up and down because they were putting tickets on sale and they would have like tens of thousands or even hundreds of thousands of people that would be in and out of a event immediately. And we've been able to kind of really battle test the subscription implementation for AppSync where we've scaled a single end point to tens of millions of connected devices for a single endpoint. I think it was in the range of like 50 or 60 million as the highest that we've tested it out at. So subscriptions are just hardcore, battle tested at this point with AppSync. I would say that if you're building your own implementation, it is something that is often like hard to get right.
Okay, so create posts. It's gonna go ahead and create a post. So if we go ahead and execute that, we should see that the posts has been created. And if we now do list posts, we should see that the data comes back. We go ahead and create another one. And we list posts. Then we see that we have two posts. So our mutation is working. Our query is working, our API is just working. So that's quite good. So now that we have played around with it, we know that there's actual data in our database. We can now bring it down to our client app and start rendering some data and making our front end. Another question that someone had is, is there a performance difference between GraphQL and using REST with Amplify? Yeah, I would say there is, actually. With REST, the REST implementation is going to be using a combination of API Gateway and Lambda. And when you're working with Lambda, you have to take into consideration cold starts. You can bump the memory size up to, you know, maybe 10X is what most people do, from the 128 up to like one gigabyte for Lambda, which speeds up your Lambda execution a lot. But there's still probably going to be like, you know, 50 to 100 millisecond cold start, which is not like a lot, right, but it's there. With AppSync, the way that we're building, we actually have a direct mapping between the actual GraphQL operation, like query or mutation, directly into the database. So you could actually do this if you bypassed Lambda and went from API Gateway directly into the database. But you're also missing out in an important part of the API layer around authorization and authentication and stuff. So it's kind of hard to get that right without using something like AppSync or Lambda. So with AppSync, though, you actually have the resolver, has its own execution environment where you can do all the stuff that you need to around authorization. And it's actually, I think, a lot faster AppSync is than traditional REST API.
13. Configuring Client Project and Fetching Posts
As long as you're not going, if you're mapping your GraphQL operation into a Lambda function, you are gonna be adding a little bit of latency. We went to AppSync by running this command. We ran this mutation, we ran this query, we wanted to make sure everything's working, so it is. The next thing we wanna do is go ahead and configure our client project and test it out. We're gonna create a file called configure amplify.js and import it. We're gonna update our main index.js to fetch the posts. This is a basic React component that fetches posts from the GraphQL API and renders them on the screen.
As long as you're not going, if you're mapping your GraphQL operation into a Lambda function, you are gonna be adding a little bit of latency. So you can do that as well, which would then, therefore make AppSync and API gateway exactly the same. So they're both extremely fast. So, I mean, we're talking about tens of seconds of latency differences. So it's like for most use cases, it's not a big deal either way. All right. So anyway, we went to AppSync by running this command. We ran this mutation, we ran this query, we wanted to make sure everything's working, so it is. The next thing we wanna do is go ahead and configure our client project and test it out. So to do that, the client project needs to know about the resources that the Amplify CLI has created. So going back to our project, I kind of went over how we have this AWS exports file that was created by the CLI. All we really need to do to kind of kick things off is we wanna import this somewhere in our React or our Next.js or our view project and our base, of course, our Next.js project, so we can kind of let the client know about these resources. So the way that we're gonna do that is we're gonna create a file called configure amplify. And anytime we need to configure amplify, we can just import this and have those resources available. So I'm gonna go to my project and I'm gonna create a file called configure amplify.js. And we're just gonna go ahead and paste that there. I see a few questions like racking up, I'm gonna come back and get to those in just a couple minutes. So we add configure amplify.js here and we can now just use this anywhere we want. So we're good to go. So we can go ahead and just save that and close it off. And what we're gonna do now is we're gonna open pages slash underscore out.js. We're just gonna import this. And you typically only need to do this once, right? Like at the root of the project and you don't need to do this again for the most part. So when a React app you do this once and even in our next app. We're only gonna kind of do it once. But if you're building out API routes you're gonna need to actually import these into those API routes. We're not doing that. But let's say you did to get the actual resource location information you need to do this. And the reason is we talked about Lambda Edge. These API endpoints or these API routes are actually deployed as individual functions at Lambda Edge. And if they don't have all the necessary information then they're gonna be missing that. So that's just something to keep in mind if you're continuing to build. So we're gonna go to pages slash underscore app.js. We're just gonna go ahead and import that file. So we created Configure Amplify and then we imported it. And now we're good to go. The only thing we need to do now is go ahead and create some functionality that queries our GraphQL API, renders that data to the screen. So that's what we're gonna do next. Now, when I kind of gave the intro to this workshop, I talked about, we're gonna be mixing dynamic data fetching along with a static site generation and service side rendering. So we're gonna be kind of implementing all that stuff. Now, when you think about like a blog or any type of dynamic list of like individual items, you have to kind of think about how you might wanna render those. Do you want to kind of rebuild the entire app every time someone creates a blog post or a tweet? Probably not, right? Like for Twitter, for example, that would not make any sense. It's extremely dynamic. And for a blog, where a lot of people are gonna be writing, you might say that doesn't make any sense either. If you go to dev.to or Medium, they're not rebuilding their entire site every time someone creates a new post, what they are doing is, they're doing a combination of dynamic data fetching, and then they're also doing server-side rendering to get that SEO stuff. So we're kind of gonna be taking a similar approach. So don't, you might be wondering like, why are we just fetching data on the client? That's because we're gonna have this one list of posts. And then when someone clicks on a post and to view that individual post, that's where we're gonna be doing the static site generation and the server-side rendering. It just kind of makes sense, I think, for this type of app. So what we're gonna do now is just go ahead and update our main index.js to go ahead and fetch the posts. And we're gonna do that by creating, I'm sorry, by just copying this code and going to pages.index.js and replacing everything. And I'm gonna walk through like what's going on in this code there. So I'm gonna go to pages.index.js. It's gonna delete everything. I'm gonna paste this new code here. We're gonna save that. Now let's go ahead and like, walk through what's going on here. Okay. This is like a fairly basic React component when we kind of really look at it. If you take out the state and the hook and the function, when we take out this mapping of posts to kind of like map over an array, all we're really rendering is this like H1 of posts, right? So we can fundamentally think about it, like let's say we just started with this H1, we just have this header. This is going to be our list of posts. How are we going to kind of make that happen? We're going to be doing a couple of things. When the component loads, we want to go ahead and fetch the posts from our GraphQL API. We want to go ahead and save that in the local state. And then once that state has been updated, we want to re-render and kind of render those posts into our screen. So the way that we do that is we import, first of all, a couple of hooks from React. We import use state and use effect from React. Is this size okay with everyone by the way, or do I need to zoom in? Because this is really, I think, some of the more intense code. Okay, cool. And so we're importing use state and use effect.
14. Importing Components and Fetching Data
We import the link component for next link and the API class from AWS Amplify. We also import the list post query from our GraphQL queries. When the component loads, we call the API.graphQL method to fetch posts and update the post array. We use Tailwind classes for styling. We test the app by running npm run dev and see the basic functionality. Next, we add user authentication by running Amplify Add Auth and taking the default configuration. We then run Amplify Push to update the code.
We're also importing the link component for next link. The cool thing about next is that you don't need a router, you just create a page, and then you can navigate to that page without having to create any like routing logic by just using the link from their library. We import next, the API class from AWS Amplify. The API class could be thought of as like the GraphQL client of Amplify. If you've ever used Apollo or Oracle or any of those, it's kind of like our version of that. And it specifically works especially well with Amplify. We also import the list post query from queries, from our GraphQL queries. When we ran Amplify push and we did that GraphQL code gen, our CLI created a folder called GraphQL. And here we have our mutations, our queries and our subscriptions. So now we can just import those whenever we need them. So that's kind of what we're doing here. We're just importing from the queries, GraphQL folder, the queries that was created for us by the CLI.
When the component loads, we call this use effect took that calls fetch posts, fetch posts cause set posts and updates the empty post array with data that comes back from our GraphQL API. So the, the way that we're calling the API is right here. So these three lines of code, we call it API.graphQL fairly simple API. All we need to do is pass in the query. This is an async call. So we just kind of say, oh, wait, and then after the data comes back, we call set posts. Now what is the array that we're looking for? Well, if we go back to the absent console, you'll see that we have the data that comes back data.list posts.items. So items is kind of like nested. That's kind of what we're doing here, where we're saying we have the data.listposts.items. And this is the array that we're kind of setting. And then once we have that array, can we just like map over the array? And for every item we're going to be kind of returning a link. And then when the person clicks on that link later on, we're going to navigate the user to do that individual post. That's fairly straightforward. This is kind of more React stuff. If you don't know how this works, then just copy and paste it and dive into some React stuff another time. We're not going to go too deep into the React stuff.
One thing that's interesting to keep in mind is like, how are we going to be linking to the page? So if we run this and try to click on this, it's not going to work now, but later on it will. We, at some point need to have a folder called posts, where we generate the posts by ID in this posts folder. And then when someone clicks on a link, we're going to say like our app slash posts slash post ID. And this just needs to work. And that's going to be something we do later. But for now, we're going to go ahead and set that up, set us up for future success by going ahead and setting that. One thing I haven't gone over a whole lot is the Tailwind stuff. Tailwind, you know, is its own completely separate thing that we could spend a lot of time going over, but so let's just only talk about it in this component and then kind of like not talk about it again, because there's a lot to talk about. But in general, you have these classes that you can use and they all do like the same thing wherever you use them. So instead of you having to create like a class name is equal to text header large or something like that, and then like using that again somewhere, we have something called text 3xl and this we could use anywhere in our app. So we can kind of like not have to ever create our own class again, we can just use this. And I think the beautiful part is not only that we can use it in our app, but we can actually build like 100 apps and we can always understand these classes that we're kind of working with. So you could kind of just think of it as like, this set of classes that we can use and we understand what they do and there's a pretty good documentation around them. All right, cool. So that's pretty much it. Let's go ahead and test this out by running npm run dev and see what this looks like. And then what we're gonna do next is we're gonna add our user profile with authentication. So we should be able to go ahead and open our app on localhost 3000. All right. So when the app loads, we see some really ugly stuff going on, but at least we see something. We have our two posts and if I click on a post, we see that we have an error or 404 or something. Which is kind of exactly what we're looking for. But at least we see that the URL says slash post slash post ID. So we're getting somewhere. And you'll notice that it's really slow and all that. If you ever want to kind of run a build and see how fast Next.js is, you can run npm run build and npm start and it'll be like lightning fast. But for now, we're in Dev mode npm run dev, you know, we're running in Dev mode. So it's gonna be kind of slow. But it's gonna be pretty fast. All right, so we're fetching data. The next thing we want to do is we want to allow authentication so we can start creating and update our own data, because when we create something, I think the general idea is we want to be able to associate a user with that and be able to kind of, you know, give administration, I'm sorry, give authorization privileges and stuff for the people that created it. But for that to happen, we need to have the idea of like a user and things like that anyway. So to get that going, we want to add authentication and we can do that by running Amplify Add Auth. Let's go ahead and do that. So we're gonna go back to our CLI, we're gonna run Amplify Add Auth, and we're gonna be given just a few prompts. We're just gonna take all the defaults. So Amplify Add Auth, default configuration, username to sign in, and then we're done. And then we're gonna again run Amplify Push. So let's go ahead and try this out. So currently our code is at very high level, and no advanced connection is required. So let's go ahead and run that. We're gonna save this.
15. Implementing Server and Client Code
In this part, we implement the server-side code and execute it. We then move on to the client-side, improving the appearance, adding navigation, and creating a user profile. The profile component is wrapped with the withAuthenticator helper, which provides authentication flow. We use the useEffect hook to check the user's session and retrieve their metadata. The profile is saved and displayed. Additionally, we can style the UI components using CSS. Finally, we add navigation and styling to the main app using the Next.js app component.
I'm gonna go back to the server, implement it, execute that, and let's try it again, and yes, it works. So we're going back to the server, have a look at our sources here. Let me just make sure my results are correct. Let's give it a look. All right, so our authentication service is being deployed. Let's now build off the client a little bit more, make it look a little better, add some navigation, and also add a user profile. So let's move on to the code.
So in your main directory, create a new file called profile.js in the pages directory. So pages slash profile.js. And then we're going to go ahead and just paste in that code. Okay, so now that that's working, I can actually go ahead and open it up, add our code. So if we were working a little bit in a remote component like this, we would need to load this file to do so. So let's go ahead and ن and enter this and j. Our code should be ready for us to install now. A profile is like the header, then we have an h3 with the user's username and a paragraph with the user's email address. Now, how do we actually get that user data? That's kind of where the UI component that we're gonna be using comes into play. So if we just wanted to like export this component like we normally would, we would just say export default with the name of the component. But here we've actually wrapped it with this withAuthenticator helper, or you could call it an HOC, higher order component. What this basically does is anytime that you want to put an authentication flow in front of any component, in a React app with Amplify, you can use this withAuthenticator component. And we get that into our app by just importing it from the UI React library. When we created our project, we installed Amplify UI React. And we also installed AWS Amplify. And this is our first time using UI React. So, we're getting like these UI components. So, the two that we're getting is a signout button called Amplify signout, which we just drop in just like that, pretty simple. And then the other is the withAuthenticator. Now, if the user is signed in, then it will render this component, okay? If the user is not signed in, it will not render this component. And instead, it will kind of show a full authentication flow. Sign up, confirm signup with MFA, sign in, forgot password, all that stuff. So, that means that we don't really have to write any of that code. All we need to do is worry about, okay, let's just assume that they're signed in, because if they're not, that other part is already taken care of for us. If they are signed in, let's go ahead and find a way to find out who that user is by reading the session from the local storage or from the cookie storage. And in our case, it's gonna be local storage. And we want to go ahead and get that user metadata and kind of render that to the screen. So the way that we do that is, we have this useEffect hook that gets invoked when the component loads. We have a function called checkUser. CheckUser is pretty simple. It calls auth.currentAuthenticatedUser, which reads the currently signed in user off of the local storage. And then this gives us a user object that we can then call and save into the local state by calling setUser. Of course, user and setUser are just created using the useState hook. And we by default set the user to be null. So when the component loads, at first there is no user. And then once this is completed, then we update the user and we are able to return the UI. If there is no user, we just return null. So very compact component, a fair amount going on, but very powerful as far as what we're trying to get done. So this is gonna be our user profile. This is all we need. So when the user is set here, then we now have the user's username and we have an attributes object on the user that has their email address, their phone number, all that other stuff. So you could imagine, like how easy it might be to create a user profile with all the other metadata you might ask for that user. So that's it, we can go ahead and save the profile, we're done.
Now, by default, the width authenticator component that we just used has a yellow color, but for this app, we're kinda styling everything to be this like blue color. So this is kind of optional, if you wanna go along with the styling, then I would follow along. If you don't care, then you can skip this part. But the current implementation of the Amplify UI component library is that they're built using Web Components. And therefore, we can just set some like basic CSS styling and it'll apply to those elements whenever we would like to kind of set that styling. So the way that we're gonna do that is we're just gonna open styles-globals.css and we're gonna set a couple of properties, the Amplify primary color, the Amplify primary tint, the Amplify primary shade, and this is kind of just a blue color. So I'm gonna go ahead and copy that and open styles-global, we're just gonna add this right below the tail end. So I'm gonna do this last section here and then we're gonna go ahead and I'll answer a couple of questions.
So we've added the profile, but if we look at our app, there's really no way to actually get to that profile. So like, let's say if the profile did exist, how are we gonna get there? We need to add some navigation and also maybe add some styling to our main app to make it look a little better. So the way that we do that in Next.js, the app is essentially kind of the entry point and we're kind of returning the app here. That is the component that kind of comes in as an argument. So if we wanted to do any styling or something that was global around our entire app, we would do it here. And this is typically where a lot of people like to navigation and stuff, and that's kind of where we're doing our navigation. So we can go ahead and just copy this and pages slash underscore app.js. And, or if you want to kind of type this out, this is kind of what we're gonna be working with. So we still wanna import our styles. We still wanna import our configure amplify but we now wanna go ahead and import the link component because we're now gonna be adding some navigation. So we import link. And now instead of just returning the component before, we were just kind of returning the component.
16. Adding Padding and Navigation Links
We're adding a little bit of padding to make the app look nicer. The navigation now has three main links: the root, createPosts, and the profile. Users can click on the profile to access the authentication flow.
So it looks something like that. Remember, now we're actually bringing that component and we're returning it within the div. And we also added this new nav. So we have the nav at the top and the rest is kind of like kind of how it was before. One of the main difference is that if you remember earlier, that everything extended all the way out to the screen, there was no padding. It looked kind of ugly. So we're adding just a little bit of padding to make that look a little nicer. So we're adding a padding Y of eight and a padding X of 16. So padding on the top, bottom, left and right. And then for the navigation, we're gonna now have three main links. One is the root, kind of like where we have been operating already. We're creating a new route that we've yet to use called slash createPosts. This is gonna be where we have the UI for the form that allows you to create posts in just a moment. And then the other is the UI or the link for the profile that we just created. So when we run this now, the user should be able to click on the profile and see this whole authentication flow.
17. Testing the App and Data Storage
We test the app by running npm run dev and see the basic functionality. We can sign in, create an account, and view our profile. The data is stored in DynamoDB, which is known for its scalability. Users are stored in the Amplify Console, where you can manage and view user information. Amplify supports types for GraphQL and allows you to create your own queries and mutations alongside what AppSync provides. Typescript types can be generated by choosing TypeScript as the codegen target. Reverting changes made with Amplify CLI may not be easy if a big mistake is made.
All right, so with that being said, we've kind of written a decent amount of code without testing it out. So let's go ahead and test this out. So to test that out, we can write npm run dev. We'll go back to localhost 3000. And we should see a significant improvement over our last time we checked this out in general.
All right, so this is what our app looks like now. We have our two posts still, but we have a little bit of padding around it. It looks a little better. But most importantly, we have our profile. And if I click on Profile, we should see that we have a way to sign in and sign up as well. I've kind of zoomed in, so it should look something closer to like that something. But I'm going to go ahead and sign in. I'm going to zoom in so everyone can see pretty good. So now on the profile, since we're not signed in, we're seeing this component. And if we are signed in, we'll see our profile. In order to sign in, we first need to create an account. So let's go ahead and click Create An Account. I'm just going to put in whatever for the phone number because it doesn't matter for me. And I should receive an email verification code. And I should be able to sign in.
All right, so if we're signed in, we should now see our profile. So we have the profile, the username of David3, email. We're kind of getting somewhere, right? If we refresh, we stay signed in. Again, it's kind of slow because we're in dev mode, but if you run NPM build and NPM start, it should be a lot faster. And then if we sign out, you can now sign back in with those new credentials. So I'm going to answer a couple of questions, then we're going to take a quick break.
18. Creating Environments and Custom Resolvers
The typical workflow is you have three environments, at least, let's say three. You have your production, your staging, and then your dev. So you could say Amplify ENV, add MyENV, and then that would create a new environment, which basically takes all of that infrastructure's code and copies it into a brand-new project, and you could deploy that and test it out, add stuff. And when you've added the things that are different than your main environment, you can actually merge those into your other environment after you've tested them out, and then maybe merge those into your production environment after you've tested those out. So we're not going to be looking at Amplify ENV, but if you want to kind of work on that type of workflow, check it out.
Does Amplify have hooks? We don't really have any hooks that are built and maintained by us, but a few people in the community have done that. Using local storage has security issues. Isn't it? That's a good question that a lot of people always have. It doesn't have any more issues than using cookie storage or any other mechanism. I think you're kind of trading, I guess, one perceived issue for other issues. And in reality, there's no, I would say, perfect way of going about dealing with any type of security mechanism on the client. They all are having their own, I would say, trade-offs. But if you look at the implementation that we have, someone did a decent kind of overview of it on Twitter where they're an information security person and they went into all the different mechanisms that go back and forth when you just make a simple authentication request with Cognito. I think there was a dozen, or over a dozen different things that go back and forth. But I would say, whenever we deploy something in AWS security is always the number one concern. And for Amplify, whenever we make any update or anything at all, for any of the stuff that we do, it goes through just probably the most robust security scrutiny that you could possibly think of. We've never had with Amplify, any customers that have ever had any security concerns or anything like that or problems that we're aware of. So, does local storage have security issues? I think everything can be exploited if the right circumstances are in place. I would say that, but we've never seen it happen.
Can we customize the automated email templates? Yes, you can. The way that you can do that is either by running Amplify Update Auth. No. Gorse, spelled like this. But yeah, you can do it that way or when you're running Amplify Add Auth, instead of setting the default, you can actually go ahead and walk through that process on the initial setup. Or if you want to, you can go into your user pool and update it here. I forgot exactly where it is, but I think it's under message customizations. You can say, hello from Matter. And then of course you can do, you can write HTML and other stuff and dynamic stuff. So, you can do that all right here.
We're back and we will go ahead and wrap this up. And we have 35 minutes. So we're going to kind of start going a little bit faster because we still have a lot to cover and want to make sure we cover as much as possible. So what I would like to accomplish next would be the ability to create posts, update, delete posts, and add images to the posts.
19. Updating Authorization and GraphQL Schema
To associate a user with the posts, we need to update the authorization. We update the settings to add API key and Amazon Cognito User Pool for both public and private access. Next, we update the GraphQL schema to include the username field to identify the owner of the post. We also add authorization rules using the at auth directive, allowing owner access and setting the owner field as the username. Additionally, we add public access with read-only permissions. We create an additional data access pattern to query posts by username using the at-key directive to create a new index on the database.
So let's see how far we can go. So the next thing I want to do is, in order to associate a user with the posts, we need to update the authorization. We need to be able to kind of associate a user with the posts. And typically, that's done by adding some type of field on a top-level item to kind of identify the owner. A lot of times this is done using something like a UUID or some type of unique identifier associating the user to a piece of content. And in our case, we're going to be using something that's built in to amplify. That's going to be able to allow us to take the username and associate that with a post in our case. To get started doing this and we'll explain more, let's go ahead and run Amplify update API. Keeping my server running, I'm going to go ahead and choose GraphQL. And what we want to do is we want to update the settings. Right now we only have API key, but we now want to add API key as well as Cognito. This is going to allow us to have both public and private access. So to do that, we're going to choose API key for the default authorization. And then we're going to choose Amazon Cognito User Pool for the additional type. So we're going to kind of walk through these similar steps, update all settings, API key for the base type. We'll keep the name of public. We're going to set the expiration for 365 days. We do want to configure additional auth types. And we're just going to highlight Amazon Cognito User Pools by hitting the spacebar, in my case. All right, so we've updated our authorization for our API, meaning that we can now send both public and authenticated requests to AppSync using the endpoint that was given to us after we deploy this. But that's not really enough. We need to actually update our GraphQL schema. So I'm going to go ahead and go to back end slash name of our API. And we're going to open schema.graphql. We're going to do a little bit more work here. So if I go to the Workshop, I can now see that I have an updated schema. So I'm going to go ahead and copy this schema. And I'm going to go ahead and update this here. So if we remember, our old schema, we had an ID, a title, and a content field. We're adding one additional field here. This is going to be the username, meaning that this is the username of the user that created this post. That's kind of like the owner. And this is the way that we're going to add one piece of authorization around this item. A couple of other things that we're adding. And I'll kind of break this schema apart a little more so you can kind of see the different pieces. So here we have our fields. We've gone over that. We now have two additional pieces as well using these directives. So we have the at model. At model gives us the database and the resolvers. You know, we used that earlier. Let's first look at at auth. At auth allows you to add authorization rules to a top level type or a field. So I might want to say I want to have some type of authorization for this field only. So I could use it here. Or if you use it at the top level, you're kind of saying I want these rules to be applied to this entire type of post. This takes an array of rules and each array of rules has a few different properties. And what we're doing here is we're going to say we want to allow owner access and owner access means the person that created this or that owns this item that's created. In our case, whenever someone creates a new post, that means they have full access over the post. That means they can create, read, update, delete, they can do everything. And then we also we wanna say, let's set the owner field to a specific property. And in our case, we're gonna be setting it as a username. Okay, so that means like if I go into my database, I should actually see David3 as a username for the post that I create. The next thing we wanna do is we wanna add public access, meaning that anyone that wants to access this can do so, but we're gonna specify which operations that they can do. And what makes a lot of sense is for something like a blog or something like that, we just wanna let those people read it. We don't wanna let them make updates, or deletes, or anything like that. So we can just pass in this array of operations, and this is only gonna give them read access. So we have private access for the owner, public access for everyone else just for reading. Another popular form of access is, this isn't something I would add here, I'm just going over it is something like group access. And here you can, I think it's like group, and then you can say groups, and you could say groups, and you could pass in an array of groups, something like admin. And this would allow you to kind of give administrator privileges as well, or something like that. Okay, the other thing that we wanna do is, we wanna add an additional data access pattern. Meaning that when I told you about like the API that's being created, we have a way to create, read, update, delete and list posts. But what we need is an additional way to query for posts, because by default, the list post query is just gonna return all the posts. It doesn't matter how many are there, it's just gonna return all of them. We want to now have one additional GraphQL query where we can say, we want to query posts by username, meaning I wanna query for like a post that I created or that someone else created only. And we wanna have a very efficient way to query our database based on this query. So to do that, we can use the at-key directive and here we can set a few properties. The at-key is gonna create a new index on our database, meaning it's gonna kind of give us a new way to query our data using one of the different fields that we have here. So let's say I wanted to query by title or content or username, you could do that using this at-key directive.
20. Querying Posts by Username and Deploying Updates
We're querying posts by username and creating a selection set for our own posts. This is the most performant way to view our own posts. We can now deploy the updates using Amplify.push.
Now in our case, we're gonna be querying by username. So for the fields we're passing in, the only field that we're passing in is username. And then now we can say, we wanna hit this username index with a query that we're gonna be calling posts by username. And now I can say, I want to get all posts where username is equal to DABITT3. And now I have like a selection set of like my posts. So this is just gonna give us a new query called posts by username. It's gonna be able to hit and query just this field here. And it's gonna return a selection set for that. So now we have two like main queries for an array of posts. We can get all the posts or we can query for posts just by the username. And this is important for our app because we wanna have like a profile view where we can only view our own posts. And this can be done in a couple of different ways. Of course, you could hit the database, bring back everything and filter on the client. That's not a good approach though and not very performant. It doesn't make a lot of sense. So this is kind of the most performant way to do that. So let's go ahead and save that. So essentially I just kind of walked through this. You could just copy and paste this and hit save and you're good to go. What we wanna now do is go ahead and deploy the updates. So to do that, I'm gonna run Amplify.push and we're gonna be good. Dash dash y just kind of automatically will say yes to whatever questions that come after. You can do that any time in your development of Amplify.
21. Creating Post UI and Dynamic Page Generation
We create a user interface for creating posts, including a form and a button to send the post to the backend. We use the authenticator for authentication, the useState hook for state management, the API class for API calls, and the UUID library to generate post IDs. We use the use router hook to programmatically route the user after creating a post. We also use the simple markdown editor for easy markdown input. The form state is stored using the useState hook, and an on-chain handler is used to update the state. We send a mutation to create a new post, passing the post data and the authorization mode. After the post is created, we use router.push to navigate to the post view. We work with Next.js APIs to generate dynamic pages for each post, similar to static site generation with Gatsby. The page generation is done within the component using git static paths and git static props.
Okay, so while that's deploying, let's go ahead and start creating the user interface for creating posts. I'm not gonna walk through this right now, we're gonna look at that later. But the next page that we're gonna create is a create post page. This is gonna be like a big form that has the input that is gonna allow the user to create a post. And it's gonna also have a button for sending the post to the backend and all that stuff. So this is a lot of code, so I'm just gonna go ahead and copy and paste it into a new page. I'm gonna call create post.
Alright, so a couple of things that we've already done before. We're using the authenticator again, because if someone is creating a post, they have to be signed in. So we're just going to wrap this page in that just to put authentication flow in front of it just in case they somehow got there without it. We're going to be using the useState hook as usual. We're using the API class as we've been doing. We're going to be importing the UUID library to generate a unique identifier on the client, and then that way, we're going to be setting an ID for the post when we create one. We're going to be using the use router hook to programmatically route the user. So when the user creates a new post, after the post has been created in the database, we want to then route them to view that post. That's the general idea there. We're using the simple markdown editor, which renders a markdown editor with just really a single line of code. So right here, we have that simple markdown editor. It's super easy to use the simple markdown editor. So let me actually break this maybe in a couple lines, go over the different properties. So this will render a markdown editor. The only two props that we have to pass to it are the values. This is going to be just a string of value that is going to be the markdown that we're writing. Then we have an on-chain handler. So whenever we type into this form field, which is essentially all it is, we're going to be updating the local state to make that update. That's about it. This is self-explanatory. We're importing the CSS for the markdown editor. We're importing our GraphQL operation. In this case, it's a mutation. We set some initial state. The only state that we really need to keep up with for someone creating a new post is the post title and the post content. The ID is going to be generated using the UUID library. The actual username is going to be automatically read by the AppSync service off of the access token. We don't even need to pass that in, it's automatically going to be read on the server securely. When you're creating a form in React, you typically are going to be doing two main things when you're creating a form in React. First of all, you need to have a place to store the form state as the user is typing. A lot of times you'll have an object that has all the different fields. If you think of an authentication form, you might have something like the username, password, e-mail address, you're storing all that stuff. You're creating a form, you just store the user's input. The other thing that you need to have is some type of on-chain handler that is attached to the input, that is going to be updating that state. That's what we have here. We have an on-chain handler that's going to be updating the local state and we also have the state itself. So pretty basic stuff there. The only other thing that we're going to be working with that's a little different than before is when we query for posts, the only thing that we did was we called api.GraphQL and we pass in a query of list posts. This is the only thing we need to do to fetch the posts and this brought us back an array. The main difference here is that instead of just querying, we're actually sending a mutation and we have to pass along some data along with that mutation. The data in our case is going to be the posts. We're constructing a post object where we have the post that's coming off of the state here. We have the post and then we now are appending an ID to that post. We have a post title, content, and ID and now we're ready to send that to our back-end. Since we're using multiple authorization modes, we also need to define an off mode if it's not the default. The default in our case is API key. If we are querying, we wouldn't need to pass this. But since we're using the other authorization mode, we need to specify this. In our case, we're just specifying that we want to use Cognito. We're basically going to make this API call, wait for that to happen, and then we're going to say router.push, and we're going to go to slash post slash post ID. Then the rest of this is fairly simple. We just have an input for the post title, the Markdia Editor for the post content, and then a button that's going to be invoking the CreatePost function. All right?
The next thing that we're going to work with is we're going to be working with the Next.js, a couple of Next.js APIs that we haven't worked with. So the idea here is that we want to be able to create n number of posts, regardless of how many are like in our back end, we want to be able to generate those pages. So this is kind of like in the realm of static site generation. So if you ever worked with Gatsby or something, you have this build process that goes and fetches like 20 posts maybe from your API. Maybe that's like some type of CMS or something. And then you have this array and then you have this function that maps over those posts and creates a new page for every post in that array. And then when your build is done, you have your Gatsby site with your 20 pages built out into HTML. Very similar thing is happening. But the API is a lot different, I think in next.js. The page generation that Gatsby does in that build is actually done within the component itself. And it's using a combination of git static paths and git static props.
22. Generating Dynamic Pages with Gatsby
Git static paths generates an array of IDs to construct the paths array. The git static props function is invoked multiple times to generate pages based on the IDs. The posts are fetched at build time and the paths array is constructed. The get static props function is called with the post ID as a parameter. The post metadata, including the title, content, and username, is retrieved from the GraphQL API. The data is passed down as props to the page, where it is rendered. Gatsby's power lies in its ability to handle complex functionality with minimal code.
Git static paths is going to get that array of IDs that we want to generate. And it's going to basically construct this paths array. And then we're going to return the paths array from git static paths. And let's say there's 20 items in this array with 20 IDs. We're basically going to invoke this git static props function 20 times, and it's going to generate 20 pages based on that ID. So let's say like you've created, Hello World. This is my first blog post. And then Hello World is my second blog post. You have that data stored in AppSync. We have the title, the content and the ID. What we basically want to do is we want to generate those two blog posts at build time or and what we're going to talk about how we might do that actually run time later. But let's say we want to build those at build time. We run a build. It's going to go ahead and fetch those two posts. We're going to construct this paths array that's going to have an object that has the post ID in it. So we're going to have, let's say two posts and this array of two posts is going to be then, um, going to be calling get static props, probs twice. Inside the params, we're going to have the ID for that post. We then call the graph tool API again, passing in that ID. And now we're getting the entire post, uh, metadata like that post title, post content, all the other metadata. And then we're able to pass that data down into the actual page itself as props. And in the page, we're getting that, that, that post coming in as a prop. And what we can basically do is now render the post title, post content in markdown form, and then the username of the person that created it. So like a ton of stuff happening in this component, but it's also very easy to because it's like not a lot of code. I think that speaks to how powerful Gatsby is. I'm sorry, index.js.
23. Creating Posts and Updating Main List
We create a folder called posts and a file called id.js. We update our main list to show the post owner. We need to delete old data that is no longer consistent with our schema. To do that, we run Amplify Console API, choose GraphQL, click on Data Sources, click on the link to the database, and delete the items. We can generate the ID on the server, but we generated it on the client for navigation purposes. After deleting the old data, we can test the app by creating a post and navigating to view it.
So what I want to do is I want to create, what we're going to do to make this happen is we need to create a folder called posts. So I'm going to create a folder called posts. And then in the folder here, I'm going to create a file called id.js. So pages slash post slash id.js. And what I'm going to do is save that. And I'm not ready to really try this out yet, but if you did click on Create Post, and your your app is still running, I believe you should see the form, but we're not quite done yet, but it is there. So you should see something like this. And you should be able to kind of like start doing some stuff like that. And then you might be able to even preview it. Doesn't look very good. But yeah. So we're still working on it. So let's go back to our workshop. The last thing we want to do is we want to update our main list here to show the owner or the person that actually wrote the post, because right now, the only thing we're rendering is like the title. So instead, we want to render the title as well as like DaBit3, the username of the person that created it. The main thing we're really doing is kind of adding this line of code here, post.username. So really, you could probably just add that. Let's try that, unless there's something else on this, which I'm pretty sure I'm not. Yeah, so you can either copy and paste this whole component back into pages slash Index.js, or you could just add I think this line here. All right. so we have that showing up. So we're good, even though there's no author showing up. And that makes sense. Because when we created our posts earlier, we did not have authorization enabled. So with that being said, what we want to do now is we have that old, it's kind of like stale, you can think of it like stale data at this point, because the data is no longer consistent with our schema that has the owner field. So we need to delete that old data. So to do that, you're going to run Amplify Console API. Choose GraphQL. You know, essentially, we're going to try to get to this background. We're going to try to get to this back to this DynamoDB console view. But let's say that you're not here already. Let's go ahead and get there again. You're going to run Amplify Console API. Choose GraphQL. Click on Data Sources, click on the link to the database, and then click on Items and delete those. So it looks like this. Data sources, database. Someone asked about generating the ID on the server. Yeah, you can definitely do that. In fact, if you don't pass an ID in, it'll automatically generate on the server. The reason I generate on the client was that I wanted to, and I did this for not really any apparent reason, but after you create a post, we go ahead and navigate to view that post. And to do that, we needed to, I guess we didn't really have to, we could have waited for that post to create and gotten the return value since we're using GraphQL. So you don't have to do that. You can generate the ID on the server for sure, and app sync automatically will do that for you. Yeah, exactly, yeah, that's a good point. We could have done that. So I'll go ahead and delete these two. I was for a database. In fact, I might update the workshop to do that next time. So if I refresh, then you'll probably see that we have no posts, which is good. But so we're ready to kind of go ahead and test this out. So I'm going to go ahead and go to create posts.
Final Remarks and Q&A
We have covered the core functionality of authentication, API, and authorization. Next, we will focus on showing only our own posts, dynamically updating the UI based on authentication state, and allowing users to update and upload cover images to S3. There are plans to improve logging and metrics in Amplify, although it may not be as robust as Sentry or Rollbar. AppSync can automatically generate IDs on the server, and the add key directive maps to DynamoDB global secondary indexes.
Okay. We all paste some code and get a picture. Okay. All right, so we go ahead and create the basic post. We should be able to go ahead and click create posts after the post is created. And it should go ahead and navigate to the actual post itself. And we can now see that we have a pretty basic post going on. We have the title. We have the author. We have the content. And it's formatted in a fairly decent way. We even have some code formatting and stuff and we're able to render images. The one thing we're not quite doing yet is that we will do later. And I don't think we're going to get to it of course, with the time that we have left, is that we're going to be able to add a way to upload images to S3 and render those as like a header image. And also we want to be able to give users the ability to edit and delete their posts. So that's kind of like what we have to look forward to next. If we want to make this run faster, like to kind of see how this actually runs in production, instead of running NPM run dev, we might say NPM run build, we want to rebuild... This is taking a while. I'm not sure if I can show you the time. Well, if not, I'll have to stop sharing again. But I think I can see why this is. I can't tell you exactly how long it's been. But let me just give you the numbers. So yes, it's the top among the numbers and, it is kind of off the chart, it's looks like it's kind of, the top of the checklist around the 16th, but really bottom of the chest. So the bottom of the list is 30. That's when you get to, that's what's essentially happening now. And those 30 are so called they are all going to be in these numbers which are basically in these cycles. No, try running MPM run on build one more time. Looks like the build happened but this is actually like a better computer than I used to have at Amazon believe it or not. I mean, it's not like an m one or nothing but see your 16 gig memory I seven processor Michael, a ton of storage one terabyte cheats. Victor does not code yet. See, I don't know what's going on with this. This bill. Anyway. It does seem it does seem to be a problem with my machine. There we go. But I think unless I just canceled it. Now the bill didn't finish. Anyway, npm run bild, like should work. I'm wondering if this is just, I just set this computer up like today or yesterday. Yeah, anyway, so we're kind of like there on Tom, honestly, like we only have five minutes left. And I want to specify. I want to get to like these questions and stuff that I can get you for the next five minutes. but I think we've covered like the core functionality of like what we're building. We've done the authentication, we've done the API, we've added authorization. If we go to DynamoDB and we refresh, we should see that like the posts now also have a username associated with them. So like, we're getting there on the authentication authorization part. So we're kind of like covered the most important stuff. What you're going to be covering next is what we're talking about. It's going to be like a new link that's going to show only our own posts. And we also want to be able to kind of like dynamically hide and show navigation based on whether the user is signed in. Again, a core piece of almost any application, we want to be able to update our UI based on the user's authentication state. So we're doing that and then the last thing we're doing is we're allowing users to update and upload cover images to SDGT. So anything that will be more or less used by the end-user will be covered cover images to S3. So if you continue with the workshop, you'll get to that. I see there's a lot of questions and stuff, so I'm hoping that I can answer some of these. I see Ali's in here. Hey Ali, I miss you already. Hope you all are doing good. Are there any plans to improve CloudWatch and overall insight into running services? It can be very hard to trace what's going on in the logs. Are there any teams working on something like Sentry or Rollbar? There is actually some work being done as far as logging and metrics are concerned for Amplify. As far as something like Sentry or Rollbar, I can't speak exactly to what it's going to look like, but it's going to be an improvement around what we have. It might not be as solid as far as error reporting or crash reporting as something like Sentry or anything like that. Can AppSync generate an ID for us? Yeah. I think I actually saw that one in answer earlier. AppSync will automatically generate the ID on the server if you don't pass one in. Do add key directives map to DynamoDB global secondary indexes on DynamoDB, or do we need to use something for those? Yes. The add key is essentially just doing that under the hood. So if we look at the schema that we created earlier, that's exactly right. In fact, the name that we're passing in here is the actual global secondary index name.
Creating Indexes for Efficient Data Access
I created a GSI called post by username in DynamoDB to enable performant queries and efficient data access patterns. Indexes allow querying directly on specific fields, enabling precise filtering and retrieval of data. By specifying certain values within the content, title, or username fields, you can retrieve data that matches the criteria, resulting in highly performant queries. If you want to learn more about this topic, I have an intermediate to advanced level video that delves deeper into modeling data access patterns with DynamoDB and Amplify.
So I created a GSI called post by username. And if I go to DynamoDB, when I go to indexes, I see that it's post by username. And the way I talk, the way I think about indexes, and if you're a back end developer, if you've dealt with databases, you're probably already, like, familiar with this. But if you're not, and the index is basically saying, okay, like, I have all these different fields in my database. I want to be able to start querying directly on these fields. So I might want to say, instead of getting everything and not being able to, like, specify what I want, I want to say, I want to, like, say I only want to get certain things that have a certain value within the content or the title or the username or so on and so forth. And then I can basically have a very performant query on this index, and then I can say it equals dabit three or it's less than 100 or equals 35 or whatever. And you're getting these very performant queries and these very, like, really good data access patterns that are starting to become enabled when you start working with indexes.