Hands on Workshop - Building a Serverless Multi-user Blogging Platform with Next.js, Tailwind, & AWS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

It was a great little walk-through for someone new to the SSR considerations like me. I found it weird though that the front-end ultimately got hosted via Serverless Framework. I thought SSR support in amplify was now?

šŸ‘ļøŽ︎ 1 šŸ‘¤ļøŽ︎ u/bacchusz šŸ“…ļøŽ︎ Jan 26 2021 šŸ—«︎ replies
Captions
all right so in this video we're going to be building a multi-user blogging platform with nexjs tailwind and aws and we're going to be using aws amplify now a lot of my videos often go into these one-off use cases and i've created maybe a couple that go a little bit more in depth but i've heard from a lot of viewers that they want to see something end-to-end we want to add all these different features put them together and build out something you know unique something interesting kind of showing how to tie everything together so that's what we're going to do in this video we're going to be building everything from scratch we're going to be building a blogging platform for uh people to be able to create an account sign in create a post view and edit their own post publish a post delete a post add cover images we're going to be adding a lot of different aws services along the way so we'll be using cognito for authentication we'll be using s3 for image storage we'll be using appsync for the graphql api and we'll be using dynamodb for the database now this is actually a hands-on workshop so you're going to see uh linked into the comments an actual link to this workshop so you can actually copy and paste the code and code along so this is kind of like a combination of live coding along with a workshop that you can then take and use yourself and then as part of the workshop there will be within the code base an actual uh you know fully deployed version of the app that we're building so you can kind of go and reference that if you do end up you know having any issues so this is um going to be you know a fairly longer tutorial video than i typically do but hopefully by the end of it you kind of come away with a lot of new ideas and you understand how this stack works and how all the pieces work together so with that being said let's go ahead and get started by creating a new next.js app so the first thing that we'll do is we'll go ahead and create a new next app and once the app is created we're going to go ahead and change into the directory and install the dependencies that we're going to need we're going to be using a lot of different dependencies so we're going to be using tailwind dependencies we're going to be using amplify dependencies we're going to be having a markdown editor that we're going to be needing so we're going to be installing some um you know dependencies for that as well so um i have all of these dependencies linked or listed in the docs that are accompanying this workshop so you can just copy and paste from there but i'm going to walk through them here as well so we're going to be adding aws amplify aws amplify ui react for the ui library for react we're adding a simple markdown editor and we're also adding a way to render markdown and we're adding the uuid library to start off and then next we're going to install the tailwind dependencies separately the next thing we're going to do is go ahead and install those tailwind dependencies i mentioned so we're going to install the ones that tailwind recommends you know starting a new project with so that's tailwind at latest post css and auto prefixer and then we're also adding tailwind css topography for our markdown editor so we can add some default styling when we render markdown for things like code snippets and headings and things like that all right so now we need to initialize our tailwind configuration so i'm going to say npx tailwind init and i'm going to say dash p and now we should see that we have this post css config this tailwind config created for us and we're going to go ahead and open up our code editor for the first time try that one more time all right so we have our code editor and we're going to go ahead and add a configuration to our tailwind config because essentially we install that topography plug-in and we want to get it go ahead and just kind of enable it by passing it in to the plugins array so we want to say require tailwind css topography and we're good to go there the next thing we want to do is go into our globals.css file and add our tailwind globals so i'm going to go here and i'm just going to kind of remove everything that's there and i'm going to say i'm going to add tailwind base tailwind components and tailwind utilities this is all kind of part of the base setup when you're working with tailwind and xjs and now that we're done with that setup we want to go ahead and initialize a new amplify project now if you don't have the amplify cli installed um go ahead and go to the amplify docs at docs let's see the actual address so it's docs.amplify.aws and click on get started and that that part of the tutorial will show you how to install and configure the cli or click on cli and then install the amplify cli and that will help you install the cli so once you've installed and configured the cli though you can go ahead and start creating a new project and that's kind of what we're going to do at this next step here so what we want to do now is go ahead and run amplify init to initialize a new amplify project and this is going to walk us through all the steps that we're going to need to go ahead and get this set up so for the name of the project you know this doesn't really matter i'll call this the default of amplify next we can give an environment name i'll take the default as well i'm going to choose vs code as my text editor our app is javascript and the framework that we're using is react actually it could have been next if we have that option but we don't for our source directory path because we're working with the next project the root is essentially the source directory now this doesn't matter a whole lot but it will start generating the the files that we need by the amplify cli at the root of the directory when we when we choose this and then for the distribution directory we can choose dot next and we can take the defaults for the build command and the start command and for our aws profile this is something you should have now that you've run amplify configure after installing the cli or if you're an amplifi user you probably already know this and then we can choose whatever profile that we'd like to use for me that's going to be a default all right so after the amplify project has been created you'll have this amplify folder with um your you know future code from the services that we create and then we have our aws exports file that is generated and will automatically be updated for us and we'll be using that in just a moment to configure our project but the first thing we might want to do is add a feature so the first thing we might need is a way to have a user create posts and we want to list the posts and things like that so we might create an api with some data so to do that we're going to use the amplify api category i'm going to say amplify add api we'll add a graphql api so i'll choose graphql for the api name i might call this next blog or something like that i will choose for now the api key as the default authorization type in the future and you know in a moment we're going to be updating that to be a combination of private and public access the private access would be meaning a user needs to be signed in and that's a way that we can kind of identify who creates a post and things like that but for now we're just going to create a public api to kind of get things going and that means we're going to use an api key to access so for the name of the api key this doesn't matter a whole lot but i'll call this something like public i'll give it an expiration of one year i don't need any advanced settings i don't have a schema yet so we'll choose no and then i will choose a boilerplate of just a single object with fields to kind of get us started but we're going to be kind of changing this up anyway and creating our own schema and then i'll choose yes to go ahead and edit the schema so this is kind of the schema that you know amplify will give us but basically we want to have a blogging app so therefore we might consider having a post type the post might have an id for uh uniquely identifying the post we might want to have a title for the post title and then some content later on though we're going to be adding additional fields like ways to identify the user that created the post and also a way to add a cover image but for now let's go ahead and save that and deploy this api so to do that i'll go ahead and you know just save this file for the schema and i'll run amplify push now the first time we run amplify push it's going to kind of walk us through a configuration for local code generation because when you're working with a graphql api you typically need to have some local definitions for your graphql operations now for us we want to be able to kind of have all of those things generated for us so the cli will do that so we'll have all the queries mutations and subscriptions created for us if we would like and if we do want that we can just choose yes here and i will choose javascript as the generation target now for the location since we don't have a source directory we might want to just have the graphql definitions created at the root so instead of choosing the default of src i'm going to go ahead and choose dot graphql or dot slash graphql and then i'm going to say slash star star star.js meaning that we want to basically say create the the directories of graphql slash queries graphql slash mutations and things like that and um that way we have all that stuff created at the root directory so we can just import it from there do i want to generate all possible operations sure and then for the maximum statement depth i'll just choose the default all right so once the api has been created we should see some output like this and then if we look in our directory we should see now that we have a graphql folder and you know in the graphql folder we'll see that we have queries mutations and subscriptions and things like that and then also if we open up our um aws exports file we'll see that we have some configuration around our graphql endpoint and things like that so that means our api was created successfully so now what we might want to do is go ahead and add some data and test out our api real quick and then we're going to query for that data on our next app so to open up our api we can run amplify console api and then this should open our graphql api which is appsync in our aws dashboard so it should automatically open up for you something like this so what we want to do now is let's go ahead and create a mutation or maybe two and then um and then query for those to kind of see if they come back so what i'm going to do is i'm going to create a post with the title of my first post with some content and you're going to notice that we don't pass an id that's because the the service will automatically generate an id on the server so i'll go ahead and create a post then i'll say maybe my second post and then now we might want to go ahead and say query list posts and then for the items we want to kind of return like the id title and content and then we should see that we have our list of quotes coming back so our api is working we can also go to our data sources here and open up dynamodb and see that we have some items in our database so now that we have that working let's go ahead and update our next app to start querying for that data so to do that we're going to need to use this aws exports file that we created earlier or that was created for us earlier i guess by the cli and we're going to create a file called configure amplify that we can then just import whenever we need to to use that file to configure amplify so in the root of the project i'm going to go ahead and create a file called configure amplify.js and in this file we're going to import amplify from aws amplify we're going to import the configuration from that aws exports file and then we're going to call amplify.configure passing the passing in the config now to use this file we can just go in any to use this configuration we can go in any file and just import it so you can you know you could also do this within the file but i think this is an easier way to do it if you need it in multiple areas which you may need to in the next app we're not going to need to in this app but if you use api routes you do need to so i'm imported configure amplify so now we should be able to call our graphql api so the next thing i'm going to do is i'm going to go to index.js and we're going to go ahead and clear all this out and we're going to start writing some code so the first thing i'm going to do here is i'm going to go ahead and import a few things first of all we're going to import use state and use effect from react we're going to import the link component from next link and this is going to allow us to create some navigation we're going to create you know later on but we're not going to need it i don't believe at the moment other than just linking to a post in the future for an individual post we're going to import the api category from aws amplify and we're going to import the list post query from our graphql queries the next thing that we're going to do is we'll go ahead and create a function and we'll maybe say export default and this is going to be our you know our main function and what we might want to do is go ahead and create some initial state to hold our posts so what we're going to go ahead and do is maybe say something like const posts set posts it's equal to use state and then we'll pass in an empty array so now we have a way to kind of you know fetch some posts and then store them in our local state so what we're going to next do is we'll go ahead and create a function and invoke the function and use effect so we're going to say and use effect we want to call this function called fetch posts fetch posts is going to call the api.graphql operation we're going to use the api import here called graphql the query that we're going to pass in is this list post query and then once that data comes back in a variable called post data we're going to set the post array using the postdata.data.listpost.items which is kind of the just the array of items that we want without anything else and then finally in our ui we want to just return um you know that array of posts that we're going to be mapping over so let's take a look at this code we're returning like an outer div to kind of wrap a header that just has our posts we set some styling using tailwind so here we're kind of saying we want to use text 3xl meaning we want to have some you know larger text we're going to be using semi-bold font we want our letter spacing to be a little wider than the default we have a margin top of 6 and a margin bottom of 2. so that's just going to be our header and then we're going to map over the array of posts and for every post in that array and in our case that should be two we're going to be returning a link to the post and that's going to be at the slash post slash post dot id and and we don't actually have that created yet so if you click on this it's not going to work but in the future in just a moment we're going to be creating that that actual file and then for every post in the array we're going to return like a div where we want to set some styling like we want to make sure that the cursor is is a pointer cursor cursor we're going to have a border of one pixel using this gray light gray color of 300 we have margin top of eight and padding bottom of four and then for every item in the array we're just going to return the post title along with a little bit more styling and that's kind of it um this should show just like a title with an array of posts and then in the future in a second we're going to be able to drill down and view that individual post so to test this out let's go back to our command line and run npm run dev and we should now be able to go to localhost 3000 and test this out so let's go ahead and open up localhost 3000 looks like we have some type of error uh let's see what's going on there maybe i didn't save all of my files so let me go ahead and make sure i save all three of these and then we refresh there we go so i hadn't saved one of those files so now we see that we have my first post and my second post coming back so that's great that means our actual api is being called and our data is being returned meaning you know we're interacting with our backend so that's a great start but we still need to add a bunch of new things so the first thing we might want to do we want to be able to kind of have users the ability to update their own posts and identify who created a post but before we can do that we need to have some type of authentication to kind of authenticate a user so to add authentication let's go ahead and go back to the command line and run amplify add off and this is going to allow us to add authentication now you can either add username and password authentication or using social provider like facebook or google or a combination of the both so it's up to you if you want social login you would choose either default configuration with social provider or manual configuration if you just want to allow username and password like we're going to do in this tutorial you can choose default and for users i'll allow them to use their username to sign in and i don't need any advanced settings now to deploy this update i'm going to go ahead and run amplified push and this will go ahead and deploy that authentication service so while that's deploying what we want to do now is we need to kind of have a new route for users to be able to to sign in and to kind of authenticate maybe show them a profile so in our pages directory we want to add a new file called maybe profile.js so what i'm going to do is i'm going to say i want to create a new file in pages called profile.js and you can either do it you know of course via the command line or you can just go directly into your code and add this profile.js file and this is kind of where we want to work next so the first thing we want to do in profile is we want to go ahead and import our or add our imports so the first thing we're going to do is import the with authenticator and amplify sign out components from aws amplify ui react the width authenticator gives us a full authentication flow wrapping any component and then the sign out button is essentially amplify sign out is essentially just a sign out button we import the auth class from aws amplify we're going to be using this to check what the current credentials are for the sound and user and then we're going to import the use state and use effect hooks like we often do from react and then we're going to create a function called profile and this is going to hold all of our you know our ui and stuff and our logic and then finally for the default export we're going to actually pass in the profile component to the width authenticator so this will wrap this profile component with authentication so the first thing we might want to do is go ahead and create some local state for holding the user and updating the user so i might say cons user and set user is is set to to null using use state and then when the component loads what we can basically do is call and check to see if there is a user and then set that user so to do that we're going to create a function called check user and we're going to invoke that in the use effect hook check user is going to call auth.currentauthenticateduser and if this succeeds then then we will go and call set user passing in the user now if there is no user then this will fail and we will not be setting user or the user will be null essentially leaving it as is and then in the profile component we can say if there is no user we don't want we don't want to return anything first of all so we're going to say if there is no user will return null and then finally in the actual you know component itself if there is a user we're going to return a very basic profile component where we're going to show the user's username and the user's email as well as a way to sign out now in the future you might want to add additional functionality here like a way to update their attributes or add additional attributes but for now this is kind of a good way to just kind of get something going for a basic profile component we've added some styling here you know for a heading like we're using this text 3xl for most of our headings we're adding a little bit of styling around the font and margin and things like that and um and that's kind of about it so we'll go ahead and save this file go ahead and close it the next thing we want to do is we want to add some styling to our amplify ui components because by default they come as a yellow color excuse me but for our project we want to be using like a blue color as our theme so we're going to be using the blue styling that comes out of tailwind for all of our tailwind stuff and for the amplified component we're going to go into our styles and we're just going to add a root element for the amplify primary color amplify primary tint and amplify primary shade and this is going to be a global override that kind of uses any of the amplify ui components that use these colors will automatically be overridden and we're going to be using this blue color of 2 5 6 3 eb and if you want to change this to another color that's cool but this is going to basically change the buttons and things like that from our amplify ui components to be this blue color so i'm going to go ahead and save that and then finally the um the next thing we need to do is we need to have a way to have some navigation to be able to link to that profile component and kind of sign in and stuff so the way that we're going to do that is we're going to do that in app.js now the only other import that we're going to need is link from nextlink because we're going to be needing a way to link to individual posts and we're going to be wrapping them in this link component from next link and for our return let's go ahead and delete this because we're going to be writing some custom code here now here we're going to be returning two main items we're going to be returning our main component as well as our navigation so let's go ahead and return that code and take a look a quick look at it okay so basically what we have is you can kind of separate this into two areas our nav here and then our actual main component here which is going to be returning you know the main component from our app um the thing that we're doing that's a little different than we had by default we're applying some global styling that's going to be applied to every single page so we're basically saying we want to pad a padding y of of 8 and a padding x of 16 meaning we want the top and bottom padding to be 8 rem based on our configuration with tailwind and we want our left and right padding to be 16 and then any component that we render any page that we render is going to be applied to styling the next thing we're doing is we're adding some navigation at the top of our page and this is going to have a you know a list of links and these links are going to grow throughout our app as we add additional links and things like that we're going to start off with a link to the home component which are the home page which is you know at the root we're going to also be adding a link to our profile that we just created at slash profile and then we're going to be having a link for slash create post that will not work at the moment because we don't have this page created yet of course but in the future we're going to be in a moment adding the ability for users to create a new post uh we added some basic styling you know like i mentioned we have like a border bottom of one pixel and it's like this light gray color with some padding and we have some styling that's going on across all of our um you know actual nav items so with that we should be able to go ahead and test this out so i'm going to go ahead and go to my command line i'm going to say npm run dev and we should be able to go to localhost 3000 again and we should see that we have our list of posts still but we should now have a new way to link to our profile and then if we're not signed in we should be able to sign in and create a new account as well so we see our post we see that we have a little bit of styling going on now if we go to profile we should see this profile component so i'm going to go ahead and click create an account and i'll go ahead and create an account now i should be getting some multi-factor authentication so i should be getting like a confirmation code or something like that to my email address so i'll go ahead and open my email over here in a separate window and i'll paste in my confirmation code and then now that i'm signed in i should have this profile page whoops of course create post isn't going to work yet but if i go to home i got a profile i should now see my profile there so so we have that working and now that we have a way to authenticate a user we can actually add authorization to our api meaning we can start authorizing you know these requests based on the signed-in user now amplify helps you a lot in this area not only in actually setting up the backend infrastructure but also making the api calls as we're going to see in just a moment but we need to now update our api settings by running amplify update api to recognize additional authentication or authorization providers so i'm going to run amplify update api we're going to choose graphql and i want to choose here update auth settings and i'm going to go ahead and choose the authorization type continuing to be public api key for the base authorization type and i'll go ahead and kind of keep those same settings that i had before but now i'm going to add an additional auth type and i'm going to be able to choose amazon cognito user pools because that is what amplify uses for its authentication provider it's kind of a managed authentication service so we can choose that and now we've updated our graphql api to be able to use authentication now what we now need to do is we need to have a new field in our graphql schema to be able to add who the user is that created the post and we can go ahead and update that by going into our i'm sorry our amplify folder into the backend api and then next blog api has this schema.graphql where we have our base type and right now we have a type of post with an id and a title and a content field and we have this at model which is kind of a directive what amplify uses to generate crud and list operations as well as graphql subscriptions database and stuff like that but we don't have any auth going on so what we're going to do is we're going to update our model and we still have the id the title in the content field now we're adding though a username field and i'll kind of go ahead and make this a little larger the username field is a way for us just to store whoever created the the post their username this is going to be like their unique identifier you could all you could use any you know identifier that you would like so you can use a sub or you can use a username or if you want to create something custom but for our example we're going to be using the username we're also creating some auth roles and we want to have multiple authentication or authorization scenarios going on if you think of an app like twitter you think of an app like facebook or even instagram if you're not signed in you're often able to view someone else's posts if they're public but if you are the owner of that you might be able to set additional privileges or you might even be able to to restrict people from seeing it at all and to do that you need you know multiple types of author authentication and authorization and that's what we're doing here we're basically saying we want to say we want to have owner privileges for the person that created the actual post so if i created a post i can do everything i can read update delete do whatever right but if i'm not the owner i can only do a single operation and that is read and this is going to allow users that are not the owner of the post to only be able to query a single post and kind of read it or query you know all of your posts so it's going to kind of have this combination of public and private access and then the last thing we're doing is we want to have an additional data access pattern we want to be able to query these posts by the username and i want to say okay i only want to get the post that i created because right now the only query that we have is this list post query that we've been running this brings back to all the posts that means if we have uh 10 or 20 different users creating posts it's going to return all of those which is fine you know for maybe a main view but i might want to have a custom view for myself where i can only see my own posts and maybe update those so that's what we're doing here we want to have a custom access pattern where we're going to say we want a query called posts by username and then we want to be able to query on the username field passing in some type of argument so for me if my username is debit 3 i can now query for only the post created by debit 3 as the username and we'll be given a new graphql query called posts by username and i'm going to go ahead and save this schema and then now what we should be able to do is go ahead and run a new deployment so we should be able to say amplify push dash dash y and this is going to go ahead and deploy the updates to the api category um based on the two adjustments that we made one from updating our schema and then the other from actually changing the type of authorization that we're working with so what we want to do now is we're going to create a file for create or page for creating posts so i'm going to go into pages and i'm going to have a new page called createpost.js and this page is going to have quite a few imports so let's go ahead and get started with our imports we're importing the with authenticator from ui react because we want to make sure that the user is signed in before trying to create a post um even though they sh in the in a second you're gonna see that um you know we have other ways of maybe managing the nav to like only allow people to maybe see a certain nav item and we could do that as well if we'd like we're gonna be using use state from react we're using the api category again we're now also using the uuid uuid library to create a unique identifier we're using use router from next router because we want to have programmatic routing after they create a post we want to kind of route them to that post we're using the simple markdown editor which is a really simple you know markdown editor for allowing users to kind of create markdown we're using the styling for a simple markdown editor we're importing that because we're going to need that to style the editor and then we're importing the create post mutation from our graphql api i'm sorry for my graphql operations for this create post component we're going to need to be keeping up with a couple of pieces of state we want to keep up with the post title and the post content therefore i'm going to create an object called initial state and then that's going to hold that so i'm going to create a function now called create post and this is going to be our main react component and then for the default export i'm going to say i want to call the with authenticator wrapping the create post component like we did earlier to get started let's go ahead and create a couple of pieces of state or a couple of local variables and things like that so we're creating a post that is set to this initial state object with an empty title and an empty content field and a function called setpost that allows us to kind of update that post we're going to destructure the title and the content so we can use that in different areas within our component and then we're going to use a reference to router by calling use router there we're next going to have a function called onchange and onchange is going to be for when the user types into one of the input fields we go ahead and update the post with the event name and the event value so if someone types into a form we want to update the title or the content based on whatever form they're working with and then finally and this is going to update this you know our state variable and then finally we want to have a function for creating a new post the first thing we're going to do is we're going to check if the title and the content or are there and if they're not we're going to just return for some light form validation we're going to create an id and we're going to be using this id to kind of you know set the id of the post manually and that way we can actually go ahead and navigate to that that single individual view and view it after it's been created we're going to then call api.graphql passing in the query which is create post the variables which is the post variable coming off of our our local state which at this point should contain an id a title and a content field and then we're setting the auth mode by overriding the default auth mode now and if if we go to really quickly let's take a look at index.js where we call api.graphql all we're passing in is a query here we're not passing in any auth mode because the default auth mode is api key meaning that it's going to send a public request by default if we want a private request or a private api call we need to now pass in this auth mode which is kind of us saying hey this is authenticated request send along the uh the request headers that include the identity token and things like that and then that way in the actual graphql api we're going to read your username and then populate the username field for you and then after this is successful we just again navigate to the actual post now for our ui we're going to be returning um a fairly simple ui for this component i think where we have a header for saying create a new post along with some styling similar to the other styling we've been using for our header we have a form input for the title field um along with you know a bunch of styling here we have an own change handler that you know calls our own change function up here and um you know all the stuff you would think of for a input component um then we have our simple markdown editor which is a really simple component really when you look at it the only thing that we're setting is the value and the own change handler that's it the on-chain handler is going to you know keep up with the value as the user types and then the value sets the current value and then the last thing we have is a button to go ahead and create a post so on click we're going to call this create new post function which calls our graphql api and allows the user to go ahead and create a new post now to view this individual post we need to now have a new folder to be able to work with this slash post slash id that we've referenced now a couple of times because we also reference this in our index.js file so to do that we're going to be able to have a new folder called like posts and then here we're going to have essentially a dynamic route where we say id dot js and this will allow us to have a single item view for a post so to get started we'll go ahead and import our imports so we have our api category from aws amplify we have used router from next next router like we've been using earlier we import our configure amplify but i think that we don't even need that as well so i'm going to go ahead and get rid of that we also have list posts and git posts from graphql queries so we're going to be using both i'm sorry both the list post query and the git post query from our graphql queries that's because with next.js we're going to be using these different apis like git static paths and git static props git static paths is going to go ahead and create pads using each individual post that's going to be retrieved from this array of list posts and then git static props is going to create the actual props for each individual post therefore we need to kind of call a git post for every item in that array so what we're expecting in this component is we're expecting to have a post variable coming in as a prop so we're expecting that post to be right here and then if there is a post then um we're going to be basically you know there should be one right so if there is one we're going to basically be rendering each individual post metadata so we're going to have the post title the post username and we want to have the react markdown with the post content and then we're passing in an interesting class name here called pros and this has to do with that tailwind configuration we set up at the beginning and this is going to allow us to kind of say we want this markdown editor to have the um styling for a markdown editor so meaning that we want to have some special styling for headers some special styling for um you know markdown that we're gonna have within this markdown editor like um i guess code and stuff like that so um the other interesting thing going on here is we're using fallback routes so what we want to basically do is we want to be able to build this app and have let's say 10 posts being built as part of the you know the initial build but if someone creates a new post we want to be able to use the fallback functionality meaning even though it wasn't part of the original build let's go ahead and fetch that post and then update it and then render it and to do that we need to pass in a couple of lines of code to check if the route is a fallback route and if it is we don't want to return the main component we want to return like some type of loading indicator or something like that so how do we get this post data well there's a couple of things that we need to do first of all we want to have a get static paths function git static pads is going to create each individual path or each individual page essentially for an item in some type of array typically and we're going to get that data by calling api.graphql getting that array of posts this is the same api call we called in the index.js file we're going to then create a paths array where we're going to map over the items array and we're going to get a new array out of that array right we want to map over all of the items array and we want to say for every item in the in the array we need an object that looks something like this we want to have an object that has a params property with an id and because we're on this id.js the id is going to be what we kind of use in the next function getstatic props and the prop will be passed in as params.id so um with that being said we can now return this paths array and we're setting fallback to true meaning that we do want fallback mode to work and then finally we need to go ahead and create our git static paths i'm sorry get static props function and get static props is going to have this params object that we're expecting here so that's being created here passed in here and then every time for every item in this array git static paths is going to be called once passing in this params object that should look something like this so we know that we have params.id so that's what we do here we just destructure that off of the argument we then call graphql api again for the single individual post and with this single individual post we can now return that as a prop back to our component to our main component so let's go ahead and and kind of minimize that so the post that is being passed in here is getting res received here and then we're going to basically say um you know for whatever data is there it's going to be passed in here and then we can use it in our main component so the flow is kind of like get static paths then get static props and then for every item and get static props we're basically um you know creating a new post at the id route of the the item itself so it's going to be something like the root of our project slash post slash whatever the id is um and i guess starting off we should have maybe like um you know two posts right um so but we'll we're gonna see because we we might want to update some of our data to make sure that we have um the user information so we might start off from scratch so i'm gonna go ahead and save that and then the last thing we want to do is let's go ahead and update the index.js page and the main thing that we want to do is we want to add some fields for the author so i'm going to go ahead and say okay for um you know we have the title and we have that there so let's now have have an area for the author because now that we have that metadata about the um the sound and user and and that's going to be automatically added to every post we want to go ahead and render out the the username for that post as well so now when uh this loads once we've started creating posts with a user the username will also render as well now for us for this to work actually since we don't have any items created by user yet let's go ahead and delete the posts that we have in our database so um you know i kind of showed you how to do this earlier but you would run amplify console api you would click on data sources you would go to dynamodb and then we will go ahead and just have the items section chosen and delete those posts okay so that being said um what we can now do is is test this out so i'm going to go to the running app and let's go ahead and start this again okay cool so we don't have any posts but we should be able to click now create post and see that we have a way to create a new post so i'm going to say test post number one and i'm going to say like hello world we see how we have this nice markdown editor here and i can say this is code conclusion so we have some really basic stuff now when we create this post though we should be navigated to the single item view and we should be able to see that post there we go so we see that we have our post there we see the author information dab at three we have our actual post there and if i go to home i should now see that i have my post and i should be able to go ahead and click on it and view the details for that post so we're getting somewhere the next thing that i want to do though is i want to add a way for signed in users to be able to edit their own post so to get started with that we first need to have a view for those posts to begin with so um within our main pages view let's create a new page called my posts in my post this is going to be very similar to the main post view actually so i'm going to just drop in you know essentially the same code that we have in index.js where we're basically just rendering an array of posts and in just a moment though we're going to be actually um you know adding some additional fields for like editing and stuff like that but for now we just want to go ahead and query those posts now the main difference here is in the actual fetch post function instead of calling list posts we're calling the post by username query that was the that custom query that we created that we wanted to send using the the current username of the of the user now this is an authenticated request um basically meaning that we need to go ahead and um you know set some of that authentication information so we can either do that there um you know or we can kind of um you know maybe set this as an authenticated um you know view by basically wrapping this in you know the um with authenticator um or what we can do in in this example that we're going to kind of um you know add in here is that we're going to make this link overall just completely protected meaning that if you are not um you know signed in you're not even going to see the link but let's say that you are not signed in and you navigate here also this is not going to be able to work because you're going to not have a current sign in user so the auth.current authenticated user is going to fail and this this function will never even get called in the first place so that's kind of uh so we're going to kind of go with this current setup here where we're basically calling the posts by username query and the variables that we're passing into the username and um and then once the the data comes back we call setpost again you know very similar to what we did in index.js um what we need to now do though is we need to have a way to update the navigation um to only show the link for my post if a user is signed in so to do that we're going to need to do a couple of things so what i'm going to do is we're going to open app.js and we're going to go ahead and import use state and use effect from react we're going to import auth and hub from aws amplify we've already used auth but hub is new hub is a way to kind of use a an event listener for different type of events and in this case we're going to be listening for the auth event meaning that if a user signs in signs up does any type of authenticated you know operation that has to do with the authentication flow this hub is going to fire and we're going to basically have the information based on that event and that means we can now create like a function called uh in this case we're going to create a function called auth listener and we're going to basically say hub.listen and we're going to say um you know if there is an auth event we want to call of various functions and these functions are basically going to be interacting with some new local state that we're going to create called signed in user so we want to when this component loads we want to check and see if there is a sign in user and if there is we want to show and hide ui based on that so we're going to create a signed in user variable set to false this could also be you know null i guess and then we're going to create a function called set sound in user that is going to update this this user and then auth listener is basically going to be listening to that auth event and basically it's going to say if the user is signed in then we want to set that the sounding user is true and if if there is not a sound and user we want to set it to false um so for the initial call of this it's going to happen in use effect and what we might want to do is when the app initially loads we want to also check if there is a currently signed in user when the app loads and if there is we also want this to happen so we don't only want it to happen during the auth event we want it to happen on the app load so uh therefore when we call this uh function maybe we'll go ahead and pass in the empty array here um is that we also are calling auth.current authenticated user and this is basically going to fire when this component loads if there is a sign in user then we're going ahead and updating that as well so this will invoke when the component loads and then it will start listening to events that happen in the future so now that we have this signed in user variable that's going to be toggled true or false based on whether or not there is a sign-in user we can now go to our navigation and maybe say that we want some logic that looks something like this we want to say if there is a signed in user only in that case do we want to render this this navigation item for my posts and um and we should be able to go ahead and save that and um we should be able to go ahead and test this out so to test it out we'll go back to our app and you see that we have this new my post and this is only going to list the post for the sign in user so let's let's test that out right so how can we test that out i'm going to go to create a post i'm going to say post number two and really something basic because i don't really care about the content i'm really here to test out the functionality so we have two posts now what i'm going to do is i'm going to actually go into dynamodb and i'm going to find the username field and i'm going to change this to jennifer and then i'm going to go back to my posts i'm going to refresh and now i see only have one post but if i go to home i see that i have two posts one by uh me and one by someone else so we know that that that actual query is only fetching our own data um but we also might want to see like what happens if we sign out um we want this to not work so if we sign out we see that that that link went away completely and if we go to my posts as a non signed in user which you know shouldn't happen anyway but you know nothing shows up because we're not signed in meaning that you know we have to have an authenticated user for this to work so i'm going to go back to profile let's go ahead and sign back in we still have more work to do we need to give users a way to edit their own posts and also to delete their own posts so what we're going to do is we're going to go back to edit post and we're going to start um you know importing some stuff and making some changes oh you know what we don't even have this created yet so what we're going to need to do now is so like right now if we if we view a single post um you know we're going to slash post slash something but we might want to do is uh there's two ways we can handle this we could have like some type of way to say okay if the user is signed in we want to like render a different ui here but i think what makes even more sense is to have like an edit post you know actual you know page where we can say okay if they're at this page then we want to allow them to you to edit and of course only if they're uh editing their own and therefore we're going to create another you know folder in our pages directory and we're going to call this edit post and in edit posts we're going to create another like id.js now edit post is going to be very similar to create post the main difference is we're going to also have a way for people to you know um i guess it's going to be very similar actually i'm trying to think of what the main differences are not really a whole lot of differences i think the main difference is going to be in the way that we actually fetch the initial post so what we want to basically do is go ahead and first of all get our imports going so we have use effect and use state we have our api category from aws amplify you know the same stuff we've been using use router simple markdown editor we have our um git post query as well as our update post query um so actually now that i think about it one of the main differences here is that for each individual time that the component loads we're going to be fetching the single post versus in the actual static view we're viewing like a statically generated page so this is going to be more dynamic and actually that makes a lot more sense right because you shouldn't be able to edit uh something that is static so um and then you're gonna maybe wonder okay so if we do a static build and then we're allowing users to edit how do we kind of make it to where that they see an updated version well we're going to be using the revalidate functionality from next.js to kind of together to get around that i guess you would say meaning that after x number of minutes or whatever we want to go ahead and fetch a new version of the page um but anyway so for edit post one of the things that we're going to need to do is when the component loads we want to go ahead and fetch the initial initial post based on the route id so to get started though let's go ahead and create a function called edit post this is going to be our main component and we want to go ahead and create some initial variables in state and stuff like that so to get started we're going to go ahead and create a post variable and a set post function set to null using use state we're going to get a reference to the router and then we're going to get a reference to the route id meaning like slash edit post slash id we're going to be able to get that by using router.query and getting the id and then we're going to use that id to fetch the individual post so in the use effect hook we're going to create a function called fetch post and we're going to invoke it as well and we're going to say okay if there is no id you know we don't want to do anything and then we want to say api.graphql we're going to query the the post by passing in the post id and then we're going to set the individual post using that data that comes back from the graphql api next we want to basically say you know if there is no post when the component loads we'll just return null whoops the next thing we want to do is we want to have that own change handler so we want to be able to you know just like we did in create post have just a handler for updating form fields so on change is going to set the event.target.name and the event.target.value like we did before but what's going to be updated there is going to be the actual post because when we start off we don't have a post but now that we do have a post we should be having an object that has like the post id the post title and the post name and things like that and since we have that stuff we can go ahead and destructure it and start using it so we know we have a post and we're going to be needing to use the title in the content and we're going to be using that in a couple of ways the next thing we're going to do is we're going to have this update current post function and this is what's going to call our graphql api and we're going to say if there is no title and if there is no content then return and then if there is you know meaning that there is some values there we're going to allow the user to call api.graphql with update post passing in the title the content and of course the id and um once that the mutation or the update is successful then we're just going to reroute them to view all of their posts um similar similarly to when they created a post we kind of rerouted them to view that individual post and then the ui here is is actually almost exactly like the ui in creating a post we're going to give them a title or a heading field i guess for uh you know edit posts we're going to have the input for the post title and we're going to have a markdown editor for the post content and then we're going to have a button that invokes the update current post function function so um the next thing we want to do now is since we have a way to actually um you know edit the post we need a way to give someone the ability to navigate to edit their own post and since we only want people editing their own posts um it makes sense to maybe put this functionality in the my post you know component so to do that uh to start off we're going to go ahead and go ahead and create import a couple of mutations so first of all we want to import actually we only need one mutation because the edit post mutation is going to be happening on the edit post component or the edit post route so the only thing we really need to do now is to allow the user to um delete the post so we're going to import the functionality or the com the graphql query mutation for deleting a post uh the next thing we want to do is we want to update our ui so i'm going to go ahead and grab some updated ui that's going to be a little bit more verbose than this so what we're going to do is we're going to return um the same heading that we've been returning you know my posts um but the main difference here is that we're going to have a link for editing a post as well as the link for viewing the post as opposed to just clicking on the actual link itself for the entire post metadata like we were doing before we were kind of having the post and the title description and all that stuff or the the author linkable now we're just kind of having individual buttons so we're going to say to edit the post we're going to navigate you to edit post slash post id and to view the post we're going to navigate you to post slash post id and then we have a button for deleting the post now other than that it's pretty um you know pretty much the same same thing we had going on before now the next thing we want to do is we want to enable incremental static generation meaning that we want to be able to kind of update the existing page for a post after a certain amount of time because we're allowing users to edit we might want to like rehydrate essentially so what we can do now is in our slash post slash id we can now go to get static props and in addition to returning the props here we want to set a revalidate to like some number and this number is going to basically be the number of seconds that you would like to set the revalidation to happen so like 100 would be like every you know minute and a half or something 60 would be every minute um you know whatever you would like to do so let's say like you know 60 is the number we want to go with um this kind of would allow the site to be rebuilt for pages every you know x number of seconds so we should be able to go ahead and test this out so i'm going to go ahead and um let's make sure yeah we still have our whoops we still have our app building or being built being running locally so i'm go ahead and refresh and i'm gonna go to my posts and we now see that we have a way to view edit and delete posts so i'm gonna click view post test that out i'm gonna go ahead and create a new post and then for editing a post let's see how that works we should see now that we have our content field pre-populated so the edit post functionality seems to be working so let's go ahead and say new edited title and then we see that we have new edit to title and if we edit that again then we have all of that still working so um we have all of that going on so the next thing we might want to do is let's look at how to add authentic add i'm sorry add images so we want to be able to add a cover image and to do that we're going to need to work with amazon s3 so to do that we're going to add storage by running amplify add storage and this is going to allow us to kind of finalize our app by allowing users to update like a cover image for their post similar to a lot of the sites that you see out there for doing stuff like this with medium and dev.2 and stuff like that so for the type of storage we're going to say content for the name of the you know service i guess you would say for our local project configuration i might call this something like project images i'm going to keep this bucket name because this doesn't really matter to me but it does need to be globally globally unique meaning no one else can have this so i'm just going to keep that i'm going to choose auth and guest users for access meaning that i want people that are signed in or not signed in to be able to read these images for signed in users i'm going to give them full access create update read and delete and then for guest users i'm only going to give them access to read do i want to add a lambda trigger for this bucket i'll go ahead and say yes actually no i don't need a lambda trigger so i'll choose no and then we're kind of done um as far as setting up that part the next thing we want to do though is we want to set up a way to have that image metadata associated with the post so therefore we need to add a new field into our graphql schema and to do that i'm going to go to amplify slash backend slash api and back into our graphql schema here and the field that we're just going to use is called cover image and cover image is just going to allow us to associate some image in s3 with this post and then once we have that available we can then kind of fetch it and render it so i'm going to go ahead and save that and we should be able to go ahead and deploy these updates so to deploy i'm going to run amplify push dash dash y and we'll go ahead and get this deployed while that's deploying let's go ahead and create the ui for allowing users to add a cover image so to do that we're going to go ahead and open create post and we're going to start you know adding a few additional imports the first thing we're going to need to do is import use ref from react and use ref is going to allow us to kind of have a reference to another element within the dom and be able to basically make a call to click on a hidden input that we're going to be using so we can kind of use a nice styled button for working with our our ui and stuff the next thing we're going to do is we're going to import the storage category from aws amplify the storage category is going to you know be how we interact with s3 so we have our imports the next thing we want to do is we want to create some local state for working with the image and we want to create a reference to the input that we're going to be working with and using use ref so the first thing we're going to do is we're going to import i'm sorry we're going to go ahead and create a variable called image and a function called set image using use state and then we're going to create a variable called a hidden file input that is a reference to the file input that we're going to be using in just a moment the next thing we want to do is we're going to have a function called handle change and a function called upload image um whoops let's go ahead and save these here so um to upload an image we want to basically have the hidden file input that we're going to be referencing in just a moment clicked by clicking on a different button so when we click on a button that we're going to create with tailwind it's going to invoke this upload image function which is going to then click on the hidden file input that we're going to basically be using for the input but we want to make it look good so we're styling it and we're going this route and then handle change is basically going to be what we attach to the input and this is going to basically say we want the event.target.files the first item in that array which is typically you know the file that you want to upload and if there is no file uploaded we just return and if there is we call set image passing in this file that we want to upload and then once we have the image we need to now make some updates to our um you know our create post function because we need to make sure that if there is an image associated we attach that image to the post and we also um want to upload the image to s3 because we haven't uploaded at this point we've only saved it locally so right under post id we want to say okay if there is an image meaning if an image has been uploaded we want to go ahead and create a a new file name that is going to be unique because when the user uploads an image it's going to have some file name but it might be something basic we want to make sure that everything in our s3 bucket is unique so to do that we're going to say kant's file name is equal to the image name with an underscore unique identifier this gives us like a really unique image name and then we set the post cover image as the file name and then we upload the image to s3 so we're basically saying okay if there is an image let's give it a new file name we're going to associate the cover image with the post and we're going to upload that and then that's about it everything else should continue working the only thing we now need to do is go ahead and create the button for uploading the image so i would say right before we create our our button for creating the post let's go ahead and add a couple of lines of code so we're going to first create the actual hidden file input and we're getting giving this that ref of hidden file input that we you know created earlier in our react component we're giving a class name of absolute with a height 0 and a width of zero meaning that we just want to completely hide this thing and um and instead what we're going to be rendering is this button and the button is going to call upload image which is going to invoke the on change handler of input which is going to you know allow user to upload an image so with that being said we should be able to go ahead and maybe test this out so let's go ahead and save this and see if we're allowed to upload an image or if we've broken anything so i'm going to go ahead and go to create post oops let me go ahead and restart the server alright so we see we have this new button that says upload cover image so i'm going to say post with image oh you know what one thing we have not added actually is we haven't added the preview because uh if we do want the image to show up as we're you know testing it out we might want to add an image preview so let's go back and do that before we continue here and underneath the i guess input for the for the title let's do something like this where we basically say okay if there is an image we want to go ahead and return you know an image component with the image source and we're basically saying url dot create object url passing in the image which is kind of the way that you would want to um render the image in your ui so save that update this one more time and try or refresh one more time so we should be able to click upload cover image and we'll click an image like this and now we see the image and um let's go ahead and click create post and um it looks like it's working but we're not seeing the image in our actual view so the last thing we want to do is we want to actually render the post in the item view so to do that we're going to go ahead and go to pages slash post slash id.js and we might go ahead and create some local state and to do that let's go ahead and import use state and use effect from react and in the um the loading of the component we're going to call use state using null passing in you know null for setting a cover image variable and a set cover image function and what we want to do is have a function that is invoked you know and use effect and basically what we want to do is we want to say you know if there is a post cover image we want to get it and set it um you know and if there's not we don't really do anything so if there is a cover image we're going to get the actual image url that's going to be available for us to render an actual image from s3 calling storage.get passing in the post cover image and this will give us like a temporarily signed url that we can use in our app because you can't just publicly grab an image from s3 it's too it's you know there's some security rules around that so we'll get the image key and we'll call set cover image and that will turn this from null into something meaning we can now maybe toggle within our ui if there is a cover image and if there is we want to maybe show it so what we can now say is if there is a cover image we want to also render that in our ui so what i'm going to do is i'm going to go to [Music] below post title and i might say okay if there's a cover image let's go ahead and show this cover image with some really basic styling so i'm going to save that and refresh and it looks like storage needs to be imported as well so i might not be importing storage so let's go ahead and import storage from aws amplify and it looks like it's working so i'm refreshing though so um so we see that we have our post image there so if i go now to all the posts and i click on post with image i see the image and if i go to a post without an image i don't see an image so we've gotten quite far the next things that i would say that we might want to do is we want to give users the ability to update a cover image and we also might want to render the image thumbnail here in this list view now i have included both of those things in the workshop and all you have to do is follow the next couple of steps and you'll be able to do that but you might want to try to do this on your own if you want to kind of like you know ingrain this knowledge a little bit better into your head like look through the code that we've written and say okay how would i implement this maybe try implementing it yourself and if you have any trouble you can then go back and look at you know the example now finally we want to actually deploy this to aws now there's a couple of ways you can do that but right now um i would typically recommend the amplify hosting you know amplify hosting service but we're currently working on ssr support which should be released you know within a couple of months of this video but right now if you deploy to amplify you can only deploy a static site so what we're going to be using in this example for now is the serverless framework and it's pretty simple to actually deploy using the serverless framework what i basically want to do is i want to create a file called serverless.yaml and the root of the project and i can go ahead and open up this serverless.yaml file and i want to paste in some basic configuration for a deployment to aws using something called the serverless next.js component and here all you have to do is just add these two lines of configuration we give the app name and we give the component that we'd like to use and this is essentially kind of like a higher level abstraction within the serverless framework we're saying we want to use the serverlessnext serverless component and we're using the version of 18.0 and then we just save this file and we can deploy and if you even want to add like a custom domain you can do that as well and i would i would recommend checking out the documentation it's pretty simple to do that in fact i'm going to create a video on how to do that but to deploy to aws now we can just say npx serverless and this will take a few seconds and then it should be deployed and we should be able to test test it out all right once everything's been deployed we should be given a domain for us to check it out so an app url so i'll go ahead and copy that to my clipboard and we'll go ahead and load it up whoops i copied the wrong thing i guess let's try that again all right so and once you start you know using the site you should notice that it's actually noticeably faster once it's been deployed that's because the build has been run you could also run npm run build and npm start and notice that it's you know pretty pretty fast so um everything seems to be working our posts with images working should be able to go ahead and sign in using the same credentials as before and everything seems to be working properly on our live domain so um that's it you know that was a fairly more long video than usual for me but i hope you learned a lot i did have a couple of stumbling blocks along the way but i hope that you still enjoyed everything and i hope that you kind of learned something and take take this and maybe build some interesting stuff so if you like this video and if you like any of my videos please subscribe to my channel and maybe share it with people that you know um if you have any you know ideas around uh future videos leave a comment and let me know and i really appreciate your time and thank you for watching this and we'll see you next time
Info
Channel: Nader Dabit
Views: 9,502
Rating: 4.9793816 out of 5
Keywords: tailwind, next.js, nextjs, aws amplify, serverless, aws, amazon cognito, jamstack
Id: 13nYLmjZ0Ys
Channel Id: undefined
Length: 76min 34sec (4594 seconds)
Published: Thu Jan 21 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.