Next.js Crash Course - SSG, SSR, API Routes, and more

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi welcome to this course my name is dmitro and in this course i'm going to cover the main concepts behind next js first i will talk about what nextges is and its primary features then i'm going to explain these concepts while building a simple example app we will set up a next js project and add typescript then we will set up and run a local mysql server for the app using docker compose next i will talk about routing and styling in next.js then we will set up a database connection in the app and build an api route for getting a list of posts using next.js api routes feature then we will create a few more api routes next i will show how to render pages using various methods such as static site generation server-side rendering and client-side rendering then we will add a form for creating posts finally i will briefly talk about next.js config and how to add metadata to pages so this is the plan let's get started next js is a react framework for building web applications it's very popular at the moment and it's used in tens of thousands of websites and web applications it provides essential features that are required by modern web applications such as static site generation server side rendering flexible file system based routing built-in feature for building api endpoints automatic code splitting built-in css support and much much more so let's have a look at the big picture of how a next js application works next js includes two parts client-side and server-side client-side is responsible for rendering pages in a browser and other front-end functionality like client-side routing and on the server side next js allows us to pre-render pages and this includes fetching the data required to render a page a basic user flow would look something like this a user visits a page and next js prepares html for that page and in the process it can fetch some data from some backend api this can be a headless cms like stripey contentful prismic or a custom api or even next js built-in api routes feature so next js gets the data prepares.html and it renders it for the user on the client side as we saw earlier next.js works on both the client side and the server side when the user requests a page for the first time next.js handles these requests on the server and responds with the initial html on the client side next.js makes this html interactive and from here next.js can handle page transitions in the browser so we can switch between pages without reloading the whole browser tab next js includes functionality for building backend api the built-in api roots feature basically you can build both the front end and the back end for an app using next js where next js really shines is the front end it allows you to pre-render your pages both statically in other words using static site generation and dynamically using server-side rendering and you can decide whether to use static site generation or server-side rendering for each page static site generation is when next.js prerenders a page just once at build time and serves the pre-rendered html to the users so a developer runs the next's build script and next js generates html for a page at build time here it also fetches the necessary data then a developer deploys the app and the users when they request a statically generated page next js serves the html it's prepared up front at the build time so it doesn't have to server side render anything also there is a really cool feature called incremental static generation this is the nexus generates html for a page at runtime based on some condition and reuses this html on each subsequent request now let's talk a little about server side rendering this is when next.js fetches data and generates a page dynamically on the server side every time a user visits a page and along with both static site generation and server-side rendering you can client-side render a part of a page it's when a rendered page is served to a browser it can still fetch some data and render it moreover you can statically generate the whole app and serve it from a cdn but because in this scenario you won't run next.js on a node.js server you won't be able to use features that require node.js for example incremental static generation and some other features will not be available so in this short course i will explain different concepts and features of next.js while building a simple app on the main page we will list blog posts from a database for the database we'll use mysql and when the user clicks on a post title we will fetch and render that post also we will add a form for creating posts so let's get started and learn more about next js let's go ahead and set up a next js project to get started let's open the docs getting started and there are few ways to set up a project we can use a script or set it up manually before next js released this setup script i used to set up my projects manually so if you are completely new to next.js i would recommend doing the manual setup once to get familiar with the basic structure of the app in this video i will use the script i will use the yarn version you should run this script in a folder where you would like to store your projects folder here we should enter the project name let's call it my next app this script creates a folder for the project using the project's name inside of this folder it initializes the basic file structure and installs the dependencies inside of this folder you can run the following commands yarndev to start the development server yarn build to build the app for production and yarn start to run the app you have built using the build command in production mode now let's go ahead and open the app in vs code i will open the terminal and run the app in development mode this starts the development server on localhost port 3000 we can open it in the browser at the moment our app has only one page the home page this page is located in the pages folder in the file index.js now let's have a look at what the installation script prepared for us let's open package.json scroll to dependencies the script installed next framework react and react dom we've got the pages directory this is where we store our pages the home page is in index.js and to add another page we simply create a file in this folder we can also create subfolders now let's create about page a page file should export by default a page component this should be a react component let's render the title and visit this page we can visit a page by typing the file name here without the extension like this and here it is okay there are a few more interesting things in the pages folder this is the api folder next js allows us to create a backend for our app it has a feature called api routes and we can store these api routes in the api folder for now we have one api route called hello it exports a function by default this function takes the request and response objects inside of this function we can do something like read from the database and send a response in this case it's json let's call this api route we can call it by typing here in the past api hello the name of the file and here it is another interesting thing is this file underscore app.js next js wraps all pages in a component called app and it allows us to override this app component and in order to override it we create a file underscore app.js and create the app component ourselves this component has been overridden initially to import global stylesheet on each page we've got a public directory next.js can serve static files from this directory and we can reference these files in our pages using relative paths like for example to access febikon ico in index.js we type the path like this basically we type the file name relative to the public folder another interesting folder we've got is called dot next this folder is created by next.js during the build step dot next is the folder where next.js stores the files it compiles during the build process usually you wouldn't want to look inside of this folder and this folder together with node modules shouldn't reach the repository let's have a look we've got some warning here let's check it out next informs us that anonymous arrow functions cause faster fresh to not preserve local component state and it asks us to name our page function so let's name it about.js let's move it out here let's call it about and export it by default okay this is fixed also the install script initializes git for us and creates the first commit okay we have set up a basic next js app now let's go ahead and setup typescript next js supports typescript almost out of the box so to start using typescript with next.js we need to do just a few simple steps the first one is to search for typescript in the docs okay to enable typescript we should add the tsconfig.json to our project we should leave it empty next.js will fill it in when we restart the dev server so next chest did not fill it in so let's check why and it says the following we need to add typescript and the types for react and node so let's do it okay let's run the dev server again and our ts config is complete i recommend to enable the strict option enabling this option we make sure that we follow best practices in our typescript code so the code is more typesafe also next.js added a file called nextnfts it references various next.js typings basically this file tells the typescript compiler to include these typings while compiling our code this line includes the default type definitions from the next package and this line includes the type definitions that are stored in the types global dts file in the next package we can even click into it to check it out here it is so this is the type definitions that are stored in next so it's node modules next types global dts i'll close it so basically this file makes sure that the compiler knows about the next environment while compiling our code that uses next api the final step is to change the extensions of our js files so let's change them react components should have the extension dsx this extension informs the editor and the compiler that this file contains jsx syntax same for the about component app component is not a component so it should have dot ts extension since it doesn't have any jsx okay i'll save the file let's see there is an error let's try to restart the dev server okay looks like the app is working now let's do one more thing related to setting up typescript now let's tell the editor to use for type checking the version of typescript be installed in the project instead of using its own version for this let's enable the status bar i will open the settings go to json mode and here status bar visible i'll set it to true and open some typescript file and now i can click on the typeship version that vs code uses and choose select typescript version and use workspace version this creates a folder.vs code with the settings that vs code should apply to current workspace finally let's fix the types in app component let's describe this props using using app props let's import them okay we should also describe this route handler here let's describe the rec object i think the type is next api request okay and response is next api response okay now let's check if the app still works the route okay it works now let's visit the index page and it works as well now i would like to talk about routing in more detail routes are defined by the file structure in the pages folder every file in the pages folder that is outside of the api folder and excluding the components we override is considered to be a page a page file should export a react component the path to the page file starting from the pages folder is mapped to the route which serves it index here is a special name that is mapped to the base of the route for example let's create a subfolder here let's call it products and create a new and index page in here let's call it products and export it by default okay now we can visit this page using the route slash products like this okay almost in any web app we need to render something based on some url parameter and the next js allows us to define such parameters using file names as well in next.js routes with dynamic parameters are called dynamic routes let's say we've got some products and we would like to show them by category and i would like to view that category by visiting the following path products category to accomplish this i will create a file in the products folder and it will have a special name which includes the name of the parameter in square brackets let's call the parameter category and dot tsx this tells next to store the category value in the category parameter we will be able to access the category value using this parameter okay let's create the page and render the category in the title to get the parameter here we should use next router and to get the next router in the page component we should use the use router hook so let's go ahead and import this hook it's exported from next slash router module now let's get the router okay finally we can get the parameter to get the parameter we should read it as a property of the query object from the router and the name is the parameter we used here okay let's export the page by default okay let's check it out so i say products slash category or let's call it sum category and here it is okay we have learned how to create pages now because we are building a modern web application we need to allow the user to navigate between these pages without the reloading effect we usually see when we click a usual a tag on the home page let's create a link to the products page in a simple html website i would do it like this okay let's remove the code that has been added by the installer all right let's go to the home page this is our link i'll click on it you might have noticed that the transition was not smooth let's check it out again i click on the link and you should look here you see it's loading the browser requests the whole page from the server every time what i i'd like to accomplish is a client-side transition this means that the next js will fetch from the server only the parts that are necessary to render the page it will not request all the html and javascript required to render the whole app initially and therefore the transition between pages will look smooth so to enable the client side transitions we should use the next js link component instead of a simple a tag let's go ahead and import the link component we should wrap the a tag in this component and move the href prop to the link here okay let's check it out i'll click on products and the transition is smooth now we've got one dynamic page to create a link to a page that uses a dynamic url parameter we should add one more prop to the link component to demonstrate this i will create a link to the products category page okay let's turn this into a list i'll copy and paste this link here rename it to products let's just name it some product category and this should link to we might think something like this some product category if you specify the path like this the transition won't work because next doesn't know that we are trying to get the page with a dynamic url parameter so to create a link to a dynamic page properly we should in href prop specify the original path including the name of the parameter like this and then specify the actual path using the as parameter and here we can pass the category name okay next js provides another way to interact with the router it's the router object we use this object already in the category page here so to get the router we should use the user outer hook we can use this router object to get various data related to the current route like for example the current path and for navigating the user for example if the user signs in we could redirect them to a dashboard page using the method called router.push for example here i can i can create a button and on click i will redirect the user to the home page using the router api like this so push and the pass now let's say homepage here let's open the okay if i reload the page oh i should visit the category page okay some product category here is the homepage link if i click on it i'll get to the home page together with pages we can create api routes each file in the folder pages api becomes an api endpoint that is mapped to a url pass that starts with the api here ok i will open one endpoint and to handle a request we should export by default the function which takes two arguments the request and response objects next also supports dynamic parameters in the api routes basically to generate api routes you should follow the same file naming rules that are used for pages next js is very flexible as to the options it provides for styling our application out of the box next.js includes the css and js library called style.jsx it allows us to write both global css and the css that is scoped to a particular component so first let me show you an example of using style.jsx let's write some css that will be scoped to the home page component for this we will use the style deck with jsx prop enabled okay somewhere in the container let's add the style jsx here inside of this deck we can write css as usual for example let's style the main tag here this one let's change its background to green during the build time style.jsx processes the css inside of this tag and scopes the selectors to the current component let's check how it works ok the main element is green let's inspect it as we can see here styled.jsx generated a class name that is specific to the homepage component and this class name is added to the element that matches our selector from the style jsx tag this selector here main should be green and style jsx appended this specific class to our selector to make it so this css is applied only to the main tag inside of the home page component so during the build time style.jsx processes the css inside of this style jsx tag and scopes the selectors to the current component we can write global css using style.jsx as well for this we should add another prop here to the style tag and this prop is called global the css inside of the global tag won't be scoped to any particular component and will be applied globally so in this example all main elements will have the background green here if i reload the page here this is our global selector now the home page's class name was not appended to our selector style.jsx is not the only option for styling an xjs application nexjs has a built-in support for global css imports css modules and sas let's have a look at global css imports the installation script generated some global css for us and stored it in the styles folder here it is and this global css file is imported in the app component next js wraps every page in this component so the styles we import here will be included on each page we can import global css like this only in the custom map component we cannot import global css files in pages or other components to style other components we can use css modules for example let's style the menu that we have on the home page for now first i will move this menu out to its own component okay let's create the components folder this is a common practice to store other components that are not pages in the components folder let's create the menu component here let's use a function component also in next js apps we can remove react imports from our component files because next.js uses a babel plugin that will auto import react for us okay i will paste the menu here let's import the link let's render the menu back on the home page okay now i will create a css module to style the menu next js requires us to include the module suffix in the css modules names this is a convention that nexgs uses to recognize css module imports okay let's import this css module let's add some css i prepared some css up front so i will just copy and paste it okay let's add the menu class to the menu container this ul tag okay let's check how it looks the menu became horizontal also let's remove the green background in next.js almost out of the box we can use sas both in global css and in css modules to use sas we should install the package called sas so let's just go ahead and do it yarn add sas to use sas we should change the extension of the css imports from css to scss or sas i'm used to scss so i'll change it here to scss okay here as well let's check if sas was enabled let's add a variable here border and apply the border here to the menu container i should start the app and here is our border let's add some padding as well okay finally let's add a layout component to share common layout styles between pages okay i'll create layout component here okay let's wrap the child components into a div typescript doesn't recognize the children prop for it to recognize it we should describe layout as react component we'll use a function component type let's add css ok and apply the container class to the layout div okay let's wrap our pages in this component let's start from the home page we will wrap the whole page here okay we don't need this we don't need them the other styles that were there initially let's remove them okay let's remove the footer and move the menu to the layout component okay let's wrap children into main tag now okay i'll go back to the index page and add some title okay let's have a look something wrong can't resolve this module okay i used wrong extension it should be scss in the import okay here it is now let's apply the layout component to other pages okay and the product category too let's import it okay okay let's have a look how it works i'll visit products same layout product category same layout as well perfect let's go back to home page wonderful i discussed styled jsx global css imports and css modules because these options are built in you can use other css setups as well like for example tailwind or styled components but they will require additional configuration before working on the front end i would like to get the api ready so we have some endpoints to read and update the data however to be able to create the api i need to set up a database and for our example i have chosen mysql because it's well-known database and the basic sql language is easy to understand so let's go ahead and configure the database first of all i would like to create a schema for this i'd like to prepare an sql script let's store it in the folder called db i'll call the file schema sql first let's create the database let's call it blog let's tell mysql to use this database for the commands that follow let's create a table for posts a post has an id integer unsigned let's auto increment it title work r255 not null not null means the following when we add a row to this table we won't be able to set title to null in case of the title we will have to set it to a string a post has content it's text not now and let's set the primary key to id ok our app will have a single table finally let's insert some test data i will just copy the insert code here this will insert the test post to our table now we should set up a mysql server for development i would like to use docker to host a local server in particular the docker compose tool to be able to create and run docker containers on your computer you need to install docker desktop this is what i use to install please download the relevant package to your operating system and follow the instructions inside of the installer if you would like to learn more what docker is you can either visit their documentation or look for a good tutorial or video about docker docker compose allows us to define and manage our docker containers using a yaml file the file is called docker compose yamu let's create it in the root of our project i prepared the code up front as well i will copy it here okay here i tell docker compose to create a mysql container i called it mysql in the scope of this file you can use any other name for this container like database for example the container should be created from the official mysql image here we tell mysql server to use for default authentication plugin mysql native password from version 8 mysql started using a new authentication plugin but many libraries that connect to mysql do not support this new plugin so here we say that we would like to use an old plugin for authentication then we have this option if this container stops for some reason like an error cures this tells docker to restart it however if we stop the container manually it won't be restarted then we define various environment variables and in particular mysql root password and the credentials we would like to use to connect and queries the database mysql server will create the development user with development password and it will also create database blog for us further with the ports option we say the following we would like to forward local port 3306 to the port 3306 in the container so we can access this database server from our localhost port 3306 to create this file i use the information from the documentation of the official mysql docker image on docker hub here okay here this is the docs here we have an example of a docker compose file i used it as a starting point now let's go ahead and start our database container to start the container let's run docker compose up okay the thing here is that docker compose occupied the terminal so let's run our container in the background for this i will use docker compose up d detached okay let's check the name of the container for this we can use docker compose ps this command lists all the containers started by docker compose and this is our database container's name now that we started our local database server let's import the schema for this i will use docker exec command to run mysql command to import the schema inside of the container let's enable the i option interactive this allows us to stream the data from our schema file to the command that we run in the container then we should specify the name of the container okay and the command we will run the command using sh c then run mysql with user root root password let's use the n variable we created earlier here next we should specify the database where we would like to import our data okay and then stream the contents of our schema file this ran our sql script successfully there were no errors only a warning using a password on the command line interface can be insecure and this is because we used the password option here in the command string but we used an end variable here instead of an actual password so i would say it's pretty secure because we don't expose the actual password now in order to connect and query the database from our api endpoints we should install and use a package called serverlessmysql later on i plan to deploy this example using versal and versal deploys the api endpoints from an xjs project to serverless functions so serverless mysql wraps the mysql client package providing connection management for the serverless environment so let's go ahead and add serverless mysql package now let's create a connection and test it let's open the pages api hello ts and import the serverless mysql here now let's set up an object that we will use to interact with the database here we should set the database credentials i will store the credentials in environment variables and to access environment variables in node we will use process n and we need mysql host then the database the user also i have noticed that typescript doesn't recognize these properties on the end object so let's describe these properties to typescript for this we need to do declaration merging basically we need to merge the declarations of these properties into the n object for this let's check how and object is declared here is the n object's type definition it's defined using the processing interface let's click into it as well and check where it belongs and it belongs to the global node.js namespace so let's use declaration merging to declare our custom and variables on the process and interface for this let's create our custom type definitions file let's call it custom.d.ts and in this file let's declare node.js namespace as well and our own process nf interface and inside of this interface we can declare our custom and variables let's copy them from docker compose okay all of them are strings now let's go back to our api route handler and declare the last credential variable and it's the password and now typescript gives us a hint of what properties are available and let's choose password okay now let's fetch the posts from the database and serve them to the client in order to use evade in our handler let's make the handler asynchronous okay let's select all posts now we should call db end it's asynchronous as well and return the posts here as json okay we have set up a database connection here by reading the credentials from the environment variables but the thing is that we need to import these environment variables into the app somehow and next.js has a mechanism to do this and to set the environment variables we can use various.n files for example we can create a dot m file to store only the environment variables for the local development or we can create an n-file to store the variables for production etc because i plan to deploy this project to versal i will configure the n variables for production using versus api so let's use the next js feature for storing n variables in the file system only for local development and for this we will use the dot amp dot local file here i see that while practicing the tutorial i already created this file here it is i have set the local development environment variables here and i added one more variable and this is the api host url that we will use on the client side to query the api you might have noticed that i added a prefix here called next public this tells next.js that it is safe to use this n variable in the client-side code because by default next.js doesn't allow us to use and variables in client-side code for security reasons so we don't expose our secrets okay let's check if this works okay let's visit the api route and here's our post we were able to fetch it from the database now let's extract the database connection code and store it in a separate module to be able to reuse it let's create a folder called the lib it is a common practice to store common code in lib folder in next.js projects let's call the database connection file db i'll close node modules and export db by default okay let's import it in the route handler it's two folders up lib db let's try it and it still works now what should we do if we stop working on this project first of all we should stop the next server and since we started our database container we should stop it as well and for this we can run docker compose stop this stops our local mysql server container now that you stopped the container you can run it again by typing docker compose up d you can also remove your container completely using docker compose down command this removes the container and the network created using docker compose also it's important to note that using docker compose down removes the data that you store in this container as well so you will lose your mysql database data so if you don't want to remove the data you can just stop the container and resume it later we created an api route that returns a list of posts now let's go ahead and create a few more routes one for creating posts and another one for getting a post by id i will use the same path for creating and listing posts in next.js a route handler function is called for all http methods it will be called not only if you request a route using http get method but also post put delete and others so when the user sends a get request we should respond with a list of all posts but when the user sends a post request we should create a post to check the requests method we can use reg.method so if it is a get request we return a list of posts otherwise if it is a post request let's create the post first we need to accept the input from the user for the input we will accept a json object with title and content of the post in the handler function we can access this object using the react.body property typescript doesn't know about the structure of our input object so we need to describe it to typescript our input object has title and content now let's create the post we are using a prepared query here to escape the input so we list the values using question marks which will be replaced with the title and the content from the input object escaped so the mysql package will escape this input before merging it into disk query now let's call the serverlessmysqlcleanup function this function does connection management related to the serverless environment finally let's respond with the created post let's respond with 200 let's get the id of the post we have just inserted it's available as the insert id property of the result object the bigquery returns result as unknown so let's describe this result object ourselves i researched the result object already and i learned that the type of this object is ok packet from the mysql package so let's import mysql here we didn't install this package directly in our project but it is installed as part of the surplus mysql and now from mysql we can use the following type here as a generic type parameter to the query function the type is called ok packet and now typescript knows about the structure of the result object and it knows it has the insert id property now let's return the other properties okay now let's test it using curl let's specify the content type we are sending a json object so it's application json then the type of the request is post then we should pass the data and the api url failed to connect to localhost port 3000 oh it's because i didn't launch the server so let me copy my command okay launch the server okay and run the command in another terminal window okay internal server error let's check it here it is table blog post doesn't exist okay i know what happened i dropped my mysql server container using the docker compose down command and then i started a new container using docker compose app and did not import the schema so i'll do it i have this command in memory it's docker exec here it is all right now let's run the curl command oops i stopped the dev server so i'll start it again then let's go back to another terminal window and run the curl command again and this is the response we've created another post which got an id too now let's create an endpoint for getting a post by id to get a post we should request the following endpoint posts slash post id so let's create a route for it so create a folder posts it's a dynamic route here we should capture the id from the url okay the handler structure is similar to posts handler so i'll just copy it and paste it here i will remove unnecessary parts okay i still need the db but now it's three folders up okay we don't need mysql anymore so let's go ahead and get that post we can get the id from the rec query object now let's query the post that has this id again let's use a prepared query to avoid sql injections and call the cleanup function so now this query will return an array of posts and since we queried by id and the id is unique this array will have only one post if it is found so we need to get that post from this array first let's check if the pulse array has something again tag strip doesn't know what posts is because the query method returns unknown so let's describe the return array to typescript it returns the following title content and id and it's an array of these objects okay so if a post was found then we should return it otherwise let's return a 404 okay let's try it let's get the balls post is id1 this is our first post and here it is finally our ultra simple api is ready so let's start working on the front end and list all the available posts on the home page now i would like to show a few options for rendering a page by default next.js prerenders pages using static generation next.js generates the html for the page during build time and reuses this html on each request our home page is a simple react component let's remove this unnecessary import okay there is no additional data fetching functionality so next js pre-renders this page during the build process by default however frequently we need to fetch some data to render a page therefore next js provides a mechanism for fetching data during static generation and to fetch data during static generation we can use the get static props function in this function we fetch the data for the page and next.js passes this data further to the page component through props so let's render the latest posts on the home page and use this function to fetch them at build time we should export this function together with the page component we should add the data we want to pass to the props object let's try fetching posts using our api endpoint this one i'll use fetch fetch returns a response object let's extract the data from this object okay because tabscript can not infer anything about the data fetched from the api here rest.json returns any we should describe this data here to describe this data let's create an interface to describe a post okay and use this interface here to say that restricts on in this case returns an array of posts finally let's return the posts we fetched in the props object now let's get the posts from props and render them again typescript doesn't know about the props of the homepage let's describe them next.js provides a type that allows us to infer the type of the posts sorry the type of the props returned by the getstaticprops function and this type is called infergetstaticprops type this one it takes the type of the get static props function as a generic type parameter okay let's list the posts okay let's have a look at the app cannot read property map of undefined i'll try to reload the page okay it works this is our list this list shows oldest posts first but because we say that we list latest posts here let's change the order to show the latest posts first i'll open the route handler here and add order by clause saying to order by id in descending order and the latest posts show up first at the moment this page is pre-rendered using static generation let's make a little experiment to see that this page is statically rendered let's render a timestamp on this page if we refresh this page the timestamp shouldn't change so let's go ahead and add the timestamp okay homepage let's add timestamp here okay let's check it on the client side we will see the latest version of the timestamp if we reload the page but in the html code that is served by the next server the timestamp shouldn't change so let's view the sources here timestamp here it is let's reload the page and we can notice that the timestamp changes so we can't really notice the result of the static generation because we are in development mode we are using the next js dev server which rebuilds the page every time we reload it anyway so let's have a look at what happens if we try to build our app for production i'll stop the app run yarn build this calls next build and we've got an error here it is request to our api endpoint failed and this happens because at build time our api endpoints are not started to fix this error we should fetch the posts directly from the database in our getstaticprops function instead of querying an api you might think that it's not a good idea to mix back-end code with client-side code it seems really insecure however from the next js perspective it should be okay if you use the server side code in this function because next.js removes this function and the imports that are used exclusively by this function from the client side bundle this is mentioned in the next.js documentation here if i open the docs scroll a little down and here we've got a note you can import modules in top level scope for use in get static props imports used in get static props will not be bundled for the client side as explained below let's click on this link and here we've got more explanation note that get static props runs only on the server side it will never be run on the client side and it won't be included in js bundle for the browser and this means that you can write code such as direct database queries without them being sent to browsers it also says that you should not fetch an api route from get static props and we should write the server-side code directly in this function also there is a tool that you can use to check what nexjs eliminates from the client-side bundle so let's go ahead and query the database from this function first let's open the posts endpoint actually let's move it into the posts folder to make our code more organized and rename posts to index so this route will be available at the same path but now we store the code related to posts in the same folder so in the post handler let's move the code for fetching posts into a function so we can share it between this api handler and the get static props function so i'll copy it and move it to the db.ts file okay get all posts here we fetch the posts let's make this function asynchronous okay let's return the posts all right let's use this function in the api handler here okay and now we can use this function in the home page here in the get static props function instead of a fetch request ok there is an error in this file this one okay typescript doesn't recognize the posts prop and this is because it did not describe the posts array that is returned from get all posts let's open this function definition here typescript cannot infer on its own the type of the data returned by dbquery function so we have to specify it ourselves and there is a generic type parameter for this so it's an area of posts let's create the post type first we already have it on the home page here it is i will move it from here to the db file and use it here to describe the array of posts okay now this function returns an array of posts using a promise alright okay no errors here anymore let's try to build the app again and there is another error let's check it out here it is error serializing posts zero returned from get static props so javascript fails to severalize this posts array and in particular the objects that are inside of this array it means that there is some property in these objects that is not serializable that cannot be turned into a string so let's make sure that the object returned from get all posts are are serializable okay here where we return the posts array we got directly from the bigquery let's map over the posts and pick only the serializable properties and these are the only properties we need okay let's build the app again and it has been built successfully wonderful now let's start this app in production mode to check our little experiment okay it starts on the same port here 3000 let's reload it let's look for our timestamp here it is let's reload the page to see if this timestamp remains the same and it remains the same every time i refresh the page so this page is pre-rendered using static site generation now let's talk about another option for pre-rendering pages it's called server-side rendering this is when next.js generates html for a page on each request server-side rendering is useful when you need to render a page on the server side and the data that you render on that page is updated frequently for the sake of example let's create another version of the home page which will be server-side rendered i will copy the original one and rename it to home ssr let's call this function home too switching from ssg to ssr is quite simple instead of using get static props to fetch data we should use get server-side props so let's rename this function to get server side props now to infer the props in the component here we should use another helper type it's in first server site props now and type of get server-side props okay this is it okay let's check the timestamp to see if this page is rendered on the server side let's build the app again and start it in production mode okay oh i should open the home ssr page here it is this is the timestamp let's reload it and here every time i reload the page timestamp changes so it's rendered on the server for each request finally let's try client-side rendering with this approach we will serve initial html without data we will start fetching the data once the initial html is displayed to the user basically we will fetch and render the data on the client side i will copy the home page again this time i will call it home csr let's rename this function to home 3 and remove this get server side props function we won't get posts from props anymore in the component let's remove these imports okay so now in this component we will use the use effect hook to fetch the posts from the api we need to store the posts somewhere and let's store them in the local state let's quickly describe the type of the data that will be stored using this hook right now typescript thinks it's undefined so let's describe it as an array of posts for this let's move the post type we created earlier here in db to a common types file i'll create it in the root project folder okay let's import it in db okay and use it on our new homepage here use state takes a generic type parameter this is how we describe the state value type so it's an array of post now let's call the use effect hook to register our callback here our callback should be called only once after initial render of the home page so let's add an empty dependency list so dependencies won't change from render to render here let's create a sync function to fetch the posts here let's call fetch this time i would like to use an environment variable to specify the api url we have added this environment variable earlier in the end local file here it is next public api url appending next public to an environment variable makes it available to use in the client side code so i'll copy and paste it actually let's add it to the type definition of the end object as we did with these variables earlier here it is okay and we need to call the posts api endpoint now let's get the data from the response okay let's tell typescript how this data looks it's an array of posts and let's call setposts with this data okay i'll call fetch posts here so this will fetch the posts after initial render and store them in the state now let's render the posts from the state here posts can be undefined and when they are undefined this means that they haven't been loaded yet so let's check if posts not defined render a loader otherwise render the posts in a list okay let's try this i will rebuild the app again okay let's start in production mode okay let's change this to csr this is our timestamp this page is statically generated let's check this by reloading it the timestamp shouldn't change and it doesn't change also we don't fetch the posts on the server now so we see this loading text now let's run this page to see if it works correctly okay here it is this is our posts this time we successfully fetched the posts and rendered them on the client i have used fetch in this example but i would like to mention that the team behind nexjs created a react hook specifically for fetching data on the client side it's called swr it provides a lot of useful functionality for data fetching such as caching revalidation re-fetching an interval and more it is distributed as a separate package please check it out now i'd like to create a page for viewing a post by id let's serve this page from the pass post slash post id for this i will create a dynamic route i'll create a folder called post and inside of this folder a file with the name id in square brackets this is the name of the parameter that i would like to use to access the id let's create a simple page component we can render this page using one of the three approaches that i have discussed earlier i would like to use static site generation compared to prerendering blog posts on the home page we have a dynamic url parameter here and if we decide to pre-render this page using static generation we have to tell next.js which variations of this parameter it should pre-render basically in this example we need to tell next.js which posts are there in the database so it can pre-render them so let's start by exporting the static props function to fetch a post by id this time we need to get the id parameter and we can get it from the context object that next.js passes as an argument here to get the type definition of context objects we can describe the whole function using a type called get static props here so the context object has the following parameters params preview and preview data we can read the url parameters using the params object so let's extract the id parameter params can be undefined then id if we hover over the id parameter we can see that it can be either string an arrow of strings or undefined so let's make sure it is a string okay now we should fetch a post using this id from the database for this let's extract the code that we used to fetch a post in the id api route here into a function okay this is the code okay instead of describing the type on the fly let's use the existing post type let's get id from the argument so this query returns an array so let's turn this array into a single post also let's make sure that this object is serializable let's extract only the properties that are serializable from it okay let's use this function in the api route here okay id is string or strings but our function requires a number so let's turn id into a number and check if the post is there we return it otherwise return 404 ok let's use this function on our single post page as well here let's turn this value into a number as well and return the post here now let's render this post let's extract the props type and render the post okay let's check here what okay so typescript did not infer the post correctly and it's because we used the get static props function type to describe our function and this type takes props as generic type parameter here and if you don't specify the props using this type parameter the props become this object or index signature where each prop is any so let's describe our props okay now we've got an error because here we require the post to be defined but in here the post can be undefined so before returning this post let's make sure it is defined and since this function runs on the server at build time let's just throw an error here if the post is undefined post with this id was not found okay now let's finish rendering the post all right now let's move to the home page and add links to the posts list so we can view the posts we use the next link component okay this link links to a dynamic route so we have to first specify the original route path in href like this and we should specify the actual path using the as parameter ok let's run the app and check it out visit the original home page here is the list with links i'll click on a link and there is an error get static path is required for dynamic ssg pages and is missing for our page as i mentioned earlier next.js doesn't know anything about our posts it can't take the posts on its own and pre-render a route for each post we need to tell it which posts are there in the database so it can render them and we can do this using the get static path function as long as we are using static site generation to prerender a page so let's go ahead and add this function to tell next.js which posts it should be rendered here our single post page we can use get static path type to describe it it's in a sync function here let's read the posts from the database and return an array of path objects that we would like next chess to pre-render using get static props so for each post we return a path object where we specify the parameters object and we've got only one parameter the id and we should also set the fallback option to false let's check what the error is property id is incompatible with index signature type number is not assignable to type string so next js expects a string for the properties here and we pass it a number so let's turn this number into a string okay now next chest knows which path it should be rendered and if i visit the page again i should see the post also we can check what nextjs pre-rendered for our statically generated page by looking at the output of the next build command okay and here so for post slash id it pre-rendered two parts post two and post one also let me quickly explain the fallback option we use this option to tell next.js what to do when the user requests the pass that hasn't been pre-rendered for example we've got two posts post one and post two and what happens if the user requests post slash three if we set this option to false next.js will show the 404 page if we set the fallback option to true next.js will try to prerender in production the paths that were not pre-rendered during the build process so it will try to pre-render our pass post slash 3 in the run time and if it pre-renders it successfully it will store it in cash and reuse it for subsequent requests now let's go ahead and create a form for creating posts for the form i will create a page in the top level i'll call it create post i will copy and paste the initial html here okay let's import layout okay this is a very simple form it has two inputs one for the title and one for the content to simplify handling this form i would like to use react hook form package so i'll go ahead and add it to use reactbook forms api i'll import the use form hook from it now let's describe the type of the values in the form then use the use form hook to get the react hook forms api we will use the register function to keep track of the values of the inputs and the handle submit function to assist us with handling the form submission for typescript to know about the values we are handling using this form let's describe the values using a generic type parameter of the use form hook here okay now let's connect the inputs okay and add the form submit handler here we pass our own handler which should be asynchronous since we will call the api here as an argument it takes the values object let's use the values to create the post i will use fetch to call the api let's specify the api url since it will be a post request we should add some additional options the method we should set the content type header to application json because we will send the json string and finally set the body to the values of the forum stringified okay now on successful submission of the form let's redirect the user to the home page for this let's use next router and one more thing let's add a link to this page on the homepage below the list of posts let's use next link component okay now let's run the app and check this out okay we are on the home page let's click create a post fill in title let's call it hello world add some content and hit submit and the post hasn't been created something went wrong let me check it out okay yeah i see i entered the wrong url here it should be posts i don't know why i had it public here probably i was looking at this word here okay so let's try it out again create a post some post some content and hit submit and this is our post now let's go ahead and add some metadata for the pages i'll open the home page and to add metadata to a page we can use the next head component it's imported from the next head module here we can add the page title various links like the fav icon page description open graph tags and other metadata basically whatever we put inside of the head component is added to the head section of our document now let's go ahead and change the title of this page and here it is the contents inside of the head are cleared when a page is unmounted like for example if you visit another page like creating a post here the title of the previous page is not there anymore so the contents of the previous pages head section are replaced with the contents of the new page's head now i will just go ahead and add a title for each page okay we've got the home page then create post page and the post page here here we will use the post title and let's visit the home page and let's open some post and the title changes next chess works well out of the box however there are times when you need to modify the next server behavior or the build process for some need of your app that is not supported out of the box next.js allows you to configure some of its internals using a configuration file let's search for it in the docs here it is it's called nextconfig.js you should store this file in the project's root directory from this file you can either export a config object like here or a function that will return the config object a function takes two arguments the face and some options like the default config the phase is the current context in which the configuration is loaded at this moment there are four phases available the export phase this is when we use the static export option the production build phase when we build the app for production production server phase i guess it's when we run the server in production mode and development server phase when we run the development server so in this file you can do various customizations like for example customizing a webpack config or defining some environment variables that you would like to use in your app and there are many other settings please have a look at this documentation for more details so we have reached the end of the course i'm glad you watched it and hope you enjoyed it and learned something new if you liked this course please hit the like button and or subscribe to my channel i will leave the link to the code in the description also i'm planning to create another video about deploying this project in the near future so see you next time have a great day
Info
Channel: Dmytro Danylov
Views: 9,152
Rating: undefined out of 5
Keywords: next.js, react, ssr, static site generation
Id: q-sYloF3xKM
Channel Id: undefined
Length: 96min 6sec (5766 seconds)
Published: Thu Aug 27 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.