Build a Full Stack Movie Streaming App in Go – Golang, React, MongoDB, OpenAI API Project Course

freeCodeCamp.org 11.2M subscribers

Join

Subscribe

1.7K

Share

Ask

38,053 views 30 Sept 2025 Lean how to build a complete full-stack movie streaming app with AI-powered movie recommendations. You’ll use Go with the Gin-Gonic framework on the backend, React on the frontend, and MongoDB for data storage. For the AI features, you’ll connect your Go backend to OpenAI using LangChainGo.

Sign up for MongoDB Atlas: https://www.mongodb.com/

✏️ Course developed by ‪@GavinLon‬

💻 Code: https://github.com/GavinLonDigital/Ma

🏗️ MongoDB provided a grant to make this course possible.

⭐️ Contents ⭐️ 0:00:00 Introduction and Overview of the Course 0:10:14 Setup Development Environment 0:10:19 Install MongoDB and Compass (GUI for MongoDB) 0:14:05 Install MongoDB Shell 0:17:54 Launch Compass, Create and Seed MagicStream MongoDB Database 0:23:38 Use MongoDB Shell to Interact with MongoDB Database 0:29:42 Install Go (Golang) on Dev Machine 0:34:31 Create Basic Go Application using Command Line Interface and Notepad 0:38:45 Install Visual Studio Code on Dev Machine 0:42:42 Create Full-stack Project Infrastructure in VSCode 0:44:38 Create go.mod file for Go/Gin-Gonic Web API 0:47:32 Install Go Extension for Visual Studio Code 0:51:00 Start Creation of Code for Go/Gin-Gonic Web API 0:51:26 Install Gin-Gonic (Gin) Web Framework 1:06:33 Create Movie Model and GetMovies End Point 1:31:01 Create Movie Controller 1:33:01 Create GetMovies Gin-Gonic Endpoint Handler Function 1:44:20 Create Reusable MongoDB Connection Code in database Package 2:16:46 Create GetMovie Endpoint Function Handler 2:35:58 Create AddMovie Endpoint Function Handler 3:02:34 Install Postman and use Postman to test Post Request to AddMovie Endpoint 3:14:41 Create User Model 3:44:00 Create RegisterUser Endpoint Handler Function 4:20:25 Create Login Functionality 5:29:52 Create Middleware Functionality to Validate Incoming Access Tokens 5:50:23 Configure Protected and Unprotected Gin (gin-gonic) Routes 6:06:49 Update Movie Review - Engage with openAI through LangChainGo 6:34:22 Sign up for Account with openAI 6:59:32 Create Movie Recommendation Service Powered by AI 8:13:57 Go/Gin-Gonic Best Practices - Minor Code Amendments 8:54:51 Create Frontend Code Using React 9:02:57 Create UI for Home Page 9:59:05 Create Header Component 10:24:43 Setup Routes using react-router-dom 10:24:58 Create User Registration UI 10:56:59 Create Login UI 11:10:30 Create Auth Provider and Auth Context Functionality 11:39:31 Create UI for Movie Recommendation Service 12:05:37 Create UI for Updating Admin Movie Reviews 12:38:29 Amend Code so that Access Token is Stored in http-only cookies (prevent XSS attacks) 13:41:01 Using react-player to Play Movies in Browser 14:04:27 Deploy full stack Web App to the Cloud 14:08:13 Deploy MongoDB to Atlas and Seed Data from Local MongoDB 14:18:42 Deploy Go/Gin-Gonic Web API to Render 14:35:31 Deploy React Client Code to Vercel 14:44:48 Outro

Transcript

Learn how to build a complete full stack movie streaming app with AI powered movie recommendations. You’ll use Go with the Jin Ganic framework on the back end, React on the front end, and MongoDB for data storage. For the AI features, you’ll connect your Go backend to OpenAI using Langchain Go. Along the way, you’ll set up authentication with secure token handling, making sure access tokens are stored in HTTPON cookies to protect against cross-sight scripting attacks. You’ll also learn best practices for securing client server communication over HTTPS. Once the app is built locally, you’ll deploy it to the cloud with MongoDB Atlas, render for the Go API, and Verscell for the React client. By the end, you’ll gain experience not just in Go, MongoDB, and React, but also in cyber security, AI integration, and cloud deployment. Gavin lawn developed this course and MongoDB provided a grant to make this course possible. Hi everyone and welcome. I’m Gavin Lawn. I’m super excited to bring you this course where we’ll build a full stack web application stepby step using cuttingedge technologies. We are developing this application for a fictional company named Magic Stream that provides Why’d you do it? I didn’t. A movie streaming service as well as an AI powered personalized movie recommendation service that leverages a technology called lang chain go for interfacing from our go or golang code with open AI. We are going to use React on the front end to leverage high performance front-end user interaction functionality so as to provide our users with a superb UX user experience. We’ll create the serverside code for a web API component using Go also known as Golang. Go or Golang is a statically typed compiled programming language created by Google. It was designed with simplicity, efficiency, and scalability in mind, making it well suited for modern systems programming, cloudnative applications, and large-scale distributed systems. I have to say, after working with Go lately, it has become one of my favorite programming languages. You’ll see it’s just great to work with. It’s a great modern, simple, yet powerful language to learn, and it also pays really well. For our data storage facility, we’ll use the awesome MongoDB, a NoSQL document-oriented database designed to store, query, and manage large volumes of data in a flexible and scalable way. MongoDB is optimized for fast read and write operations, especially useful for real-time applications. As mentioned earlier, for creating a super responsive front end, we’ll use React in order to provide our users with an excellent UX user experience. React is a JavaScript library, not a full framework, used for building user interfaces, especially single page applications, SPARS, where the UI needs to update efficiently in response to user actions or data changes. It was originally developed by Facebook and is now open source and widely adopted. So this course has a bit of everything. Learn a modern cuttingedge programming language like Go. Create a full stack distributed web application. Learn important aspects of cyber security. Learn how to integrate AI into your applications. And then at the end of the course when we deploy our app to the cloud, we engage in aspects of DevOps. Once we’ve developed our application, we’ll deploy the serverside web API component written in Go to Render, a modern cloud hosting platform that provides developers with a way to easily deploy and scale web applications. We’ll deploy the React client code to Versel, a cloud platform optimized for front-end development. And we’ll deploy our MongoDB database to Atlas. Atlas is MongoDB’s fully managed cloudnative database service, sometimes described as database as a service. So this web application is a great example of the implementation of a loosely coupled architecture where functional entities on the web, the serverside component written in Go and the client written using React interact via HTTP as one full stack web application. We’ll also include cyber security features like token authentication over HTTPS to protect our data. So, as mentioned earlier, I’m Gavin Lon. I’ve been developing software professionally for decades at this point and love sharing my knowledge. I run my own YouTube channel where I teach programming and discuss technology in general. I’m proud to say that I’ve created several courses for free code camp, most of them dedicated to teaching programming. But I must confess that I went to the dark side last year and taught a course on video editing using Da Vinci Resolve. So please check out that course if you want to add video editing to your skill set. Okay, so enough about me. Let’s discuss what this course is all about. I’ve created a fictional company called Magic Stream. We are going to develop a movie streaming service for Magic Stream in the form of a full stack web application that provides a special feature that uses AI to recommend the top five movies tailored to the user’s specific tastes. This recommendation service is based on the users’s movie genre preferences and the sentiment derived from movie reviews created by the administrators of our system. So the workflow here is an administrator logs onto the system and creates a movie review in natural language for a movie offered for streaming through magic stream. When the administrator submits the movie review, a custom prompt that we’ll create is submitted along with the movie review to OpenAI via a technology called Langchain Go. The relevant LLM large language model returns a response in one word describing the sentiment behind the movie review. Our prompt includes instructions to only return one word describing the sentiment behind the movie review. For example, the sentiment could be excellent, good, okay, bad, or terrible. This sentiment described in one word is then saved to our database along with a unique numeric value associated with each sentiment. You can see how it is easy for our system to include a value for each of these sentiments. So for example, excellent can have a value of one, good, two, okay, three, and so on. The last one, terrible, has an associated value of five. We can then query our MongoDB database for movies and order them by their values that denote the sentiment for each of the movies stored in our database. It is important to note that these sentiments are extensible. We are going to architect the application so that we can extend the number of possible sentiments associated with movie reviews. We could later include a wide spectrum of sentiments and associated values. For example, you could include the word unwatchable below terrible where terrible has an associated value of five and unwatchable has an associated value of six. So these values can be used as a ranking order for the relevant recommended movies. This course has pretty much got everything. I mean we are also going to include cyber security features like tokenbased authentication over HTTPS. I’ll firstly show you how the access token is generated once the user authenticates with the system i.e by logging on with the user’s credentials. Then for subsequent HTTP requests the token is passed through the header of the relevant HTTP messages so that the user can be authenticated before accessing protected endpoints. But it doesn’t stop there. By passing the token through the header, our application is still vulnerable to XSS attacks, cross-sight scripting attacks, where malicious JavaScript code can potentially be injected into the client side and read the access token. An access token is like a key that can be used to unlock protected resources. So, we must protect the key. How can we do that? I’ll show you how you can avoid storing the tokens on the client side and passing it through the header by instead storing the access token as HTTP only cookies which means the tokens cannot be read through JavaScript code and therefore mitigates the risk of the access token being stolen. I’ll show you how to do that in detail in this course. So these are very important cyber security related practices to learn to leverage Go for our web API component. We are going to use a web framework called Genonic. Genonic sometimes referred to as Jyn is a high performance HTTP web framework for go Golang. Gingonic is lightweight and fast, minimalistic but powerful. Jinggonic. I know it sounds a bit like jin and tonic. We’re not going to be vibe coding in this course. Oh, sorry. Whiskers. I feel it’s important to learn to code at least at the beginning without the use of AI assistance. So it gives you a tactile and deep sense of the development process and building up the code step by step without AI assistance in my view is the best way to understand programming and associated technologies. So we are going to build this full stack web application step by step and we are going to have a great time doing it. So as I said at the beginning this course has a bit of everything. Learn a modern cutting edge programming language like Go. Create a full stack distributed web application. Learn important aspects of cyber security. Learn how to integrate AI into your applications. And then at the end of the course when we deploy our app to the cloud, we dabble with DevOps. Dabble with DevOps. We engage with aspects of DevOps. So we are going to have a great time developing and deploying our full stack web application. Right, let’s start from the beginning which involves setting up our local development machines with the technologies we’ll use to develop and test the application. Let’s get into it. Right. So to install MongoDB, we need to navigate to this URL. So let’s go through Chrome. I’m going to use Chrome as my browser and I’m going to navigate to this URL. www.mongodb.com/try/d download/ community then press the enter key and you can see the website has appropriately detected my platform settings. So we have the platform setting for Windows x64 which is the platform I’m using uh which I’ll be using to develop the application for this course. You have a choice between MSI and Zip here, but we want the MSI package. And this is the appropriate platform for us. But we have a list of various other platforms. We have Mac platform and we have various distributions of Linux available to us here. But as stated, we want the Windows x64 platform selected here. So the next thing we do is just hit the download button to download the relevant MSI file. Once it’s downloaded, we’ll double click on the relevant MSI file and run the relevant installation package. You can see we’ve got an indication as to how much longer it will take to download the full MSI package. So, we just need to be a little bit patient here. 2 minutes left. It says not too long. Let’s fast forward. And please note that this is the community edition of MongoDB server and it’s absolutely free to use on our local machines. All right, not long now. 9 seconds, 8 seconds, 7 seconds, 6 5 4 3 2 1. Okay, great. And now we should have the folder icon displayed to us. Here it is. And we can just navigate to our downloads folder using that there. So to install MongoDB on our local machines, we simply doubleclick this file here which has themsi extension. Excellent. The setup wizard will install MongoDB 8.0.11 blah blah blah blah blah. Click next button. Excellent. And then let’s accept the licensing agreement by selecting this checkbox here. And then let’s press the next button. And we are recommended to do a complete install here. So let’s hit the complete button. And on this page, I think we can just leave everything as is. So let’s press the next button to continue. Now this is a very important checkbox here. Let’s keep this checked because this will install automatically a guey interface for MongoDB. So we’ll be able to use this guey interface to manipulate our data, create databases, collections, documents within collections, etc. and it’s a really great graphical user interface that we can utilize. Great. So, let’s press the next button here. And then the last step is to press the install button. I’m not going to press the install button because I’ve already installed uh MongoDB on my local machine. So, I’m just going to cancel out of this. But please go ahead and press the install button and go through with the installation process. So, the next step is we want to verify that we have indeed installed MongoDB successfully on our local machine. So to do that we can do it through the command prompt like this. So d- version like this. Excellent. So we’ve got version 8.0.8 which is the target version that we installed on our Windows platform. Excellent. Then in order to be able to interact with MongoDB with the MongoDB database on your local machines, you can run MongoDB commands like for example query commands from your command line using the MongoDB shell. This can be handy for test purposes. Please note that the installation of the MongoDB shell requires a separate installation to the one for installing MongoDB, i.e. the installation we have just completed and verified. So to install MongoDB shell, it’s www.mongodb.com slashtry slashd download and then the word shell slshell like this. Press the enter key. And you can see now that the actual website itself has detected my particular platform, Windows x64. So, I’ve got Windows x64 version 10 plus. So, I’m actually on version 11 of the Windows 6 x64 operating system, which is correct. And then here, we can download the relevant MSI file. And then we can just run that MSI file from our downloads folder by double clicking on the relevant file and going through the relevant installation instructions. Then to verify that MongoDB shell has actually been installed and I have actually gone through the installation process. So I’ve got MongoDB shell installed. You can verify this by typing MongoDB. So sorry it’s not MongoDB, it’s Mongsh version like this 2.5.0. So, I’ve got the appropriate version of MongoDB shell installed on this local machine, and we verified it with this command. Excellent. Uh, sometimes it won’t automatically configure your path environment variable, which can lead to problems. So to verify that our path environment variable has indeed been configured correctly, we can go to this utility cis dm dot cpl. Press the enter key. Then we can go to this tab, the advanced tab, and press the environment variables button here. And let’s look inside the system variables list here and navigate to the path environment variable. Hit the edit button and let’s see if we can find the MongoDB environment variable that we want. Sorry, well the path environment variable and make sure that MongoDB is appropriately set up within the path environment variables. And here it is. C program files MongoDB server 8.0 bin. And if we copy that to our clipboards and we press Windows key R here and use Windows Explorer, we can navigate to that path. And we can see here that the relevant files have been appropriately put into that directory structure and we are appropriately pointing to that directory from within the path environment variable. So that’s excellent. Great. And then the other verification we should do is check that MongoDB is actually running in the background. And we can do that by running our services. So Windows keyr type services.msc like this and then type select the list and type mo. And there we are. We can see our MongoDB server is running in the background here. And we can use this facility to stop or start start our server. But we don’t want to stop our server. We want to keep MongoDB server running in the background. The next thing we want to do is launch Compass. So we can see the graphical user interface that ships with this installation of MongoDB. And then let’s press on the connect button here next to main connection here. Excellent. As you can see, I’ve been playing around with a number of MongoDB databases, and this is essentially the structure we’re about to create. So, we’re going to create a new database next to the main connection node here. Let’s press the plus button, and then let’s give our database a sensible name. So, let’s call this magic stream movies like this. And our first collection will be named movies. So let’s enter it here. Collection name. And then we just press the create database button to create our database. Excellent. So I want to create three other collections within this database. So let’s hit this plus icon here to create a new collection. And let’s name this collection users. This will store information regarding the users of the system. I’m going to import three data for three users initially so that we can uh test our application as we develop it using that data initially and then we’ll also create a registration facility where we can add further users but we’ll get to importing the relevant seed data in just a bit. So let’s create a new collection. Let’s call this one genres like this. Let’s hit the create collection button here. Excellent. And we’ve got one last collection that we need to create. So, let’s hit the plus button here. And this collection’s name is rankings. And we’ll see the data for the rankings collection in just a bit. But let’s first import the data for movies. Now to import the seed data you can firstly download the relevant data from my GitHub repository. So let’s go to my GitHub repository here. And if we go to Magic Stream and this folder, Magic Stream seed data, you can see a number of JSON files here that I recommend you download to your local machine and then you can import the relevant data into your collection through a utility within Compass. And I’ll show you how to do that right now. So, if we go back to Compass, let’s import the data for our movies collection. And to do that, we hit the add data button here. Import JSON or CSV file. Let’s select that option and then doubleclick movies.json here and then hit the import button. And you can see I’ve prepared offcreen 15 data for 15 movies and we’ve just imported them into our movies collection here. You got Jack Reacher, Blitz, Star Trek, The Undiscovered Country, Star Wars, Empire Strikes Back. What a classic. And we’ve got a few classic movies here. The Good, the Bad, and the Ugly. Classic from the ’ 60s. Unforgiven. Awesome movie. Great. And then I was talking about the rankings. Now, we’re going to create a facility that interacts with AI with OpenAI and it’s going to extract sentiment from movie reviews written in natural language. You can see here within each document, each movie has a movie review. Um, so for example, this movie is awful. The AI will extract the sentiment just in one word, terrible, and the ranking value makes that sentiment quantifiable. So, we’re taking a movie review written in natural language and we’re quantifying its sentiment. And you’ll see how useful that can be later on when we develop that part of the application relevant to the AI functionality. Excellent. And so, let’s import the data for the rankings collection. And it’s the same procedure. We go import JSON like this. And double click on the rankings.json file and hit the import button here. And there we have it. And you can see the AI will select from the admin review written in natural language one of these options, excellent, good, okay, bad or terrible. And in doing so, the AI is taking qualitative data written in natural language and turning it essentially into quant a quantifiable value which is represented by each of these sentiments. So, we’ve got excellent, good, okay, bad, and terrible to choose from here. Right, let’s import the genres. And it’s the same procedure. You click this dropown, go import JSON or CSV file, and then double click on genres.json and click the import button here. Excellent. And lastly, we have the users. Let’s import the data for the users collection like this. Let’s hit the import button. And we’ve now imported three users, one of which is an administrator. So Bob Jones is the only one because he’s part of the admin role. He’s the only one who can actually add reviews to movies. Okay. So the next thing I’d like to demonstrate is how we can use the command prompt to interact with our MongoDB database. Before we test the MongoDB shell, I just want to clarify that this is just one way of interacting with MongoDB. In fact, in this course, we are going to use a specific MongoDB for Go driver from our Go or Golang code in order to interact with our MongoDB database. Using the MongoDB shell can be a convenient way of interacting with our MongoDB database. For example, during an appropriate testing phase of your application or perhaps to diagnose potential database related issues during the development phase of your applications. As stated in this course, we are going to interact mostly with our MongoDB database in code via the MongoDB for Go driver. We’ll look at this in detail a little bit later. So let’s fire up the command prompt like this and then we need to go into the MongoDB mode if you like and we can do that with this command mongod. Press the enter key and as you can see we are automatically in a database named test here. So it actually created this database by default when we went through the installation process. So let’s create let’s run a command that creates a collection within the test database. And we can do that with this command db.create create collection and let’s name our collection customers like this and press the enter key. Okay, one. So that looks good. It should have created our customers collection and we can verify that by going into compass here. Let’s go into our test database customers and there it is. There’s our collection and let’s create let’s first clear the screen and let’s create a document within our customers collection and we can do that with this command dbc customers doinsert one and then we can include the relevant JSON object like this and we’re going to add a customer named Bob Jones. So let’s give this field the name name like this. And then let’s include the field value like this within quotation marks. Bob Jones. And the email address email is Bob Joneshotmail.com. Let’s press the enter key. Excellent. So, we’ve added a document to our customer’s collection. The document is denoted by the customer, Bob Jones. And let’s verify that Bob Jones has been added to the customer’s collection. And there’s the document for Bob Jones. And you can see it’s added a unique identifier for this particular document. And let’s for good measure, let’s add another customer to our customers collection. This time, let’s add a customer called Sally James. Let’s give Sally a sensible email address. Hotmail. Whoops. Should be at atotmail.com. And let’s press the enter key. Excellent. And let’s verify that Sally James has been added to the customer’s collection. And there it is, Sally James. Brilliant. And then if we want to display, if we want to query for all of the documents within our customers collection, we can run this command. DB.Customers customers.find open close brackets press the enter key and there we have the two documents that we’ve just added to the customers collection displayed to us. Brilliant. Let’s clear the screen um and let’s actually go into the the uh and let’s actually go into our magic stream movies database. So to do that we type use magic stream movies like this. So we’ve switched to Magic Stream movies and then we want to return all the movies that are saved to our movies collection. So to do that we type db dot moviesov dotfind open and close brackets. Press the enter key. And there we have our 15 movies. Brilliant. Excellent. And let’s say we wanted to just return one of those movies. Let’s clear the screen first. We can do that with this command. DB dot movies oops movies dotfind one and then each movie has a field named IMDb ID. So you probably know this but on the internet each movie has a unique identifier. It’s IMDb number. So I’m using that within our database to identify them to uniquely identify our movies. So let’s say we wanted to find the movie called Once Upon a Time in Hollywood. So we can do that through its IMDb number or IMDb ID. And to do that in code, we pass in a JSON object to the find one method like this. So this field is called imb_id. So we don’t need the quotations here, but the field is called IMDb_ID colon. And then we want to include the value for the IMDb ID field. TT 713 6 22. And then let’s press the enter key. Excellent. Once upon a time in Hollywood. The document for Once Upon a Time in Hollywood is displayed to us. And our command has executed. successfully. Great. Right. So to install Go on our local machines, let’s navigate to this URL. So it’s https colon slash slashgo.dev. It’s an easy URL to remember. Excellent. And then let’s press the get started button here. And then let’s press the download button here. And you can see here we have a number of installations to choose from. And it all depends on what platform you are running. I’m running a Windows platform. So Windows 10 or later. This will be compatible with Windows 10 or later. I’m on Windows 11. So, I’m going to click on this link here to download the relevant MSI package. And then we’ve of course been through this before. We got our indicator here and it’s downloading that pretty quickly. So, we don’t have to fast forward anything. And we’re on we’ve only got say a few seconds left to go. That’s looking good. Oh, a bit of a hiccup. Right, let’s fast forward. And here we go. A few more seconds. Six. Four. Three. Two. No. It’s another Okay. 4 seconds. 6 seconds. 9 seconds. 8 seconds. [Music] Guess my internet’s not particularly good today, but we are getting there. Nearly there. Excellent. Done. So now all we need to do is click on the folder icon here to go to our downloads folder. And here’s our MSI package. And then to install go simply double click on the MSI package and follow the instructions provided to us in the wizard. Please wait while setup wizard prepares to guide you blah blah blah. So the next button isn’t yet enabled. So we can’t click on that just yet. So let’s just be patient here. Migrating feature states from related applications. It’s doing things behind the scenes. Welcome to the Go programming language AMD 64 Go 1.24.4 setup wizard. The setup wizard will install Go programming language AMD 64 Go 1.24.4 on your computer. Click next to continue or cancel to exit the setup wizard. Let’s press next and install Go. So, what’s happening there is it’s detected that I’ve already got Go on my machine. So, I’m actually going to exit, but please go through the instructions, go through the installation process to install Go. Right. So, I’m just going to go no exit here and then finish. And just going to minimize this. Right. And then just to verify that I’ve got Go installed successfully on my local machine, I’m going to go into the command line here and type Go version. Excellent. So 1.24.2 Windows AMD 64. That’s excellent. So I’ve got Go successfully installed on my local machine. Um just like we did when we installed MongoDB, we should verify that Go is appropriately configured within our path environment variable. So to do that, we do the same procedure. We type CIS DM CPL here and we go to advanced and to get to our environment variables, we hit the environment variables button here. So within our system variables, let’s find the path environment variable settings here. Press edit and let’s see if we can find go within the paths configured here. And here we go. Program files go.bin. And we can actually copy that to our clipboards. Go Windows keyr and navigate to that directory. And we can see we got go.exe exe there go FMT.exe. So we’ve got the relevant executables in our Go directory and that has been successfully configured with for within our path environment variable. So that’s great. Okay. So let’s go out of this here and the next thing we can do is let’s go to the command line. I’m actually going to navigate to the C drive because we have configured go correctly within our path environment variable. We should be able to run our Go commands from any directory. So, we’re in the C drive here and let’s create our first Go application. So, to do that, I’m first going to create a folder. So, I’m just going to go mk dur and create a folder called go first app like this. Press the enter key and then I’m going to navigate into that folder that we’ve just created. Go first app. Excellent. And then I’m going to launch notepad because for this first application, we’re just going to use Notepad. It’s going to be a very simple traditional Hello World application. So, we don’t need a particularly sophisticated editor for that. But when we get down to creating the web API that we’re going to create with Go and Jenonic, the Genonic web framework, we’ll use Visual Studio code for that. But for now, let’s just use Notepad and create a basic hello world application. So, let’s initialize our Go application with this command. go mod init my app like that. Press the enter key and then let’s launch notepad. So we can just type notepad. If you’ve got notepad appropriately, it’s set up. You should be able to just type notepad to launch notepad. And let’s save this file as main.go. Go. It’s going to go to my C directory. Go first app. There it is. And then I’m going to create a file called main.go here. Set that to all files so that it doesn’t mess with our extension here. So main.go is going to be where we write our code. And let’s save that like that. So firstly we want to designate this go file as part of a package called main. So to do that we type package main like that. Press the enter key. Then we want to import a particular package that we can use to write out text to the screen and we can do that with this package. So I’m just going to type import and it’s the package we need is called fmt. Let’s press the enter key here. So every go application has a main entry point, a method called main and that’s the entry point of the application. So let’s create the function for this using go. So we type funk and then the name main like this. Open close brackets and then we can open and close curly brackets. And this demarcates where our go code will be where we’re going to write some text to the screen functionality that writes text to screen. So we’re going to use that fmt package functionality and then the method is print ln and then we’re going to write uh whoops hello go world like this. Let’s give it an exclamation mark here. Let’s save that. And that’s our code. That’s our first Go application written. And let’s see if we can run it. Right. So, I’ve cleared the screen and let’s see if we can compile our Go application. So, to do that, we go we type go build like this. Great. And let’s look in that directory and see if it’s created an executable for us. And it’s created the executable called my app.exe. So to run that, we can just type my app like that. Hello Go world. And we’ve written our first Go application. Brilliant. Right. So as mentioned earlier, we’re going to be using on both the client and service side, we’re going to be using Visual Studio Code to develop our application. So we of course want to firstly make sure that we have Visual Studio Code installed on our machine. So let’s type in https/ slash slashcode.visisualstudio.com/d download so that we can download Visual Studio if we don’t already have Visual Studio on our machines. And then it’s just a case of going through the install instructions as we’ve done before. So you want to download the installer by clicking if you’re on a for example on a Windows 10 or 11 platform, you want to download this installer here. You want to click this option here. If you’re on a Mac platform, you want to click this option here. And of course, this is for Linux. So let’s click on the Windows option here. And we’re downloading it. Excellent. Going very well. Here we’ve got our indicator. And once it’s downloaded, of course, all we do need to do is uh double click on the relevant file and go through the installation instructions provided to us through a wizard. And that’s it. We’ve got the folder icon presented to us. And then we’ve got an EXE file here which is our installer executable. And we can just double click on this and install. I accept the agreement. Next. I would actually encourage you to select this check box here to click on this checkbox here so that the icon is readily available on your desktop once the installation process completes. Then let’s press the next button here. And then you press the install. I’ve already got Visual Studio installed, so I’m not going to go through this process, but please go through and install Visual Studio Code if you haven’t yet installed Visual Studio Code. So, the next thing I want to do is create a folder on my local machine. So, cdev and Golang. And you can see I’ve done a number of incarnations of the magic stream application in preparation for this course. But we’re going to create a fresh new folder where we’ll create our application. And I’m going to call this folder magic stream movies. Like that. Press the enter key. Excellent. So we’ve got our designated folder that will house the files for our application. So we’re going to now fire up Visual Studio Code and firstly create the infrastructure for our projects. So we got a React project on the front end, the client, and we’ve got a Go which leverages Jenonic which is a web framework on the server. Great. So let’s open the folder we’ve just created, the fresh new folder. So, open folder and we want magic movie. No, we don’t want that one. We want magic stream movies. And let’s select that folder. If you already had Visual Studio Code installed on your machine, you might not necessarily have the latest version. And you can check for updates with through this menu option here. Check for updates. Okay, there are currently no updates available. So, I’ve got the latest version. So, I’m good to go. But if you haven’t yet got the latest version, just hit the check for updates menu option here. Excellent. In this part of the course, we’re going to focus on the server side code, but just bear in mind, we’re going to then once we finish the server side code, we’re going to come back and develop the client React code. Excellent. So, let’s create a folder for the client React code. And we’ll call this folder aptly client. And we’ll get back to this a little bit later. So let’s progress to the server side code. So let’s create a folder called server which will house our server go code. So let’s create a folder called server. And then within this folder, let’s create a subfolder called magic stream movies server like that. Okay, excellent. Please note that if you get stuck at any point or you just want to reference the final code for the application, please check out the final code at this URL. There may be a few minor differences between, for example, naming conventions for the files when comparing the final code to what we’ve discussed in the video. But the differences are very minor. So please don’t be put off by these minor differences. Okay. So now let’s invoke the terminal window. You can do that by control back tick like this. By default, uh it has selected the root directory of our solution which will contain both client and serverside code. So we’re in the magic stream movies folder here. And we’re going to be using PowerShell here. Okay. So within this directory here, we want to create a module which will store all the references to the dependencies that we will be using in our Go project. So to do that we first need to be in the root folder of our serverside code which is magic stream movies server and then we can type the command to create our module our go.mod file. So to do that we type go mod in it. So we’re initializing our project and this uh file that we’re creating will store all the the references to the dependencies that we will be leveraging for our for the server side part of our application which will be written in go. Okay. So go mod init and then the name of our module this is a best practice will actually be where our serverside code will be stored on GitHub. So, we’ll be uploading this serverside code to a designated location on GitHub. And this is where our serverside code will reside when we eventually push it to GitHub. So, github.com/gavanlon [Music] digital. So, this is my path. So, obviously you need to upload it to your particular GitHub path. So you you’ll want to include where your code will be stored on GitHub. So, github.com/gavlon digital and then magic stream movies and then server and then magic stream movies server like that. Right. So, Gavinon, so github.com gavlon digital magic stream movies server magic stream movies server. That’s where my serverside code will reside. And as I said, this is just a best practice. What this will mean is that for example other users will be able to import your packages by using the go get command and then typing the relevant GitHub path to get your particular packages to import those packages into their applications. So this is why it’s a best practice. It futureproofs your application. Great. So obviously I haven’t yet uploaded anything to this path but I will be uploading the relevant code to this path at the appropriate time. Great. So I’m going to press the enter key here and it’s created now the good file here. Right. So before we start writing a little bit of code, I suggest that we firstly install a very handy extension that will enhance our go development experience basically. So uh to do that we go to the extensions tab here. And let’s search for Go like that. And this is the extension we want to install. And I’ve actually already installed it. That’s why this button text is set to uninstall. So if I press it, it will uninstall it. But yours might be set to install, meaning you haven’t installed it yet. And if it’s set to install, I recommend pressing the install button and installing this very handy extension here. So this go extension contains the following handy functionality. IntelliSense results appear for symbols as you type. Code navigation jump to or peak at a symbols declaration. Code editing support for saved snippets, formatting and code organization and automatic organization of imports, diagnostics, build, vet, and lint errors shown as you type or on save. Enhanced support for testing and debugging. So, it’s well worth installing this Go extension. Great. So the next step is to create a file within the magic stream movies server folder called main.go. So let’s do that. main.go. As discussed earlier when we created our hello world application, each go application has its entry point and the entry point of the application is denoted by a function called main. So firstly let’s name the package for the code we’re about to write. Let’s name it main like this. Excellent. Let’s import fmt. This is the package we are going to use to write out our text to the screen within the main method. And let’s now create the function main like this. And let’s use the fmt package. Print line. And let’s print out. Hello. Go world. Like that. Okay, that looks good. Let’s make sure we save our code. Right. And now we want to run our code. So to do that, we can make sure firstly make sure you’ve saved the code within the main.go file. And then we can run this command. So it’s just go run dot and that should run the code within our main function here. But firstly make sure that you are in the appropriate directory the root directory of your serverside code which in my case is magic stream movies server like this. And then go run dot should run our code. Excellent. Hello Go world. Great. So now that our infrastructure is set up appropriately, we’re ready to write our web API code. Okay. So firstly, we want to make sure that we’re in the same directory as the go.mod file. So let’s navigate to the appropriate directory. So we want to be in server slash magic magic stream movies server like this and then we want to type the command that will import and install the ginonic web framework package. So to do that, let’s type go get dash u like this. And then we want the path on github to where the relevant package is. So github.com [Music] jinonic slashjin like that. Whoops. Jin and then press the enter key. And it’s as simple as that. And as always, we have to be a little bit patient while the import and installation process is underway. And we’re finished now. That’s brilliant. That wasn’t too painful. Let’s clear the screen. And we’re ready to develop basic functionality using the Genonic web framework. Excellent. So firstly let’s replace this code within the main method. Well let’s get rid of this code and then we can start coding the main method which of course as discussed earlier is the entry point for our application. But before we start writing the code let’s have a quick look at go.mod here. And you can see as discussed earlier, we have references to the relevant packages here from within our gomod file. And we have this gossum file which has also been created for us here since we installed the jinggonic web framework. Let’s go back to the main.go file and let’s write some basic code. First of all, you can see here that the FMT package reference here is underlined. It’s got a red squiggly line under it. And that’s just because we’ve imported it and we’re not currently using it because we removed our FMT. Ln code. So, let’s write the code for our Genonic functionality. This is going to be very basic functionality that we create just to test that we’ve got the Genonic web framework now installed and is now ready to use. So firstly let’s just write some basic code using the Genonic functionality. Okay. So let’s firstly type routter like that and go rout equals. So this operator means we are both establishing or declaring the type for this variable as well as assigning a value to it. And we want to assign jin dot default to this variable. And you can see here it’s automatically imported gonic. So it’s automatically imported the jonic web framework package here. And the the reason there’s a red underline under router is because we we’ve declared and assigned a value to the routter variable but we haven’t yet done anything with it. Okay. So firstly we need to actually include open and close brackets here. And now let’s create an end point a very basic endpoint initially just for testing purposes. So this is going to be a get a HTTP get endpoint. So we go router.get open and close brackets. And then let’s create the root. So it’s going to be for slashhello like this. And then let’s create a function a function handler for our get end point. So to do that we simply type funk like this. open and close brackets. And now we want to include a parameter within our function definition. Let’s call this parameter C because it represents the context of our Jin Gonic web framework. So this is the context of the incoming request coming from the client which allows us to call various functionality on this C object that is given to us through the function handler method automatically. This is the function handler method for our for/hello endpoint. Great. So we got C and then we include the type of C which is star jin dot context like that. Excellent. And then let’s implement the code for our function handler method. So we also want to close the brackets here. Okay. And all we want to do here is return C dot string return a string to the calling code. So we’re going to go C.string 200 which is a restful web API return value indicating that the execution of this function handler method has been successful. And let’s return a message. Hello. Magic stream movies like that. Okay. And we have a little issue here. And we don’t need this bracket here. That’s the problem there. And let’s save that. And now we need to run the router on a particular port. So in in go we can do it like this. We go if an assignment operator router.run and we’re going to run our web framework on this port. So we’re listening on this port port 8080 here on the server. And then let’s make an assertion. If error is not equal to null, let’s run this code which uses now I see that the FMT package has been removed. We’re going to need the FMT package here because we’re going to write something to the screen. So if error is not equal to null, we want to write something to the screen. And so it’s going to be fmt dot print ln open and close brackets and a appropriate message failed to start server and then comma and the error like this. And we need to include FMT here within the imports. FMT. Excellent. Um, and nil should just have one L here. So, let’s save that. And now our server, our web API should be listening on port 8080. And we should be able to access through our browsers this/hello endpoint and it should return a string value of hello magic stream movies. Great. So to run our code, let’s just save that. Make sure that that is saved first. Then type go run dot press the enter key. Great. So it’s telling us that it’s listening on port 8080. So we want to go to localhost port 8080/hello. And it should return hello magic stream movies to us through our get request. And we’ve got our get request function handler method which is returning the relevant status the HTTP status of 200 as well as a message and that should be printed to our browsers. So let’s go into our browsers. So I’m going to activate Chrome here. So I’ve got Chrome running here and I’m going to type HTTP. We’re running on HTTP like this localhost and then we want port 8080 forward slash and we type the name of our route or the name of our endpoint hello like this and let’s see if that works. Hello magic stream is printed to the browser as expected. So our ginonic functionality in its most basic form is up and running for us to use. So, we’ve created a route and we’ve been able to access that route through our browsers and our function handler method handling the relevant functionality for our hello route has been executed and printed the appropriate text value to our browsers. It’s returned it to the client and printed the appropriate value to our browsers. So, great. We’re now ready to write more complex functionality and get into the substance now of the course. Excellent. Okay. So now we are going to get serious about building our restful web API solution using Go and Jenonic. So before we start writing our code, let’s firstly create a basic folder structure so that we can keep our code neat, clean, and dry. Of course, dry stands for don’t repeat yourself. So, we want to keep our code in discrete, easy to find structures so that as our project becomes more complex, we don’t get lost when we need to, for example, implement a piece of reusable code. Right? So, firstly, let’s create a folder named. So, let’s hit this icon here, new folder, and let’s name this folder con controllers like that. Excellent. This is where we’ll store our controller code, which will contain endpoint function handler code used for implementing logic for our HTTP endpoints. If this isn’t clear to you at the moment, don’t worry. It will become clearer as we progress with implementing the functionality for our controllers. We’ve actually already created a HTTP endpoint function handler that handled the functionality for the hello endpoint. If you’ll recall, it was a very simple function that returned a message containing the text hello magic stream movies to the client. And the next one we want to create is a folder called database. So let’s select that. You can rightclick and go new folder. And let’s call this database like that. We’ll store code here that will leverage the MongoDB go driver to connect our web API component to our MongoDB database. Right. So the next one is middleware. Whoops. We don’t want to create it there. We want to create it here. So let’s select that folder, right click, go new folder, and middleware. Like that. Press the enter key. Got our middleware folder. We’ll implement code within this middleware folder later when we implement the authentication and authorization functionality. The middleware code is relevant because some of our endpoints will be protected meaning only logged on users will have access to these protected endpoints and conversely some of our endpoints will be unprotected meaning a user will not need to be authenticated before accessing the unprotected endpoints. The meaning of the middleware code will become clearer as we progress with our course and we get into the authorization and authentication functionality later in the course. So the next one we want to create is a models folder. So let’s select this folder here, the root folder. Right click new new folder and let’s call this models like that. Excellent. This is where we’ll define our models which are used to define the structure of the data that we’ll be handling from within our web API solution. The structure of the relevant models will be based on the relevant document structure seen in our MongoDB database. For example, we’ll create a movie strct that will be based on the structure for a movie document stored within the movies collection in the MongoDB database. So the movie strct will represent our movie model which is based on the movie document structure implemented within our movies collection within our MongoDB database. Great. So the last one actually not the last one but the next one second to last one is called roots. So let’s create a roots folder within our root directory. Okay so here we go roots. Excellent. Uh here is where we’ll write the code for defining both protected and unprotected routes. Essentially these roots define a path that client code for example JavaScript code running in the user’s browser can use to access the endpoints defined in our serverside code our web API code written in Go. As discussed the unprotected roots pertain to endpoints that clients can access without the need to be authenticated before accessing the relevant unprotected routes. Protected roots pertain to endpoints that a user can access only after being appropriately authenticated. So the user will need to successfully log into the system before accessing protected endpoints. We’ll look at the authentication and authorization functionality in detail later in this course. Right. So let’s create the last folder and this is called the utils folder. So let’s create a folder called utils within the root directory. utils. So this is where utility functionality will be stored. We’ll store code here for reusable helper or utility functions that can be reused throughout our web API solution. Great. So now that we’ve got our folder structure in place, let’s create our first file, our first code file within one of our newly created folders. And we’re going to create the new file within our models folder. So to do that, let’s create a file called movie model.go like this. So we’re going to create our movie strct within this file. To access our MongoDB database, we’ll use a specific MongoDB go driver. For this purpose, the ID for our movies model will be of a data type defined within this driver. So firstly, we need to install the MongoDB go driver. So let’s do that. Okay. So firstly, we need to navigate into the same directory where the go.mod file is stored. So we need to navigate to the server slash magic stream movies server. magic stream movies server like that. And now we just need we can install the MongoDB driver for go with this command. So the goget command. So it’s goget then go do db.org org slash d-driver slashv2 slash like that and let’s press the enter key and that should install the relevant go driver the mongodb driver for go as always we need to be a little bit patient here while the installation process process takes place. Excellent. So now if we look at the go.mod file, we should be able to see the reference here to the new Go driver. And where is it? There it is. So here’s a reference to the MongoDB driver for Go package here. Excellent. So we’re ready to create our model now. Let’s go to the movie model.go go file and firstly we want to define we want to declare the name for our package that these models will where these models will reside. So we’re just going to call this package model like this. Excellent. So this means we’ll be able to import the model package which contains which is going to contain the various strcts representing our data models. We’ll be able to import the functionality within this file easily within other Go files. And you’ll see how we do this in just a bit. The package declaration at the top of a file defines the package name to which the file belongs. A package in Go is a way to group related files and functions together. Files that belong to the same package can share functions, types, and variables. Note that only exported names starting with a capital letter are accessible from outside the package. Unexported names starting with a lowercase letter are private to the package. So these packages can be imported by other Go programs or packages. So the name of the package affects how other Go files or modules import and use its contents. So this named package declaration helps us with the organization of our code. So as our project grows in size and becomes more complex, we are able to identify relevant code efficiently. Now let’s write code to import the relevant data type information from our MongoDB driver for Go package. You’ll soon see that we need the BSON.object object ID data type to define our ID field within the movie model which is what we’re about to create as a strct. This bson object ID data type resides within the MongoDB forgo driver. So let’s create an import block like this. So import like that then within round brackets we can import the relevant package. So all we do is we type go.mongo [Music] db.org slashgo.mongodb.org slmongod-driver/v2 slashbson like that. And the only reason it’s got a red squiggly line under it is because we’re not using the the driver functionality anywhere yet, but we’re about to do that. So anyway, let’s press the enter key and let’s create the movie strct. So let’s uh declare our strruct. And we can do that with this code here. Type movie strct. And that’s how we define a strct and go. Open and close curly braces. And we want the first field we want to include is to store a unique identifier for a movie. So let’s call this ID. And then its type as discussed is BSON.object ID. So the first field is called ID and it’s of type BSON.object ID. And this field is for the purpose of uniquely identifying a movie document or or movie data for a particular movie. So, we’ve got ID there. And then the next field is IMDb ID like this. And it’s we’re going to define this as a string like that. Okay. Just a quick word about the IMDb field. An IMDb number, often called an IMDb ID, is a unique identifier assigned by the Internet Movie Database, IMDb, to a specific title like a movie, TV show, or episode or a person like an actor, director, etc. So, for example, the IMDb ID for the Clint Eastwood classic western, Unforgiven, has an IMDb ID of TT 0105695. Great. So let’s create the next field which is named title like this and this will of course store the title of the movie for example unforgiven the shaw shank redemption once upon a time in Hollywood etc. And the next field is called poster path like this and it’s also defined as a string. This field is used for storing a publicly available URL that points to an appropriate poster image for each of our movie documents. Shout out to the movieb.org website. We are using the poster images available on their website in our application for this purpose. And the next field is the YouTube ID field. So let’s create a field called YouTube ID like this. And it’s also of the string data type. This field stores a YouTube video ID that points to a trailer for each movie document stored within our MongoDB movies collection. We are using the trailers on YouTube as a substitute for streaming the actual movies. You’ll see a bit later how we can use this YouTube video ID value to run an appropriate trailer for each of our movies from within our web application. So let’s include a field named genre like that. And this field is going to be defined as a genre strct which we haven’t yet created. And we want an array of genres stored within the movie strct. Great. So in this field we are going to store an array of genres. Each movie can be associated with one or more genres. For example, airplane would just have one associated genre in the array which would be the comedy genre. Unforgiven could be associated with more than one genre. for example, western and drama. You’ll see a bit later in the course how we are going to use this genre array to help recommend movies to the user. So we need to create our own strruct definition for the genre field which will store an array of genre strcts. So let’s define the genre strruct like this. type genre strruct and let’s open and close the brackets like this and then let’s create a field called genre ID and this will be of the int data type. So the genre ID field will uniquely identify each genre within our database. And then the next field is genre name and this is defined as string. This field will store the actual name of the genre as a string. For example, comedy, western, drama, thriller, etc. And then next we have the admin review field which we’ll define as string. This field stores the movie review associated with each movie document. So an administrator which is just a user who is part of the admin role will create a review for each movie in natural language. We’ll use open AI through lang chain go. We’ll discuss lang chain go at the appropriate time and we’ll prompt an LLM through the use of lang chain go to extract one word to define the sentiment behind each of the administrators movie reviews. The sentiment can either be excellent, good, okay, bad, or terrible. These options are specified in the relevant prompt along with the relevant movie review. The sentiment can then be stored in a field which we’ll name ranking. So let’s create the ranking field. So the ranking field here is of type ranking and we haven’t yet created the strct that represents the ranking type. and we’ll do that now. So let’s create the strct for the ranking data type. So we do that by typing type and then ranking strruct open and close curly brackets like this. And this has two fields one named ranking value like this and it is of type int. So as discussed each sentiment excellent, good, okay, bad or terrible has an associated value. For example, excellent has the associated value of one. Good has the associated value of two. Okay has the the associated value of three. Bad the associated value of four and terrible has a ranking value of five. So the next one we want to include is ranking name and this is defined as a string data type. So an integer value is stored in this ranking value field which is associated with the relevant ranking name field which as discussed can be one of the following excellent, good, okay, bad or terrible. So these fields are part of functionality that turns a movie review written in natural language into a quantifiable value, a ranking if you like. You’ll see later how we can use these fields to help recommend movies to users. So the next thing we want to do is define how we want our fields to look in BSON and JSON format. So the BSON format refers to how the field looks within the relevant document in the database and the JSON format is how the data will look, how the relevant field will be named. For example, when the data is returned from our web API application to calling client code, for example, to JavaScript code, for example, running in the browser. So to do that, we open and close back to characters like this. And then for the bon format, we type b. Then within quotation marks we include how we want the format to look. ID like that. Excellent. And then we can also do the same for the JSON format. So it’s got to remain within the back characters. And we type JSON. And in this particular case, the ID is represented the same way as the BON as in the BON format. So it’s underscore lowercase id like that. So we can define within our strcts how the fields map to our MongoDB database as well as to the JSON data that will be sent to calling client code. So for example, our ID field must conform to this BSON format because this is how the field is defined within our MongoDB database and we want our ID field to look the same within the JSON data sent to calling client code. This may not always be the case. Sometimes we may wish a field to look different in JSON format to what it looks like within the database. Therefore, the BSON definition here may be different to the JSON definition here in some cases. So, we include these JSON and BSON definitions within back characters like this. And let’s fill out the other fields. So bon and we want in bon format we want our field represented as imdb ID and in fact we want the same within the JSON format. So we just include the same format here like that. And we just really go through here and do the same sort of thing for each field. So the title field bson in quotations title will just be title like this. And you can see that in all of the cases so far bon and Jason are exactly the same. What have I done wrong there? Underline bon genre id. Okay, so we can’t leave gaps like that or else it complains. Okay, so interesting. Anyway, let’s just remove that gap and that s and let’s include the definitions here. So the last thing we want to do in this section of the course is create declarative rules for each of our fields. we can establish within our strct rules for each of our fields so that only valid data is stored within our strcts. So when for example client data is passed into our serverside code at the point where our code maps for example our movie strct to specific movie data passed in from the client the movie data is automatically validated based on the rules that have been defined. So establishing these types of rules can for example protect our database from being updated with undesirable data. So let’s look at the IMDb ID field. So for this field we want to include validation whereby this field cannot be left empty. So validate. So if the client passes in data representing a movie, this field is required. So it cannot be empty. So we include the required keyword here within quotation marks like that. So this validate keyword here is associated with a comma delimited string of criteria validation criteria represented by keywords like for example the required keyword like that. So we’re only going to include the required validation criteria for the IMDb ID field here. Okay, let’s move on to the title field. And for the title field, we want it to be required. So we include the same thing as we did in the other in the IMDb ID field. So we include required like this. But we also want the title field to contain more than two characters. And we can enforce that rule by using the min equals 2 code here within quotations. And we can also include a max. So we don’t want this field to contain more than 500 characters. So we can include max equals to 500 to enforce that rule. Oh, still got a brown squiggly line there. Can’t believe it. Oh, it’s cuz that needs a space there. Okay, perfect. So that now is formatted correctly. So the the yellow squiggly line went away. No more warning. Okay. Okay. Let’s move on to the poster path field. And we want this to be a valid URL. So let’s include validate colon within quotation marks. We first want to include required. This has to be filled in. This has this poster path field cannot be empty. and then comma and then we can include the URL keyword which means it has to be a valid URL for this path here. Okay, required URL and we have problems here. All right, and it’s because we haven’t closed off poster path here. Okay, and now it likes it. It’s okay. All is well. Okay. So within here validate and we’re just going to include a required validation criteria here. Okay. Brilliant. Genre. So for genre we want it to be required but we also want the validation to dive into the nested array. So let’s first include required here. Let’s first include required and then dive. This keyword dive ensures that the nested structure here which is an array of genres will also be validated. And we’ll include the various validation criteria in just a bit for the genre strct. Okay. And this one here, the admin review will just be we’ll just include the required keyword like that. Okay. And we’ll just include required validation for the ranking field. Okay. Required. Right. So let’s move on to the genre strct and we’ve got as you can see here we’ve got the dive keyword. So this will also be validated. So let’s include the criteria and this must be required and that’s all we need for the genre ID. Okay. And then for the genre name we’ll include we’ll include the required criteria but also min and max criteria. So min equals to two whoops max equals to 100. Lastly we just need to include the validation criteria. Oh, and we haven’t included the BSON and JSON definitions here either. So, let’s do that first. So, so validate, let’s just make this required like that there. And then let’s include the declarative code for the the appropriate declarative code for the ranking name field. So this is just ranking name ranking name. It’s a required field. And now you could establish a little bit more complex validation for this field because you might want to include validation where only one of the following values can be stored within this field. So to do that you can go comma one of. So perhaps you don’t need the required because you’re already telling it that one of these must be in the field. So we go one of equals excellent. So this is delimited by a space these values. Excellent. Good. Okay. bad, terrible. But you may want this field ranking name to be extensible because the way I’ve designed this application is that you could potentially create a huge spectrum of sentiments, not just excellent, good, okay, bad, and terrible. So you must bear in mind that this functionality can be be extended in which case you you’ll have to change that here. So in fact I’m going to remove this because it’s a bit too restrictive especially at this point in the development process. So I’m just going to make this required. Brilliant. And that’s it. So in this part of the course we are going to create an endpoint and an endpoint function handler named get movies within a controller that returns movie data to a calling client. We’ll write code to query our movie collection from our MongoDB database and return all the movie data to a calling client. We’ll use the Jin Gonic web framework to help with HTTP requests and HTTP responses. And we’ll use the MongoDB driver for Go to query our movies collection within our MongoDB database. We’ll then send a HTTP response to the calling client that will contain a list of movie data. But firstly, let’s get set up with the basics for our get movies endpoint function handler and then set up the endpoint that will make it easy to invoke the functionality in our get movies method via HTTP. Okay, so we’re firstly going to create a file within the controllers folder. So let’s select the controllers folder here and let’s click this icon here to create a new file. And let’s call this file movie_ontroller. So we are using a common naming convention here which is snake case for the name of our movie controller file. Excellent. Um and because I’m using snake case here, we should probably use snake case everywhere to keep things consistent. So I’m just going to rename the movie model.go file to movie and then a lowercase M here for model. Great. So that’s now consistent. We’re using the same naming convention for these files. So let’s go back to the movie_controller.go file and let’s name the package. So we want our package to be named controllers. Yep. Like that. So we’ve got our models our model package here. Yes. Okay. So this package we should have named models. So I’m going to do that now. I’m going to name this package models so that we’re being consistent with our naming convention. So this is a package named models which contains movie model and this is the movie_controller file and it’s part of the controllers package. Great. Let’s create a block for our imports like that. Right out of the gates, I’m going to include the gingonic package.com. So, this is the package for the gingonic web framework for slashjin like that. Let’s save it. Excellent. And in fact, that should be import not imports. Okay. Okay. And this red squiggly line is just because we’re not currently implementing any code regarding the G jinggonic web framework. So let’s implement a function called get movies. And ultimately this function will be used to return a collection of movie data queried from our movies collection that exists within our MongoDB database and return that to the calling client code. So let’s create the function. We use the funk keyword and we want this function to be exportable. So it’s going to the first letter must be capitalized. If the first letter was not capitalized, it means the function is a private function. But we want this to be a public exportable function. So we’re going to capitalize the first letter here. Get. And let’s move these like that. And you can see it’s in camel case. And let’s open and close brackets like that. And now this function must return a jin dot handler funk type because we’re now going to actually return a function from the get movies function. And within the return function will contain our implemented logic. So let’s type return like this. funk open and close brackets C and then star jin dot con context like that and then open and close curly brackets and we’re going to then implement sorry there should be a curly bracket here open the first function and then a closing bracket here to close the nested or the returned function here. And you can see now those red squiggly lines have actually disappeared here because we’re now implementing a function handler that uses Genonic. This is how we’re hooking into the Genonic web framework. So here we are able to interact with the Genonic web framework through a function that returns a function. The interaction with Jenonic is facilitated by our endpoint handler function that returns a gingonic type jin. handler funk. So basically get movies returns a function of type jin dot handler funk. Through this context object denoted by the C parameter name passed to our HTTP endpoint handler function get movies. We are able to read incoming HTTP requests as well as create HTTP responses. So let’s create the relevant response. So we’re just going to create a test response for now and then after this just so that we can test our root. We’re going to set up our root for our endpoint and map that route to the get movies function handler. So firstly we’re just going to create a basic JSON response here. So we’re keeping this very simple just so that we can set up. So return 200 HTTP status. Okay. And then we can use jin.h H to help us return a valid JSON message to the calling client. So let’s go message list of movies. Of course, in the actual end code, we’re going to be we’re going to actually return a list of movies, a list of movie data. But before we do that, let’s just set up our route correctly so that we can access the get movies functionality through an endpoint. So to do that, let’s go to the main.go method here and let’s create a new endpoint. Let’s go to do that. Let’s go rout.get. We’re using an HTTP get request to access this endpoint. This endpoint is going to be for slashmov. And then we’re going to map that to to our method. So firstly, in order to do that, we need to actually import our controllers package into the main package. And to do that, we simply type the following controller and then within quotations. And I’ll explain what the controller keyword here represents in just a bit. But let’s include the path to or the name of our package, the full name of our package which includes github.com/gavanlon [Music] digital slashmagic streamovserver slashmic check stream movies server and then forward slash controllers. So this might look a bit weird to you at the moment, but let’s go back to when we created the go.mod file. If you look here, we named this github.com gavanlon digital magic stream movies server magic stream movies server. So we want all of our packages to stem from this root path basically and this just makes it more futurep proof our code and ultimately easier for another developer to import our packages into their application. So we’re going to keep this path as our root path and then all the other packages that we create will stem from this. So for example, if we wanted to import our models, you would just include this root path forward/models and the same for controllers. So let’s go back to the movie_controller file here. And you can see that this package is named controllers. So if we go to the main.go file, we have controllers appended to this root path if you like. So, github.gavlon digital magic stream movies server magic stream movies server controllers and you can make sure that that’s correct by just copying and pasting that. So if you copy that, go back to the main method to the import section here and you can just paste that in there forward slash controllers like that. And we’re doing this so that we can access the get movies endpoint function handler that we just created in our controllers package. As mentioned earlier, this get movies function is exportable because the first letter of the function name is capitalized. If get movies had a lowercase G, it would be deemed as private and therefore not exportable. So because it is exportable, we can access it from within the main method once the controller package has been imported. So here we are importing the controllers package like this. Now you can see here we are using this import alias and this is just for readability. So instead of accessing the get movies method via controllers, we are going to use this import alias controller. So that’s why there’s a red squiggly line under it because we’re currently not using it and we’re about to use it here. Great. So now let’s access the get movies function from the controllers package and we because because we’re using an alias controller we can type controller here dot and in intellisense we’ve got get movies and that’s the one we want. So we are mapping the for slash movies endpoint to controller.get movies here. And if we just go back to get movies, we can select that and go to go to reference. Sorry, go to definition. Okay. And you can see we are just sending back a JSON object to the client message list of movies. So we should be able to run this, go to our movies endpoint and receive this message. Assuming that our route has been set up as we wish it to be set up and we’re declaring how we wish it to be set up with this line of code here. So let’s run it. Let’s first go into the root directory. CD server CD magic stream movies server and let’s type go run dot okay and it’s listening on port 8080. So if we go to port 8080, we should be able to. We want local host, but we don’t want that port. We want 8080, not hello this time. We want movies. That’s our endpoint. If we click the enter key, if we hit the enter key rather, oops, 88, that’s not what we want. We want 8080 there. And there we go. Message list of movies. So this means that our end point has been successfully mapped to the get movies method, which is what we want. So now we are in a position to query our MongoDB database and return the actual movie data to the client. So let’s get on with that then. So let’s go back to our code here and cancel out of that. Clear the screen. And now that our route is set up, let’s complete the logic for our get movies function handler method. Right. So I’m going to delete that. So now that we have our for/mov endpoint and we’ve mapped it to our get movies uh function handler, let’s create the code that queries our magic stream movies database and returns the document data within the movies collection to the calling client. But before we do that, we need to create reusable code that will connect our web API solution to the MongoDB database. And that will be done through the use of the MongoDB for Go driver. Let’s create a file within the database folder here. New file and let’s name it database connection.go like this. Press the enter key. package database. So the code within this file will belong to the database package. Let’s include an import block like this. And we want to import FMT the FMT package so that we can write certain values to the screen. This is good for when we’re during the testing phase of our development during the testing and development phase. And we want to be able to log information. So let’s include the log package. And we want to include the OS package. And this package will be used for reading environment variables which we will configure in a bit within a file called env. And now very importantly we need to import the MongoDB for Go driver packages. So if we go to go.mod, we can see we’ve installed the driver here. So we can actually just import the packages. We actually went through the install process already. Oh yes, and the reason we’ve already installed that is because we needed the bison.object ID data type which resides within this package here. That’s why we’ve already installed it. Okay. So let’s go back to the main.go file. Sorry, we want to go back to the database connection.go file here. Okay. And let’s include the following imports. So we want go. MongoDB.org or slash dashd driver/v2 slash like that. So go.mongodb.org/mongod org/mongod driver/v2/mongo. And then we want a similar import here, but this time we include a forward slash options. And we’ll see the package functionality in action in the context of our database package in just a bit. And of course, they have these have all got red squiggly lines under them because we’re not using any of the functionality of these packages yet. So we’re going to create a function called DB instance which will be used for connecting our Jin Gonic web API application to our Mongodb database through the use of the MongoDB forgo driver. We have already imported these driver related packages for this purpose. Okay. So let’s create our DB instance method. So to do that we type funk DB instance like this open and close brackets. And we want this to return a value of type client like that. Let’s open and close curly brackets. And the first thing we actually want to do is read an environment variable. So to read an environment variable from av file, we actually need a particular package and we haven’t yet got that installed. It’s called joo/go.env. So to install it, we use the goget command. So let’s type goget and then it’s github.com/ [Music] slashjo slashgo.ev. So go get github.com/jo/go.env. Let’s press the enter key. And it’s done it for us. Excellent. So we can verify that. If we look at our go.mod file, we should be able to see a reference to it here. It should have added it. And there it is. Great. Okay, so let’s create the code for our DB instance method. So as discussed, we want to access av file, but we don’t yet have an env file to house our uh various environment settings. So to do that, let’s rightclick magic stream movies server here. Let’s go new file and then create a file called env. So the first environment variable that I want to configure is called database name. So let’s configure our database name here. And we’ve called our database magic stream movies. We can verify that. Let’s just save that. And we can verify that by going to compass. Let’s launch compass. Okay. So, magic-stream.mmov, that’s what we’ve called it. Okay. That’s what we’ve called our database. So, let’s go back to the code and change this to magic dashream dash movies like that. And then the other environment variable that we want to create is called MongoDB uri. So this is a pointer to our MongoDB database. This is a connection string to our MongoDB database and we can configure it here. And this will make it easier for when we deploy our application to the cloud later on. And we can just configure the new location of our database here. The new connection string for our remote deployment deployed version of our MongoDB database. We can just configure that connection string here. So let’s minimize that and go back to compass. And we can actually get our connection string from here like this. So we can just click on this and copy the connection string to our clipboard. So, let’s head to this option here and then we can then we can just paste it in here. Excellent. Okay. And let’s go back to our database connection.go file. Okay. So, the first thing we want to do is load our uh file that contains our environment variables into memory. And we can do that with this line of code. [Music] env.load and then in quotations env like this. And oops. And if a error has occurred while trying to load this file, it means it might be deployed to a remote server that doesn’t contain this file. So we’re actually just using our environment variables locally at the moment and that’s why we’re using this particular package. The environment variables will be configured differently um when we deploy it to the cloud. But we need this line of code here. So in fact this line of code will return an error in the cloud. So we don’t want it to be a fatal error. We just want to check for the error and we can just print it to the screen. Just print a warning telling us that the env file does not exist. And this is this probably is harmless because it means that it’s deployed to the cloud and it’s reading the environment variable a different way. We’ll cover that in more detail when we deploy the application to the cloud. So if error does not equal to null, this means an error has occurred, which probably means that the env file does not exist, which probably means that the version that’s running is a deployed version and the environment variables are not configured within av file. But we’ll discuss this a bit later. So I’m just going to print a warning message to the screen. warning unable to find. Envile like that. Great. So if you had for example gone log.fatal here, the code would actually stop. It would print the the error to the screen, the error message to the screen and this function would and the execution of this function would discontinue at this point. So, we want to print it as a warning message so it’s not a fatal error. Great. Okay. And then let’s read the environment variable. So, let’s include this line of code that assigns that assigns the relevant environment variable. We’re using the OS package here. Get env method and we want to read a particular environment variable. We want to read the Mongodb URI environment variable. So let’s go back to that and paste it here. And then we can check. So if db is equal to an empty string then some sort of issue has occurred and our application cannot really continue without this information. So we’re going to log a fatal error this time. dot fatal. We don’t want the code to continue after this point. So we go log.fatal fatal and we can output a message saying MongoDB URI not set not set that should do okay can’t read the MongoDB URI which contains the connection string and we can just make we can just print that using FMT to the screen we can actually print the the relevant environment variable to the screen using the FMT package like this comma db like that. Excellent. So now that we’ve got our URI, we should be able to connect our code here, our client code in this case. Even though it’s serverside code, it’s in the context of connecting to the MongoDB database, it’s client code. So we can go client options colon equal options dot client open and close brackets dot apply uri and then let’s include the readin environment variable which is mongod db. Excellent. And then now that we’ve read in the actual connection string, we want to actually connect to our database, our MongoDB database. So let’s do that with this line of code. Client, error. You see this function that we’re about to to call returns two values. one which is a Clyde object and the other which potentially contains an error. If the code hasn’t been executed for some reason, if the code could not be executed for some reason due to an error, this object will contain a value. Great. So now we call mongo.connect connect and we pass in the client options to connect to our MongoDB database. So if error is not equal to null, we’ll cover error handling in just a bit. We’ll cover the details of error handling in just a bit. Let’s first write our DB instance method. Let’s just get the fundamental code in place first and we’ll look at some of the details in just a bit. So if it’s not equal to null, I’m actually just going to return null to the calling code. In fact, let’s return the in fact. So if is not equal to null, let’s just return error to the calling code. This means the code will cease to execute after this and it will return whatever error occurred at this point in the code. And if no error has occurred, let’s return the client object. For now, let’s just return null. If the error occurs at this point, we’ll just return null just to get things up and running. We can handle the details of that exception a bit later. So now we want to create a method which opens the actual connection to our database. So let’s create a function called open collection like this. And this function contains a collection name parameter as a string. So for example, if we are querying the movies collection, movies will be passed in here as a string. Okay. And we want this to return a collection. So collection like that open and close curly brackets. And we can implement the logic for this method now. Okay. So we need to firstly load the env file to see if it exists. and it will exist if the code is running locally but perhaps not if but won’t exist if we’ve deployed it to the cloud. So let’s do a check for that. Let’s go gov.load and let’s load the env file here like that. So if uh is not equal to null, let’s do the same thing. We’re just going to print a warning. Okay, like that the env file doesn’t exist. And then we want to read the relevance environment variable at this point. So, and assign it to a variable called database name. And you can see that we’re using this special operator here, colon equals, which means it’s actually declaring the variable and its type. It’s inferring the type by the returned value here. So, get the get env method returns a string data type. Therefore, database name because we’re using this particular assignment operator, it knows that database name go knows that database name is defined as a string at this point. So, we don’t have to explicitly for example in a line up here go something like string database name because it already knows that this is a string based on what is returned by this method. Okay, let’s go data base name like this. Okay. And we can go use fmt print ln just to for testing purposes to make sure that the correct database name is indeed being read from the environment variable. And we can just output it here like this. Okay. And that’s just for testing purposes. Great. Now we want to actually load the relevant collection. So to load the collection we go collection and once again we are inferring its type by the returned value from a method which we’ll write right now and we actually need the client object at this point. So outside of all of our methods we want to create a client object based on the DB instance. So the client returned from here will be our client object. So let’s call this client and it’s of type mongo.client equals to the return value from our DB instance method here. Great. So we can now use this client object to open a collection. So let’s do that. Let’s go client database and we’ve read in our database name. So we can use this variable which should contain our database name and pass it in to the brackets here. and then dot collection and then we want to pass in whatever collection name has been passed into the open collection method. So we can copy that and paste it in there like that. If collection equals to null obviously something has gone wrong. So we’ll return null to the calling client code. But if it gets to this point, we’ll return the actual collection. This has only one L. I keep giving null two L’s. Sorry about that. So that’s now corrected. So now we have our um open collection code written in this method here. We are able to connect our client which was set up through our DB instance method here to the MongoDB database from within our web API component. Excellent. So, we’re in good shape here, and we’re ready to write the code for the get movies function that will connect to MongoDB, query the movies collection, and return that data to a calling client via HTTP. And we’re going to do that with the assistance of the Jingonic framework. So, this is the context variable here. and we’re going to use that to help us uh return a response to the client. Okay. So firstly we want to include this line of code here, cancel and this is done so that no matter what happens in this method that our query that we’re about to write will time out and clear up any resources that are hanging about. So this is really just to ensure that housekeeping is automatically conducted no matter what happens in this method. So context dobackground and we can set the time out here to 100 seconds second like that. Okay. And you can see that because I’m referenced the time object here, it included the relevant package automatically within the imports block here. Within the import block here. Okay. And these are underlined because we’re not yet using them. These variables here returned from the with timeout method. And we also need to include this line of code. Defer cancel like that. And then let’s define a variable called movies which will store an array of movie models a movie strcts. So firstly we actually need to import our models package from here. Okay. Okay. And to do that, we can like we did here where we’re importing the controller controllers package, which is one of our packages. We can do a similar thing here. I’m just going to copy that and change the relevant bits. So, let’s go to movie.controller movie_controller.go and include the local import here. And instead of controllers, this will be models like that. So we’re now importing our models which will contain a reference to our movie strct. So we can go models dot movie like that and define our movie movies array as models.mov. So this array can store a number of movie struts within it. Okay. And then let’s create a cursor for the purpose of querying and returning data from our MongoDB database. So firstly we actually need to create we need to connect to our movies collection. And because we’ve written all this reusable code here, we can use this open collection method here to connect to our MongoDB database. So we also need to import the database package into our controller file here, movie controller file here. So let’s make a copy of that. Copy that. Paste it here. and replace models with database like that there and then we can use the exportable methods within the database package within our get movies function handler method here. So let’s create outside of any method here. Let’s create a connection to our movies collection. And we can do that with this code var movie collection. And let’s And our movie collection is of type mongo.olction. Oops. Like that. Equals to. And then we can refer to database because we’re now importing our database package dot open collection which is our method that we created within our database package and we can pass in the movies collection here as a string. So we pass in the movies name which is the name of our of the collection we want to connect to here. Excellent. So it’s running our code here. to connect to the movies collection. So let’s go back to movie controller there and let’s use our movie collection variable here which is been deliberately coded outside of the method so we can access so we can access it with any method within this file. So let’s paste movie collection there dot and let’s call the find method and we’re actually calling the find method on a collection object a MongoDB collection object. So, we’re using the MongoDB forgo driver here. And let’s pass in the context ctx and then bson m like this. And we need to create a reference to bson here in order for that to work. So, we’ve actually already done that in the movie model file. So, we do the same here. We import the BSON package here. Okay. And that’s now working. And of course, we are not using any of these variables. That’s why we have these three problems here. And these are underlined. Okay. So, let’s move on to that. The next code, let’s check the error object. So if it’s not equal to null and I will get it right this time, null 1L. Okay, we can use the context and write JSON to the response like this. And we want to include HTTP 500 status error. And we can use this value here for our internal server error. So we’re saying an internal error, an error has occurred. If the error is not null here, an internal error has occurred within our web API component. We’re sending that to the client. And we can use jin.h here to send back a JSON response error. failed to fetch movies like that. Okay, excellent. Now, if it gets to this point, so no errors have occurred there. We’ll use the defer keyword, and I’ll explain what this all means a little bit later. And then we close our cursor. So no matter what happens within this method, the cursor gets closed and any of the resources that the cursor uses will be cleared up. So this is really for memory management that we’re using this line of code here and this line of code here of course. Okay. So let’s now create an if statement if equals cursor. Let’s use our cursor here. Do all method called on our cursor. Let’s pass in the context. This is for resource management. This context up here resource management purposes. And then we want to pass the cursor into our movies array like that. So we want to pass it in at this memory address. So this amperand means that we’re passing it directly into a particular memory address in memory into this movies array here. So now let’s put a semicolon there. And if error is not equal to null, we’ll explain what the semicolon is in just a bit. But let’s just first write out our code. Okay. So if that equals to null, let’s also pass back a status of internal server error. Okay. Uh, and the message here should be failed to decode movies. So, it failed to insert the movies cursor. Oops, we have a problem there. Failed to insert the movie’s cursor into this movies array variable. Okay. If an error is returned at this point here, sorry, no, if an error occurs at this point here where we’re placing the cursor into the movies array here. Great. Now if it reaches this great and now if it reaches this point here we can return using our context context our C variable which is the context object from Gonic passed into this method from the gingonic web framework. We can use this C method, the C variable, the C parameter or argument to call the JSON method and pass back a status of okay. So that’ be 200. Okay, sorry. Status. Okay, this must be capitalized. And then we can pass the movies array and it will be passed down to the client in JSON format. Excellent. No problems. That’s what we like to see. And we can actually launch this and test this all. So let’s try that. Let’s go to the terminal here and run our code. Go run dot. And it’s now listening on port 8080. So let’s see what happens when we navigate to the movies endpoint. Let’s see if we get any results. Okay, let’s fire up Chrome http localhost port 8080 slash movies. Look at that. All our data has been returned. And of course it looks rather ugly at the moment. But when we create the React part of our code using Bootstrap 5, React Bootstrap, we can make it look something like this. I’ll show you right now what we’ll make it look like. And there we go. So this is what it’s going to look like. The end result will look like. So it looks pretty cool. Our movies returned to the browser will look like this. Okay. So let’s write the code for another endpoint handler function that retrieves and returns data for just one movie this time from the movies collection in our magic stream movies database. Okay, you can see here we’ve got the get movies handler function. And now we’re going to create a function that is for the purpose of retrieving just one movie. Rather than returning all the movies from the movies collection, we’re going to find one specific movie within the movies collection. And that query will be based on the IMDb ID for the relevant movie. Okay. So let’s write the code. So it’s funk get movie like that. Open and close brackets. And then we want to return a function just as we did in the get movies handler function. We want to return a function of type handler funk like this. And this is a way of hooking into the gingonic web framework. Okay. Uh so to hook into the gingonic framework, we are going to return a function of type jin.andlerf funk. So let’s employ the return keyword here. And let’s write an anonymous function here. So when I say anonymous function, of course, I mean it doesn’t have a name. So to do that, we type funk open and close brackets like that. And then we can pass in the context of the jinggonic web framework um through this parameter the C parameter here which is of type star jin.ext like that. So this will give us context essentially from the web framework and will make it easy for us to well for example return a response to the calling client code or reading a parameter. You’ll see we are able to read a parameter from the URL which is something we’re going to do in this particular function and handle requests handle request data and that sort of thing. So the Gengoni web framework is making that a lot simpler for us. Okay, perfect. So let’s write the code. The first thing we want to do is create the context for our query to the MongoDB database. So to do that, we type ctx and it’s returning two values. So we also want cancel here. And then let’s use our assignment operator context dot and then of course the method with cancel and we want with cancel time out here and this is for the purpose of cleaning up resources. Okay, within memory dot background and then comma and we wanted to time out in 100 seconds. 100 times time dot second and we have a problem here. Uh it’s not with cancel timeout, it’s with timeout. That’s the method we want to call here. And then we need to include this line of code defer cancel like that. As you saw earlier when we created the get movies endpoint function handler, we are returning a function of type jin.andlerf funk which provides us a way to hook if you like into the gingonic web framework. So this for example makes it easy for us to map a HTTP endpoint route to the relevant HTTP endpoint function handler. This also makes it easy for us to handle HTTP requests and create HTTP responses from within the relevant endpoint function handler. So you can see here we are calling the contextwith timeout method like this. And this defer cancel code like this. This code creates a new context ctx that automatically cancels after a specified duration. So here the timeout will occur after 100 seconds. The function returns ctx. CT ctx is the new context that carries the timeout. Cancel is a function to manually cancel the context before the timeout if necessary. Defer delays the execution of cancel until the surrounding function returns. This ensures that the context is properly cleaned up and resources are released even if the function exits early due to an error. Okay, so now we’re going to use the C argument here, the jin frame, the sorry the jin context or jin gonic context here to read in a parameter from the URL and we’ll see the details of how this parameter value is passed in through the URL when we map this handler function to a particular endpoint and we’re going to call the endpoint for/mov instead of for/movies which is mapped to the get movies function there. But we’ll do that in just a bit. For now, we just want to read in the movie ID, which is going to be the IMDb ID for a particular movie stored within our database. And then we can use the C object here param method like this. And then pass in the name of the parameter we want to read from the URL from the end point path if you like. imb ID. Excellent. So that should populate our movie ID variable. So the next thing we want to do is just check that we have in fact retrieved a value from the URL. So let’s create an if statement to do that. Movie id equals. So if movie ID equals to an empty string, let’s pass back to the client using the C gen context object and the JSON method. Let’s pass back to the client a HTTP status of bad request. status bad request. And then let’s include a message jin.h. And we can pass back valid JSON to the client like this. So error colon and then an appropriate error message. Let’s just say movie ID is required. So no movie ID was passed in with the endpoint path. So we need to fail this function call here. And let’s include the return keyword so that the execution of code discontinues at this point. Okay. And let’s now create a variable named movie var movie. So this is a local variable of type mo models do movie. And this is a userdefined type that we created a strct that we created within our models within our movie model.go go file here that we can use to store movie data. Great. So, let’s go back to our movie controller. And the next thing we want to do is check if an error has occurred while we attempt to query our movie movies collection with this with this code. and we’re using the find one method. So we are employing the functionality within the driver for go package. As you can see here, if we go to movie collection which this is what we this is the reasonable connection functionality we created in the previous part of this course. Here we are returning a mongo.colction collection and we’re connecting to a particular collection through this reusable code here. So if we go back to movie_controller we can see here that we are connecting to that particular collection the movies collection here. So now with this code using find one we want to query the our database the movie collection specifically for a particular movie and the query will be based on a unique identifier which will be the IMDb ID retrieved from the IMDb ID parameter. Okay. So find one and then let’s pass in the context which is for the purpose of ensuring that resources are cleaned up. So even if an error occurs within this function the relevant resources associated with the query will be automatically cleared up. So this is a housekeeping mechanism that we need in place so that uh to prevent things like memory leaks for example. Okay. So find one ctx bson m and now we’re creating a filter here on the IMDb ID parameter value like this colon movie ID. Of course you can see here we’ve set we’re setting and that shouldn’t be capitalized. Sorry. You can see here we’re setting this local variable. We’re declaring it and setting this local variable to the parameter value passed in through the URL to the our handler function get movie. Yeah, our handler function called get movie. So movie ID equals to the IMDb ID parameter and we’re using that here to query the movie collection for a particular movie. Okay. Okay. And then we can then the next thing we must do is pop the ref pop’s probably the wrong word. Um to put the relevant movie data if it’s found by the find one method into our movie variable here. So let’s do that. Movie. And we want to place it at a particular memory address of our movie variable. So let’s include an amperand preceding the movie variable name here defined as our movie strct. So we want it to be placed inside a particular memory location here. Okay, great. And let’s check if that error is null. So if it’s not null, an error has occurred and we need to handle the error. And all we want to do is pass back an appropriate message to the client and the relevant HTTP status. And we can do that through the C.JSON method like we’ve done before. So let’s pass back the status. http status not found. So this means that the resource that we’re looking for within our database was not found. So we need to pass that back to the client with an appropriate JSON message and we can implement the relevant JSON message using jin h like this. So we’re employing the ginonic framework for this purpose here. And so error Movie not found. Okay. Error. Sorry, it shouldn’t be a comma. That should be a colon like that because it’s a JSON type format here. Okay. So, we also want to include the return keyword because we want the execution of code to stop at this point. Then if no error has occurred with this through this line of code where we are finding the movie document based on the IMDb ID value then we can return that movie data through this line of code to the calling client code. So we want to pass back a HTTP status of okay like this. And then we can just pass the movie data back like that through this second argument here using the JSON method called on the C context object passed into our handler function. our endpoint handler function through the Jinggonic web framework here. Brilliant. You may have noticed the colon equals operator and you might have correctly inferred that this is an assignment operator where the relevant parameter value named IMDb ID is assigned to a variable named movie ID. The colon simply means the type of the variable is inferred based on the value being assigned to the variable. So colon equals is a concise way to declare and initialize variables in go. It infers the type automatically. It’s only valid inside functions. It’s a Go idiom used often and preferred by local variable declarations. Right? So we’re now ready to map our handler function here, get movie to an appropriate endpoint. So to do that, let’s go to main.go. Let’s just duplicate this line of code here firstly and change the appropriate parts. So here we want to change this to movie because we just want to return a singular movie to the client code and we also need to change get movies to get movie here. But the real difference between this routter.get get method and this one here is that we need to pass in a parameter value through the URL when this particular endpoint is called because the get movie functionality depends on the relevant IMDb ID value. So to do that it’s actually very simple. we include forward slash colon and then the name of the parameter which is im db id like that. So if we go back to our get movie method, go to definition, you can see here we are able to retrieve using this context object passed in by the web framework. we are able to retrieve the IMDb_ID parameter value here and then we can proceed with our query and the functionality of this get movie method and ultimately return the movie data to the calling client. So let’s go back to the main.go go file and within the main method you can see that we have now created the relevant route for our get movie handler function. So the only thing left to do is really is just test it. So let’s firstly go into the appropriate directory so that we can run our code. So cd server slashmagic stream movies server like that and then we just type go run dot okay and and it’s listening on port 8080. So we can navigate to our new endpoint through our browser. So let’s go to let’s use Google Chrome. Let’s use Chrome to navigate to the appropriate endpoint. So http localhost colon at80 and this is the sort of thing we want. We want to go to movie and then we need to include a particular IMDb ID. And let’s find a particular IMDb ID through compass. So, I think we should use this one. Let’s uh query for the Empire Strikes Back. Star Wars: The Empire Strikes Back. An absolute classic, of course. And so, let’s go here and include TT000080684, which is the IMDb ID for Star Wars: The Empire Strikes Back. Let’s press the enter key, and it returns the appropriate data. We can actually verify this visually by copying the poster path here within our JSON data to another tab here. And let’s see if it returns. And there it is. Look at that. Episode five, the Empire Strikes Back. So that is working. Brilliant. Okay, let’s find another one. Let’s go to Compass and let’s do Star Trek the Undiscovered Country. So let’s copy that ID to our clipboards and paste it in here. So you can see how the IMDb ID is passed in through the relevant endpoint. And there it is. Star Trek the undiscovered country. Let’s copy the poster image here. The path to the poster image which is a publicly available image address. And let’s paste that here and press the enter key. And there we go. Star Trek: The Undiscovered Country. And for good measure, why not? Let’s do another one. And I love the movie Unforgiven. So, I’m going to copy this IMDb ID and paste it here. Let’s see if it finds Unforgiven. And there it is. And let’s confirm that by copying the poster image into the oops into the next t tab like this. And what a movie this was. Rest in peace Gene Hackman. But this was a fantastic Clint Eastwood classic. And of course Gene Hackman was amazing in it as always. Morgan Freeman was fantastic and Richard Harris. So what a cast. Excellent movie. Great. So anyway, we are absolutely getting there now. I’m very happy with what we’ve done so far. I hope you are too. Great. So we’ve created two endpoint handler functions for two HTTP get requests. One that retrieves data for the entire movies collection and one that returns data for a specific movie. So we have covered creating genonic endpoint handler functions for HTTP get requests. So let’s create a handler function and let’s name this function add movie like this and returns a type of jin. Handler funk because it’s a handler function that hooks into the gingonic framework. Let’s open our curly braces like that. And let’s include the return keyword here. And then an anonymous function that will contain our actual implemented logic. And we want to pass in a parameter named C of type star jin. Context like this. And this is all for the purpose of hooking into the Jinggonic framework which makes it easier for handling requests and creating responses. So here we we are declaring a function named add movie which will handle the functionality for an endpoint that we’ll map to this function once we have written the logic for the function. It returns a jin. funk type which is just a type alias for type handler funk funk and in parentheses star context. So this is the actual handler function that jyn or jin gone will call when a request hits the root it’s attached to c star jin.context gives you access to the request response path forward/query params form data etc. So our function is integrated with the Jinggonic web framework which makes it easy to access the request response path for/query params form data etc. This function is going to handle a post request because our function will add movie data passed in from the client to the movies collection within our MongoDB database. In our web API solution, we are appropriately using HTTP methods like get and post. We have so far implemented codes to handle get requests to retrieve resources from our MongoDB database and send them back to the client in an appropriate HTTP response using the restful architecture. When adding a resource or resources to our database, it is appropriate to use a HTTP post request. So we are using the restful architecture in our Genonic web API solution. REST or representational state transfer is a software architectural style used for designing web services and APIs. It defines a set of rules and conventions for how clients like web apps or mobile apps and servers should communicate over HTTP. A restful API uses standard HTTP methods get, post, put, delete, etc. to perform operations on resources which are typically represented by URLs. So our get movies and get movie endpoint handler functions hook into the gingonic web framework to handle HTTP get requests with our add movie endpoint handler function. We are going to add a resource to the movies collection within our MongoDB database. So we are going to use a post request. We are going to map the add movie endpoint handler function to an endpoint that handles a post request. So let’s add the code to handle the context of our interaction with the MongoDB database. So it returns two values from this particular method. one which is the context and the other one which allows us to cancel the action that the context will be mapped to. So we need to call the context dot with timeout method like this. Pass in context dot background like this. And the amount of time we want to lapse before the action that this context is mapped to is forced to stop. So we want that time lapse to be 100 seconds. And we can implement that code like this. Okay, great. Okay, that looks better. contextwidth timeout. Um, okay. Excellent. So, the red squiggly lines under these two variables is because they are currently not used. So, the next line of code we want to include is defer cancel like that. Excellent. Okay. So, that red squiggly line has gone away. and then we’ll use the ctx variable a little bit later. So, as we did in our last two endpoint handler functions, we implemented code using go’s context package to create a timeout context. And it’s typically used in Jin and other Go web frameworks to control how long an operation is allowed to run before being forcefully cancelled. This is useful for setting a deadline on longunning operations like database queries, HTTP requests or any blocking operations. When the timeout expires, the CTX or context is cancelled and any operations listening on that context will stop. Context.background is the base context often used at the top level of an application or request. It’s not cancelellable by itself, so we wrap it with a timeout context. Cancel is a function that manually cancels the context before the timeout. Defer cancel ensures that when your function exits, it frees up resources associated with the context. It’s important to call cancel to prevent context leaks. For example, go routines waiting on a context that never gets cancelled. Note that a Go routine is a lightweight thread of execution in the Go programming language. It’s one of the key features that makes Go well suited for concurrent programming. Go routines allow you to run functions concurrently. They are much more lightweight than traditional threads. Creating thousands of Go routines is feasible and common in Go. Unlike threads managed by the OS, Go routines are managed by the Go runtimeuler, which multipplexes thousands of Go routines onto a small number of OS threads. You start a Go routine by using the go keyword before a function call. So note that even though you haven’t seen me use the go keyword explicitly in this web API project, the R.Run. Yeah, we’ve got rout.run. The r.run method call starts a HTTP server using go standard net http package. the net HTTP package itself spawns a new go routine for each incoming HTTP request. So every time a client makes a request, it is handled concurrently in its own go routine. So let’s look what happens under the hood. Jin is built on top of Go’s net HTTP functionality. The HTTP listen and serve function used internally by R.Run run listens for connections and calls a handler in a new go routine for each request. This allows multiple requests to be processed concurrently and efficiently. Okay, let’s go back to our add movie function. So, let’s create a variable for the purpose of storing the movie data passed in from the client in memory on the server. Okay. Var movie models do movie like this. So this is the userdefined strct that we created earlier on within the movie_model.go file here. So this is the type that we are going to use or the strct we are going to use to store the incoming client data in memory on the server. Excellent. And it’s just got a red squiggly there because declared but not used. Same with the ctx at the moment. Okay, let’s press the enter key. So here we have created a variable of type models.ov and this variable will store the relevant movie data passed in from the client. The models domov type is of course our userdefined type that we created earlier. Let’s write code to attempt to save the movie data passed in from the client to our add movie endpoint. And we want to save this data within our movie strct. So let’s write the code for this. So then our assignment operator here, we’re just checking for an error returned from C.ind. We want JSON. So it’s C shouldbind JSON like this. and then the amperand character because we want to locate a specific part of memory. So we use the amperand for this. So it’s a pointer to a memory location when we put an amperand. It’s a reference to a memory location rather than the data itself. It points to the data amperand movie like this. And then we’re going to check the error by adding a colon there and going not equal to null. So if the error is not equal to null, we need to handle an error because an error has occurred. And so let’s type c the context the jonic context JSON method like this. And we want to pass back a status of bad requests. So status bad request like this, which I believe is represented by the number 400 or 403. No, it’s 400 I think. HTTP status bad request jin and this is just a shortcut for creating a JSON object. So jin.h and we’re using the gingonic framework for that purpose. So error and then let’s map the key value pair the key error to a value of invalid input. Whoops. Input like this. Okay. And then we want to include the return keyword. So it will stop execution at this point because an error has occurred and we want to just handle that error. Send back an error message to the client and then terminate the execution of the function. The functions basically failed at this point. So let’s just go over this code again quickly. So C is of course the jinggonic context object which represents the context of the current HTTP request should bind JSON and then in brackets amperand movie attempts to pass the incoming JSON payload from the request body into the movie strct. If there’s an error during binding for example the JSON is malformed or doesn’t match the expected strruct fields the error variable or the error variable will not be null. So the code inside the if block will execute. If there was an error, this line returns a 400 bad request HTTP response. Jin.h error invalid input is a shortcut for creating a JSON object with a key error and a value invalid input. The return keyword ensures the function exits immediately after returning the error response. No further processing occurs. This code safely handles JSON input by validating and binding it to a go strruct movie. If the input is invalid, it sends a clear error response and holds further execution. The amperand character in amperand movie is the address of operator and go. This gets the memory address of the movie variable i.e. it passes a pointer to movie not the actual value. So now remember we included the declarative tags in our movie model here for validation purposes like validate required. So to validate the movie data now stored within our movie variable we need to firstly install a go package named go playground. So to do that make sure that you’re in the current directory. Uh rule of thumb is make sure you’re just in the same directory as where the go.mod file has been stored. And then we type this command to install go playground. So it’s just go get. Remember we’re using go playground to validate our movie variable. So whatever the client has passed into the add movie method, we’re validating based on this criteria here that we included within back to characters here. And we need this particular package to perform the validation. So we’re installing a validation package commonly used in Gengonic applications. So we type this command to do that. Go get and then it’s github.com slash goy-N playground hyphen valid day tour slashv10 like this and let’s press the enter key and something is happening Brilliant. That’s a good sign. So, we’ve installed our Go Playground package and we can verify that by going to go.mod here. We should be able to see it somewhere here. There it is. Validator. Our playground packages. The references to the playground package are here. Brilliant. So let’s go back to our code and let’s include the relevant validation code. So to do that let’s type if assignment operator validate. But you see we haven’t got this validate object set up yet. So we need to do two things. We need to import the go playground package firstly. So we can do that here like this within quotations. Okay. So let’s type github.com slash Whoops. github slashgoy-enplay. Whoops. Gosh. Go hyphen. playground slash validator slashv10 and we’re importing our go playground validator package like that and then the next thing we want to do is create the validator object and we can do that with this simple line of code we type var validate attor equals to validator dot new like that. Great. And now we can use our validator which is which is provided to us by the go playground package here and we can use it now to validate our data. Great. So validate we’ve called We’ve called the object validate and then dot hang on. Oh, we’ve called it validator. So, let’s call this validate. Okay. See if we can get some intellisense. Now, we’ve got the name correct. Validate dot strct. Then we pass in movie like this. And then we can include a semicolon and check the error. So if the error is not null, we know an error has occurred and then we can handle the error appropriately and we want to send back an error to the client with a HTTP status of internal. Actually no, we want it to be a bad request. So it’s a bad request 400 we’re going to send back. And then we’ll use Jin Garnic or Jin to create a JSON response error response. Oops. We type error colon and our error message could be the following. Validation failed. Oops. failed comma and details comma dot error. So the actual error message. So hopefully this can be useful to our client code in the event that an error occurs at this point in the code. Okay. Great. And then let’s use the return keyword here to stop execution of the logic within the add movie function here. Okay, brilliant. So if we get to this point in code, we want to then insert the actual data within the MongoDB database. Not sure why we’re getting all this. Okay. Before new line, missing comma before new line. What? Oh, I haven’t included the if here. Sorry. Having a bad day. The go playground validator is typically used in a genonic web application to validate a strct like movie after it’s been bound from JSON. It uses the Go playground validator v10 package. A powerful and widely used validation library in Go. Validate is an instance of the validator. Validate.ruct movie checks the movie strct against any validation tags. For example, validate required on its field. If any field fails the validation, it returns an error not null and the if block runs. This code ensures the movie strct is valid before proceeding. The validation is based on our declarative validation instructions that we included between backtick characters for the movie structure. For example, if we go to our movie models file and we check some of these, we’ve got validate required. We’ve got min max. We’ve got limitations on the minimum amount of characters we want stored in the title and the maximum amount of characters we want stored in the title field. Here we’ve got a URL validator here for the poster path. So we can include declarative code here to create rules for our fields within the movie strct. So our validation functionality here will validate the rules that we’ve established between backtick characters for each of the fields within our movie strct basically and then we are handling that error here. Okay. Excellent. Right. So if any field fails validation based on its strruct tags, the API responds with a 400 status and a helpful error message. So let’s write the code that actually inserts the data passed in from the client. Now that it’s been validated, we want to insert it into the database. So let’s write the code for that. So we type result, so two values can be returned, the result and an error if an error occurs. So let’s assign that to the actual action and we want to use the movie collection and then we want to call the insert one method to insert a resource into our database a movie resource. Now we can map the context that we created up here. We want to map that context to this action here context and then we want to insert movie into the database. So we do this and then we let’s check our error. So if error is not equal to null. Oops. So if error is not equal to null c.json and let’s pass a relevant error back to the client. So let’s pass a status of internal 500 to the client internal survey error like this. And let’s use jin.h here to pass wellformed JSON back to the client. Let’s include an error key and its value failed. to add movie. Great. That looks pretty good. And then include the return keyword to stop execution because it’s failed at this point. So this go code snippet is inserting a document, a movie into a MongoDB collection using the MongoDB go driver. This is a MongoDB collection object connected to the collection where you want to store the movie data, movies. The insert one function inserts one document into the collection. CTX is a context used for timeout, cancellation or passing metadata. Movie is the go strruct or map that will be converted into a bon document and inserted into the MongoDB movies collection. Insert one returns a result which contains information about the insert operation such as the inserted document ID. is an error object which is nil if the insertion was successful. If insert one fails for example due to a connection issue bad data etc it responds with HTTP500 internal server error and a JSON object with a error message failed to add movie then returns to the client. The return keyword halts further execution. So this code tries to insert a movie into MongoDB. If successful, the code execution continues. If it fails, it sends a 500 response with an error message. Lastly, if the code reaches this point, we want to send a success message to the client, a HTTP success message. So, we type C.json and then within parenthesis HTTP status created and then we want to send the result back to the client. Great. So that is pretty much our logic finished for the add movie function. So the next step is to map the add movie handler function to an appropriate endpoint an appropriate route. So let’s map an endpoint route to our new add movie endpoint handler function. So to do that let’s go to the main method and let’s just make a copy of this here. this code here, but this time we want to use a post HTTP method. And then let’s map our handler function method to a end point. And let’s call this one forward slash add movie like that. So that’s the path to our endpoint that we’ll add to the base address of our web application. And then we go controller add movie like that. And that’s it. We’ve mapped our add movie handler function to the add movie endpoint here. So let’s test our new add movie functionality. But now it’s not so easy to test this functionality as we did for the get movie and get movies functionality. The get movie and get movies functionality are both mapped to HTTP get requests which makes it easy to just invoke the relevant endpoints through the browser. The results are returned and we can see the displayed results within our browser in JSON format. However, we need to map our add movie handler function to a HTTP post request where we need to pass movie data in JSON format through the body of the relevant HTTP message. So rather than for example create our own HTML form on the client that can run within our browser for testing the add movie functionality an easier option is to use a free tool called Postman for this purpose. This is a great tool that we can use for testing our restful web API endpoint. So let’s navigate to www.postman.com. Right. So let’s navigate to www.postman.com. So this is where you can download this free tool called Postman and we use this tool to test our web API endpoints. Then you want to click on this menu option here. Let’s just accept all cookies. And then we want to click on this header menu option here and then click download Postman so that we can install our free version of Postman. And you can see here it’s detected my platform Windows 64 and then please just go through the relevant instructions. Download the relevant install file. Double click on it once it’s downloaded to your downloads folder and go through the install process. And this is what Postman looks like. Okay. So now we want to test our ad movie endpoint using Postman. So I’m going to invoke Postman here. Brilliant. Okay. And there we have it. So this is what we want. We want instead of get, we want post to be selected here. And then we’ve got the path to the ad movie endpoint here. Okay. In order to test our result. And then we want to go to body here and then raw like that. Okay. And let’s just minimize that. Let’s go to let’s go back to our browser here. Open up a new tab. And I’m just going to go to GitHub here because you can download the relevant JSON data that we’re going to use to test our post request here from this location. We’ve got a file called add test movie doc. We’re going to add Highlander 2. One of the worst movies ever created. We’re going to add to our database. That’s just my opinion. Horrible movie. I really loved the first one, especially when I was a teenager, but this movie diabolical. Anyway, I’m going to copy the data for it and then we can just so you can copy it from this location from my GitHub repository, Magic Stream GitHub repository. Copy it and then paste it within the body of our Postman request here. So, it’s body raw and then copy it into this text area box here. Copy the data from GitHub here. Then of course we need to make sure that our serverside code is running. Our test serverside code is running. So let’s go back here and to run our code we just type go run dot. Okay. And it should be listening on port 8080. Excellent. And now through Postman we can test whether we can add Highlander 2 which is an absolute abomination to our database. Okay. So let’s do that. Let’s press the send button to send this data to our add movie endpoint. Let’s see what happens. 500 internal error. Oh dear. What’s happened there? undefined validation function min. Okay, let’s go to our movie model here and let’s check genre name min equals to two max equals to 100. Okay. So, let’s take out the gaps here. I think it is a bit funny with formatting here. It’s a bit temperamental with formatting. And let’s save that. Let’s try again. Go run dot enter. Okay, it’s listening on 8080 there. Let’s send that through. 400 bad request. Okay. Well, we’ve gone a little bit further. Validation failed. Key movie admin field validation failed on the required tag. Okay. Okay. Because we got no right. Okay. So, so we’ve already said that the ranking is bad. This movie was really really bad. So our field validation is a is working. That’s what that proves. So we go to 400 bad request which is correct because we hadn’t included any field value for the admin review field here. And now we have So this should work. Let’s send that through. 201 created. So we’ve created Highlander 2 within our movies collection. Okay. I’m not too happy with that. Oh, right. Okay. Right. All right. So, Highlander 2 has been created within our movies database. Let’s double check that. Okay. Reload data. And let’s see if we can find Highlander 2. There it is. But that doesn’t look right there. So, I’ll have to look at that. But it has created the has created a resource but it hasn’t created a unique identifier as I would have expected. So I need to just have a look into that. Okay. So the reason why that ID within compass was just a series of zeros and not what I would have expected to see. for example, a unique ID like this is because I hadn’t included a particular keyword within the back to characters here. So, we need to include this keyword here omit empty there in the bon and I’m also going to include it in the JSON. So if we include that in the bon the bon criteria here it will insert a unique identifier within the database if the ID field does not have an does not store a value when it is inserted when that data is inserted into the database. So this will create a new unique identifier like this by default for us. Okay. So, let’s take that there. Same code. Omit empty like that. And place it here also like that. There. And now if we test our code again, let’s type go run dot to run our code. Okay, great. It’s listening on port 8080. Let’s go back to Postman. We’ve still got everything set up here and ready to go. I actually deleted the previous record offscreen. I deleted the Highlander that we had here. So, we’re deleting a new So, we’re inserting a new record into the database, and it should include a unique object ID for this resource here. So, let’s test that. Let’s press the send button. Okay, we’ve got a 200 created status sent back to us, which is great. And inserted ID, and that’s what I would have expected to see. So it’s created a unique identifier for our resource within the MongoDB database. So let’s check that. If we go to compass and let’s refresh. Let’s reload data. And there we have Highlander the quickening and it’s got a an appropriate unique identifier now included here for the ID field. Excellent. So our code is now working. I’m actually going to remove this particular validation from admin review because initially when we create the resource we actually don’t need this to be to have a value. So I’m going to take this validation away from this field here. And you’ll see why I’m doing that in just a bit when we create the the code for using the AI functionality to read the admin review and extract the ranking from that admin review using AI. And we’ll see how that works in just a bit. Excellent. We’re in good shape. Okay. So, when a user first accesses our Magic Stream website, on the homepage, they will see some of the movies that are available to be streamed. However, when they click one of the movie items on the homepage, they will be prompted with a login screen. For example, we click on Shaw Shank Redemption. Here, we are prompted to log in because we can’t use their services. We can’t use the services provided by Magic Stream without authenticating first. So, we have to first log on. So, I’ve actually seeded the users collection with a user named Bob Jones. So, if I go bobjones@hotmail.com, that’s Bob’s email address, and I enter his password, and I log in, we can now we can now stream the Shaw Shank Redemption. And this is just a trailer played through YouTube, so it’s not obviously the actual movie. The gun and then stop to reload. So, So, if we go back here. So, now we’re we’re able to actually stream the movies. And of course, I’m just playing the trailers through YouTube to that acts as a placeholder for the actual streaming service. Great. So, let’s log out. Anyone can see the actual movies provided by Magic Stream. But if you want to stream one of the movies, you have to be authenticated. The user must firstly sign up or register for a Magic Stream account. The user must firstly be identified by the users’s registered details, the user’s credentials before the user is able to make use of the services offered by Magic Stream. So the user must log in firstly to the website and be authenticated based on the user’s authentication details which will be the user’s email address and a password that the user chooses when registering with Magic Stream. So if we log in here, we don’t have an account. We can click this link here to register and we have this registration form presented to us. So we can also access the registration form through this button register and we can register our relevant user details and our credentials for example our unique email address and a password here. So the first step in creating this authentication functionality is to create a model representing user registration details. So let’s go to Visual Studio Code and let’s create within the models folder here. Let’s create a new file and let’s call this user_model.go. [Music] So this is the file user_model.go that we’re creating in order to include a user strct which will represent a users model. a user model that will contain the users’s details, the user’s authentication details and general details. Even though we are in a different file to the movie_model.go file where code within the movie_model.go file is part of the models package. We can also make any code we write within the user_model.go Go file part of the models package simply by including this declarative code package models [Music] like that. And you can see we’ve got the same declaration at the top of the movie_model file here. Package models. So anything we write in here will also be part of the package part of the package named models which will make it easy for us to import whatever exportable code we have here into other packages and other applications. Okay, great. So all exportable code for example a model like the movie model where its name starts with a capital letter can be imported and used by another application or from within another package. For example, if we go to the movie controller.go file the movie_controller.go file we can see here we are importing the models package. So we have the root path here which we established within the go.mod file by naming the module with this path on GitHub that where we will upload our code to eventually. So in movie controller we have that as our root path the name of the actual module and models is the package name that we are the package that we are importing here so that we can use for example the movie model within our code. So when we create our user strct we’ll created it pretty much in the same way the same basic way that we’ve created the movie strct. So firstly let’s create an import block here and then we’ll create our user strct. Okay. And we need to import the go. MongoDB.org slashmongo-driver/v2 slashbson package like this. and it’s got a red squiggly line because we’re not yet using the relevant functionality within this package. Okay, so let’s create our user strct and we can do that by typing type user strruct like this. Oops. And then within the curly braces, we’re going to create our first field, which is an ID field that uniquely identifies a particular user within the users collection. And we’ll type this as bon.object id. Excellent. So the reason we need to import the go.mongodb.org/mongo-driver/v2/bent or/mongo-driver/v2/bon package is so that we can make the id property of the user strct of type bson.object ID. When a new document is added to a collection in our magic stream movies database, a unique identity is automatically generated for that document. So this is what this ID field represents in our user model. A field that uniquely identifies the document in the collection. So this is a field that uniquely identifies a user resource in the users’s collection. Let’s also create a field that uniquely identifies the user. So let’s create a field named user ID for this purpose. So user ID and this is of type string. This field is of type string. Let’s create a first name field like this of type string and a last name field like this also of type string. Let’s create a field named email and this is also of type string. Let’s create a password field also of type string. Note that we will include hashing functionality to obuscate or hash the password before it is entered into the database. It is of course not good practice in terms of security to save the password as is to the database. Let’s create a field named RO and this is also of type string. So this is also of type string but must either contain a value of admin or a value of user. We’re only going to have two roles available. So we could extend the role to enable multiple types of roles to be stored in the ROS field. But for now we are going to restrict the values to either admin or user for this field. Of course only administrators will be added to the admin role which will mean they will have administrative privileges to for example in our application to add reviews to movies. A user is only able to stream movies and in our application will not be able to review the movies. I’ve deliberately made it like this for simplicity, but you can of course extend the functionality so that the users can enter reviews for the movies. So for auditing purposes, let’s create a field named created at like this. And this field is of time dot time like that. And you can see that we’ve got a red squiggly line here undefined because we need to import the relevant package. And we can do that here like this. And there the red squiggly line has disappeared. Then let’s update our model with the update at field which is also for auditing purposes. And this field is updated when a user’s details are updated. For example, if the user’s password changes etc. So let’s create that field. So update at and this is also of type time dot time like that. Brilliant. Then let’s create a field named token and this is of type string. We must have it with a capital letter because this is exportable code. So token string like this. Excellent. We are going to use JWT or JSON web tokens for authentication and authorization purposes in this application. So we are going to persist the values for the relevant tokens within the document that is represented in code by this user strct. A JWT JSON web token is a compact URL safe token used to securely transmit information between parties as a JSON object. It’s commonly used for authentication and authorization in web applications. A JWT has three parts separated by dots. A header part which specifies the type of token and the signing algorithm for example HS 256. Payload contains the claims user data or metadata like user ID roles or expiration time. Signature verifies the tokens authenticity using a secret key or public private key. So let’s look at how JWT is used for authentication. A user logs in with their credentials. The server verifies the credentials and generates a JWT containing user information. The JWT is sent back to the client, usually stored in local storage on the client or a cookie. We’re going to firstly look at storing the the JWT in local storage, but we’ll discuss why this is not the most secure solution. And then we will eventually store it in what’s known as a HTTP only cookie, which means the cookie cannot be read on the client by JavaScript code, making it more secure and less and less susceptible to XSS attacks. We’ll look at the details of this a bit later. For future requests, the client includes the JWT in the authorization header. This is in the case where the token is stored on the client and then is added to the authorization header when for example a request is made to a web API endpoint. But we are we we’ll first initially look at this technique. But as I said it’s not the most secure technique. We’ll eventually we’ll implement our code using HTTP only cookies which is a much better and more secure way of passing the tokens from client to server. For example, the server verifies the token signature and grants or denies access based on the embedded claims. The benefits of using a JWT are as follows. Stateless, no session storage needed on the server. Scalable, easily used in distributed systems. can be passed between systems and services securely. Then it is good practice to also create a refresh token so that the client is able to authenticate without the user needing to log in again once the token’s expiry date is reached. We’ll discuss this further later in the course. But let’s for now just include a refresh token field like this. And this is of type string just like the token field. And the last field stores an array of our userdefined type genre which is based on our genre strct that we created within our movie_model.go file. And you can see it here with all its validation that we also added in these tags here. And we’re going to use this within our user_model.go file. And the reason we can just use it straight away is because this code here is part of the models package just like the code in the movie_model.go file. So we got package models and we got this declaration at the top here. So we can just use that strct automatically here because they’re part of the same package. So let’s do that. I’m going to call this field favorite genres like this. And this is an array of genre strcts. So we can just automatically include this type here because our code here, our user strct is part of the same package. Even though they’re in separate files, even though the genre strct is in a separate file to the user strct, we can access the genre type because they are part of the same models package. So it stores an array of genres. So when the user registers to use our magic stream movies application, the user will be presented with a list of genres and the user must select one or more of these genres to provide our application with insights so that our application can recommend movies to the user that the user is most likely to want to stream. We’ll discuss our AI recommendation feature a bit later in the course. So let’s quickly look at the registration form for the prototype app that I created when I while preparing for this course. So you can see here when the user registers with the system they can select their favorite genres and this will become part of the recommendation functionality that we’ll add a little bit later where um this whatever they’ve chosen here will be instrumental in how we recommend movies to a particular user. So I mean if we log on as Bob Jones for example joneshotmail.com like that there if we log on you can see that if we go to the recommend recommended tab here it’s recommended five movies and in part this is due to the genres that Bob selected when he registered with the application. So you can see for example he’s more than likely uh when he registered he selected comedy, sci-fi, thriller for example or drama or fantasy. And you can see it’s ordering the recommended videos in terms of sentiments. So excellent and you can see okay. So the rest are okay. So it’s ordered by excellent down to the okays here. But if for example if Harry Potter had a bad sentiment that would be displayed after airplane which has an okay sentiment. So this is how we are going to recommend the movies to specific users. But we’ll look at the details of this in just a bit. It’s quite an exciting part of the course because we’re going to be using AI to extract the sentiment from the movie reviews. But we’ll see the details of this a little bit later. Okay. So let’s minimize that. Let’s go to let’s go back to our code here. And we want to include validation code for each of these fields. So firstly, let’s open our back tick characters here. Oops. Okay. And now for the JSON, we want the field to look like this. So it’s just underscore ID. So in the JSON that gets sent back to the client, the field will be underscore ID. Okay. And then we’re going to include this omit empty keyword here. And then we’ll get back to what that means in just a bit. So these need to be wrapped in quotations here. And then let’s address the BSON format. So let’s include BSON here. And this is the way the actual field is represented within the database. So it’s underscore id. The field will be underscore id like that. And now if you’ll recall we need to include this omit empty keyword. So if for example just these fields are included in the client’s payload which is what we’re going to do. So it’s just these fields and no ID is specified. MongoDB based on this keyword here will automatically create a unique identifier for the relevant user within the database and that’s what we want and that’s the reason we’re including omit empty here right so let’s include the let’s include the back characters next to user ID and we want so let’s say the JSON we want the JSON for the user ID to be user ID like this. Okay. And the BSON will be the same as the JSON in this particular case. So we want within the JSON, we want within the BSON, we want the BSON field in the MongoDB database to also be user ID like that. Brilliant. Okay, let’s include the back tick characters next to the first name field. So Jason, we want the JSON to look like this. first underscore name and the BSON is the same. So in all of these fields, the BSON and the JSON are going to look the same. It’s just the way we’re setting it up, but they could look differently if we chose to do so. And then let’s address the validation for the first name field. So include validate keyword here colon and then within quotations let’s include the required the required uh keyword like this comma min equals 2. So at least two characters must be stored within this field and max 100. So we don’t want more than 100 characters stored in this field. So that’s our validation for the first name field and let’s just make a copy of that because the last name field is very similar to the first name field and let’s just paste that here for the last name field like this and let’s just change the bits that we have to last name last name and the validation is exactly the same required min= 2 max= to 100 and then the email field let’s do that. So, let’s include our oops, let’s include our JSON criteria here. And we want the JSON field to be email with a lower case like that. And then the BSON we want also to just be email like this. Oops, like this. I mean, okay. And then um let’s include the valid date keyword. Okay, this is a required. So there must a user must provide a valid email or must provide an email. That’s what the required keyword signifies. But it must be a valid email. And this is what email signifies. So always remember that I think gaps may introduce an error. So we must remove the gaps like this. So these two must not have gaps just be comma delimited like that. Great. Okay. And then for the password let’s include the declarative rules for the password. So JSON password will just be oops will just be password like this. Okay. VSON also just be password like that and then validate will be required and let’s just keep this simple for now. The password must have a minimum of six characters. Okay, so very simple for the password and then the ro. So this one will be a little bit more different to the other fields and you’ll see why in just a bit. So JSON and we want this to just be called roll like that and then bson going to be the same roll like that and then validate keyword. Now this is interesting. So we want the validation to validate that this value is either admin or user. So to do that we can use the one of keyword like that. One of like that equals admin space. So this is space delimited for the one of keyword or the one of criteria admin and then user like that. So it can only be one of these two values included within the role field because we only have two roles currently in the system admin or user. Okay. So the next one is created at and we’ll just keep these ones quite simple. So we just go JSON and then let’s say created at like that. And then the BSON is very similar to the JSON bon. Whoops. BSON created at and that’s all the validation we want to include there. Let’s make a copy of that. So no validation but we want it we want the JSON to look like this and the BON to look like that. And then for this one we obviously need to change the way the field will look in JSON. So updated at all lowerase updated at all or lowercase for the BSON. So the BSON and the JSON once again are exactly the same. Okay. And then for the token I’m actually not going to include any validation for the token. So we’ll just include the way the token field should look in JSON and bon token. And that’s pretty much the same. Oops. So this doesn’t have back tick here. We need to put a sorry back tick there. And then of course we need to name this appropriately. Refresh token. Copy that. Refresh token for the bon. And then here for the genre, this also requires special sort of validation because we’re actually going to be diving into the relevant genre strcts that will be stored in the favorite genres field here. So the validation of importance here is the validation that we created on each of the fields within the genre strct. So let’s firstly include what the JSON will look like or the JSON sent back to the client. So we want this to be called favorite genre all lower case favorite genres all lowerase and then the bison will be the same. So this is how it will appear in the MongoDB database bson like that there. So this field can’t be empty. So let’s include required then comma. Now this is the important part dive. So it dives into the value stored within the array and it val validates each genre which the validation criteria has been set up. Of course not there here. That’s the validation that will be used to validate the genre, the various genres stored in the favorite genres array. So just to go over this again, the omit empty tag is used to indicate that a field should be omitted when serializing if it has the zero value for its type. So also remember when we tried to add a movie to the movies collection in our MongoDB database, the underscore ID field was set to a series of zeros because the relevant ID field did not include the omit empty tag. This keyword omit empty will ensure that if when we add a user resource to the users collection that a unique ID is automatically provided for the newly added user resource to the ID field. So when we add a client payload for a user’s resource, we’re only actually going to be including these fields. So we won’t include a value for the ID field. So we need this omit empty tag to be present so that MongoDB will automatically add a unique a unique identifier to the relevant user resource within the MongoDB users collection. Great. And that’s it. We’ve created our user model. Excellent. Great. So now we have created the user model. Let’s create the code for inserting the users’s details entered during the user registration process into our Magic Stream Movies MongoDB database. Once we have finished the code for inserting a user’s registration details into our MongoDB database, we’ll test the functionality with Postman. I feel that in the previous sections of the course, we covered quite a lot of theory and have gone over a few detailed explanations regarding the code we are writing. So this part of the course is going to be more about implementing code and we are going to move at a fairly fast pace here so that we can get the code written and tested for adding a new user to the users collection within our MongoDB database. Right, let’s get going. Right. So, let’s add a file to our controllers folder and let’s call this file user_ontroller.go. At the top of the file, let’s declare the code that will be added to this file as part of the controllers package. So, let’s type package controllers at the top of this file like this. Excellent. So now the code within the movie_controller.go file is part of the controllers package 2. You can see we have declared that the code within this file the exportable code within this file is part of the controllers package and we’re going to and we’ve done the same within the user controller file here. So any exportable Kobe we write here will also be part of the controllers package. Let’s include an import section like that. Let’s import the following packages. So let’s import this package. gethub.com/gavlon [Music] digital slashmagic stream movies slashserver slashmagic stream movies server for oops forward slashmodels like that. Okay. And it’s got a red squiggly line because we’re not yet using the relevant model within our code which will be our user model. Okay, great. And we can just check that that path is correct by looking in the go.mod file. And you can see the root of that needs to be the same as this here. The root of that path needs to be the same as the name of this module. So if we go back there, we could actually just do this. Paste that in there and then for slash models to make sure that that’s 100% accurate. Okay. Okay, we are going to need a reference to the Genonic web framework package. So, let’s include the gingonic web framework package within our import section like this. github.com/jin dash Whoops. gonic/jin like that. Excellent. And we the red squiggly line is appropriate at this point because we’re not using any of the functionality quite yet within the Genonic package. We are going to want to validate the user registration data passed in from client to server. So let’s include the github.com go playground validator v10 package. Okay. So, github.com/go-playground. [Music] Okay. SL validator slashv10. Great. Excellent. and it’s dimmed out and we’re getting red squiggly line under the package reference here because of course we’re not using the validator just yet but we will do in just a bit. So we’ll add the other import references as we develop the register user endpoint handler function. So let’s create the basic structure for the register user endpoint handler function like this. So let’s type funk register user. We want this to be exportable. So the first letter in the name of this function is capitalized the R and then let’s include open and close round brackets and then jin whoops dot handler funk to declare this as a handler funk an endpoint handler function like that a jinonic endpoint handler function if you like and then let’s return an anonymous function that we’ll implement where we’ll implement our logic for this handler function and we want to include the C parameter which is of type jin.context which will help us implement the ginonic functionality. So for creating requests returning responses etc. Let’s declare a variable named user of type models do user which is of course the user strct that we implemented that we just implemented within the user_model.go go file this user strct here and the next step is to create an if condition if C that’s the generic context dot should bind shouldbind JSON is the one we want should bind JSON so let’s select that there we include the amperand user because we want to reference a particular address in memory so where that reference it’s a pointer to memory rather than than the actual data at at this point. So we need to pass a reference to a memory address rather than the actual data. That’s what the amperand means preceding the user variable name there. And then colon and let’s include an object there and an if condition. So if it’s not if the variable is not equal to null that means an error occurred during the binding process. So let’s send back an appropriate message, a HTTP status of status bad request to the client and then an appropriate error message. And we’ll use Jin to help us send back oops nil should have one L here and we’ll use Jin to send back well-formed JSON to the client. So error and then colon and then let’s send back an appropriate message invalid [Music] input data. So if it can’t bind successfully here it’s invalid data and then of course we don’t want code to continue execution because the handler function would have failed at this point. So we include the return keyword which makes sure that the code stops at this point once the relevant error message has been successfully returned to the client. Okay. And then let’s create a new validator. So to do that we go validate and then colon equals the assignment operator. And it also declares a variable of the relevant type at the same time. That’s why we have a colon preceding the equals there. So we go validator dot new like this and then we do we check we check an error object if the validation so validate strruct if the valid if this validate code fails it will return an error. So we need to include an object here and then user our variable name. So this code will return an error value and assign that error value to this variable here. And then we can continue the code by adding a semicolon here and checking the for a value to see if an error has actually occurred during this validation. process. Okay. So if it has, we want to output an appropriate error message to the client. So we want the status to be HTTP dot status. So it would be a bad request if the inputed if the client user data passed to this handler function fails validation. We deem this as a HTTP bad request. So we send that back to the client as well as an appropriate error message. So jin.h. So we can include well-formed JSON. So we can send well-formed JSON back to the client if indeed the validation fails. Let’s include the main error message which will be validation failed. And then we also want to include the details of why the validation failed. So details like this. And then we can include this code error. And then the error object will have an error property that we can leverage here in our code. So it’s an error method. It’s an error function. So it will return an appropriate message which will contain the details of why the validation failed. So we can send that back to the client and then we include the return keyword here to end execution of the function because the function would have the handler function would have failed at this point here. Okay, excellent. And now a very important part of the code before we insert because if it’s past validation we want to insert it into the database into the users collection but it will contain the user’s chosen password. The user variable will contain the user’s chosen password and we don’t want that password to be saved as is to the user’s collection within our MongoDB database because that’s a security risk. So what we want to do is hash the password. So we’re going to use a particular package to hash that password. So let’s install the relevant package. But we first need to make sure that we’re in the root of our application where our gomod file exists. So we go cd server and then mad stream movies server like that. Check that that’s correct. Yep. And then let’s press the enter key. So we’re in the appropriate directory and now we can use the go get command to install the relevant package that we’ll leverage in order to hash our password before the user data is saved to our database. So let’s go let’s uh use the goget command. Go get golang.org org/x/crypto slashb crypt corrupt crypt not corrupt okay so bcryptrypt so golang.org/x/crypto org/x/crypto/bcrypt. Press the enter key. Okay. And it’s doing its thing. It’s installing the relevant package. Downloading go.org xc crypto the appropriate version. Excellent. Patience is a virtue, I’ve been told. So, we’re getting there. And then we’ll be able to you leverage the functionality within this package to encrypt the password which has been passed through within the user data passed in from the client. Okay, great. So let’s clear the screen. And now let’s create a function called hash password that will hash our password that will encrypt our password before it is saved to the users collection. So funk hash pass word. This is the name of our method and we’re going to pass in whatever is passed in through the client from the client to the server and it’s a string value and this returns a string and an error object. So that’s what we’re declaring here. Here’s our return types, a string and an object. Let’s open the curly braces like that. And let’s go hash password. So this is one of the return values and these are the return values from our B crypt dot generate from password and then we want to include an array of bytes. So we’re type casting the password into an array of bytes like this password which gets passed in as an argument here and then we tell it what we want to it to do. So b cryptdefault cost. Okay. And then if so, we’re checking the error. If an error occurred during this hashing process, we want to handle it. And let’s return. So we’re not we’re going to return an empty string here for this type here if an error occurs as well as the error object itself there. And then if all is successful, we want to type cast the hash password into a string. So we can do that with this code string hash password. And the error that we’ll return this object here should be null. So we can just return null like that. And that’s our hash password code written. It’s as simple as that. So we can now use that within our register user code to hash our password before we save the relevant user data to the users collection. So let’s go back down here and let’s implement the relevant code. So we go hashed whoops hashed password, colon equals 2. And then we can call our function that we’ve just written and pass in user password like that. And that’s the password that the user of course has passed in to our server code here that needs to be hashed before the user data is saved to our database. And now let’s create the context that helps us clear up resources for this function. resources created to interact with our MongoDB database. So we’re familiar with this code now because we’ve implemented quite a lot already in this course. So with timeout and then context dot background and then 100 times. So it’s going to time out. The code will time out in 100 seconds. So if something goes wrong, the resources will still be cleared up in one. And of course, we need to include the defer cancel code here like this. Okay, brilliant. And we haven’t included the time package. So we need to do that. Let’s include the time package here. Time. And it’s seems to have automatically added context and net/http automatically. So make sure you also include these imports within the import section here. Great. So that’s excellent. And then the next thing we want to do is make sure that the email address passed in for the particular user for the specific user is unique i.e has not been saved to the users’s collection. So everybody’s so members of the magic stream website must have a unique email address if they want to register with our system. So we need to check that we need to validate that. So to do that we firstly need to include the users collection. So if you’ll recall we’ve got this code here which which opens the relevant collection. So this is the movies collection in this particular case, but we want to open the users collection within our MongoDB database so that we can save the relevant user data to the users collection. So I’m just going to make a copy of that because the code is very similar and then just change the relevant bits. Okay, so let’s do that just outside of any particular function. We can include that code and we’ll go user collection like that. MongoDB collection. So we actually need to include our MongoDB driver for Go packages here in order to make use of this code. So let’s replace that with users because we want to reference the users collection here and we need to now import the relevant packages here. So we can actually copy the relevant code here to do that. So we’ve already included the relevant importation code here within the movie_controller.go file. So let’s just copy that code. Go back to the user controller.go code and include those package references the import the relevant importation code here. Right? And you can see when I saved that it actually automatically imported our database package and that’s the database package that we created based on the code that we written here. We have a reference to database here. So when I saved it Visual Studio Code actually did the automatically included the relevant importation code within the import section here which includes our database package. So if we look at our database package here, now all the exportable code within this package can be leveraged from within our user_controller code. And that’s what we’re doing here. That’s what we’re doing here. So now that we’ve got the user collection variable, we can use that to interact with the users collection within our MongoDB database. So we want to count how many documents contain the email that has been passed in from the client to server. So passed in for the relevant users details from the client to our server code here. So we’re going to now leverage the users collection variable here. We don’t want replace one. We want to leverage this method count documents ctx. So we include our context here so that the resources will be managed appropriately based on the code that we’ve included here in this section. Then we can filter our data using this code. So bon like that dot m like that. And I think we need to include another package here. Yeah, we need to include another package. So, let’s go back. Let’s go to the movie_controller go code here. Oh, no, we’ve already Okay, so we have included the BSON package. No, we haven’t. Oh, okay. So for some reason it Okay. So when I saved the file, it actually removed that package automatically that I had included here because we’re not leveraging any of its function. We weren’t leveraging any of its functionality at that point. So I’m going to just copy that package again to the user_controller file here. Like that. And let’s save that. And now that we’re using it, it won’t delete it. Okay, so we can bon oops bon m and we want to use bon.mm to filter our result by the email address passed in from client to server. So we go user email here right. Okay. So we need to as always check the error if not equal to null. We need to handle that. So c.json JSON http and this will be a status 500 internal server error. If this code has failed, we want to throw a status internal server error 500 HTTP error and send that to the client with some well-formed JSON giving a bit of context to the error scenario. Okay, so error and then user already exists. So they got to choose a unique email address. Okay. And then let’s include the return keyword here so that it halts execution of this handler function. Brilliant. And then the next thing to do is just Oops. No, that’s the wrong error message. Sorry. So this error message is failed to check existing user. So sorry wrong error message. So failed failed to check existing user here. So if it fails at this point, it’s actually failed to check the email. So the code itself has failed. It hasn’t actually performed the it has it’s been unable to to perform the appropriate count of email addresses currently saved to the users’s collection. So this is an appropriate error message. Okay. So now we’ll check to see if the count is greater than zero. And so if the count is greater than zero, we cannot add the user data passed in because the user’s email address already exists within the users collection within our MongoDB database. So let’s now include an appropriate status which in this particular case will be HTTP status conflict because the email address conflicts with an already existing email address in the users collection jin.h H and now this error will be user all ready exists like that. Okay, so that’s where we’ll include the user already exists error if the count is greater than zero meaning that that email address already exists in the database. And then of course we want to return we want to include the return keyword here so that the code holds because this handler function would have failed at that point. Great. Okay. And then I’m going to include a unique user ID and I’m going to say user ID equals to bson dot we’re going to leverage the new object new object ID.hex hex method to just create a unique user ID for the user. Okay, great. And then usercreate at will equal to time dotn now. So we’re just going to include what the time is now for this particular field at this point at the point where the user data is being added to the users collection. And the user do update at property or uh field should also be set to whoops not that. Copy that time.now. Great. And then lastly, we want to add it. Add the user data. If it code gets to this point, we’re in good shape. And we want to use the user collection variable and the insert one method to insert the user into the database. Okay, brilliant. And then we need to check the error object. If error is not equal to null, yeah. Oops. If is not equal to null, let’s handle the relevant error. So c.json http status in internal server error and let’s include let’s use jin gonic to include an appropriate error message. So it’s going to be error colon and then failed to create user like that. And then of course we include the return keyword. So that execution holds at this point. And you can see there’s a red squiggly line under result because we’re not yet using the variable. And at this point we can return the result to the user to the client calling code like this. JSON http status created. So it’ll be an HTTP 2011 status that we’ll return to the client as well as the result there. And you can see the red squiggly line has disappeared. Okay. And one thing we didn’t do is we didn’t assign the hashed password. We hashed our password, but we didn’t assign it to the actual relevant user password field. So we need to do this equals to hashed password. So that was nearly a mistake there. Okay. So it’s got a warning. Let’s just check this value of error is never used. Ah, okay. So, we’re not checking the error at this point. Yeah. Okay. So, we need to check the error. Okay. So, if error not equal to null close brackets and we can that would be a status internal server error. So let’s just take make a copy of that and paste that here. Error unable to hash password and the warning squiggly lines, the yellow squiggly line disappears. And all looks good with our function. And that’s it. We’re in good shape. So next we’re going to test our function through Postman. Great. So firstly, let’s create a route for our register user handler function. Let’s go to the main.go file here. Let’s just create a duplicate of this. Copy paste and let’s map our register endpoint. So for slashregister is our endpoint here. And we want to map it to the function we’ve just created which is register user like this. There we go. Excellent. So we’re now ready to test our register user functionality. the functionality we just created here, which essentially saves a user’s registered details to the users collection within our MongoDB database. And we’re doing lots of other things here. We’re hashing the password before the users details get saved to the users collection. And we’re also checking for any conflicts in terms of the user’s email. Each user must have a unique email. Great. So now we are ready to test our register user functionality through Postman. So let’s launch Postman here. I’ve got Postman loaded here already. Let’s set the drop-down list here to post because we’re about to test a post request. Let’s enter the URL. So our end point is for slashregister. We’ve got the base address here already in place. And then we just need to include for/register which is the end point maps to our register user handler function. And then we just select the body option here and raw. And we need to populate this text box with the relevant JSON to register a new user. Now I’ve actually already created some sample data that we can use to test our functionality. So let’s go to GitHub specifically my GitHub repository. GitHub and then we want to go to so this would be github.com/gavlon digital and then we want to go gavlon digital magic stream here and then click on the magic stream seed data folder here and then I have prepared this data here. So add test user doc and all we want to do here is copy this code oh sorry this data here into our text box here and then to test our functionality we simply press the send button. Let’s do that. And of course that’s not going to work because I haven’t run the code. Okay, so let’s go back there and let’s run the code. So let’s go to our terminal window here and type go run dot. Now the code is running. Excellent. Listening on port 8080. And now let’s try that again. So let’s go to Postman. Press the send button. 201 created. That looks good. Ah, okay. We have a problem. So, we got a series of zeros here again. And why hasn’t that worked? I thought we had taken care of this bug. So, let’s go and look at our user strct and see if there’s anything funny there. Okay. User model. And if we look here, there’s a gap between this comma and omit empty. Remember this was the problem last time when we tried to save a movie to the movies collection. It did the same thing. It created a series of zeros for the ID and we want this to be a unique ID field. So what’s actually happening here is because the formatting here of these tags is very temperamental. It’s caused a problem because we’ve got a gap between this comma here and the omit empty tag. So let’s just remove that gap. Um, save that. I’m just going to cancel this by pressing Ctrl C. Let’s clear the screen. Let’s run the code again. Go run dot press the enter key. And now we’re running it again. And hopefully this is will resolve our issue and we’ll the result will contain a unique identifier for the user that we are saving to the database. But firstly, let’s delete the data that was created cuz it did actually create a document within the user collection. You’ll see here reload data, but this object ID is incorrect. This ID that uniquely identifies the user is a series of zeros and that’s not what we want. So let’s delete this record by pressing delete there. And then confirming the deletion. You see here document flagged for deletion. And then let’s confirm the deletion by pressing the delete button here. And now that record’s gone. So if we reload data, we can’t see that record anymore for Craig Denton. Great. So let’s go back to Postman. You can see I’m still running the code here. I haven’t stopped the code from running. And let’s try that again. And we’re all set up here. Our data doesn’t change. That data is fine. And see the password here. Password one exclamation mark. Now this password I’m using for all the users just to make things simple. So for all the users, you’ll be able to log on with this password here. But you’ll see when it goes to the database, it will be hashed i.e. encrypted as a security measure. You shouldn’t save the password as is to the database. So, we want this to be encrypted and um we’ve done that through this encryption code here. We’re actually hashing the password. We created a hash password function here using the b-rypt package here to hash the password. So when we look at the database, we want to see that the password has been hashed once we’ve saved the relevant data to the database, of course. So let’s go back to Postman and all we need to do, we’re all set up here. We’ve got the post drop-down set. We’ve got the correct path to the endpoint, the register endpoint, and the data we copied from GitHub. And we’re ready to test. So let’s press the send button here. 201 created. That looks good. But is our ID unique now? And it is. So that did fix the problem. And of course the problem was this silly little gap was caus a gap between a comma here and omit empty tag was causing the problem. It’s a bit temperamental in terms of formatting of these tags. So we got to be mindful of that with this logic here. Okay. Brilliant. So let’s take a look at the at compass to see if our data has been created. Our resource has been created within the users collection. So let’s reload the data by going view reload data. And there it is. And it’s got a unique ID here, which is fantastic. Great. So we’re in good shape. That’s worked. One more thing I’d like to test is if we now try to add that same record, we should get a conflict with the email because each user within the collection within the users collection must have a unique email address. And of course, we’ve just seen that Craig Denton with this email address has been saved to the users’s collection as it were. So if we send that same data to this endpoint, we should witness a conflict here. So it should send back a HTTP conflict response, HTTP response to us, uh HTTP status response and it and that will result in the data not being duplicated within the users’s collection. So we don’t want to save another document that contains an email address that already exists in the users’s collection. So a conflict should occur when we hit the send button here because this data contains an email address that already exists within the users’s collection. So let’s try that. Let’s press the send button. 409 conflict. Brilliant. So that’s working. user already exists as our error message sent in JSON format to us to the client code. So you can see that here 409 post it’s resulted in a 409 conflict. Brilliant. So the data won’t be duplicated if let’s just confirm that by going to compass here and we should only have one record for Craig Denton. Excellent. So our code that handles the conflict has executed successfully. And let’s look over here register user. You can see here the code for that count documents filtering on email. If the count is greater than zero, error user already exists. Brilliant. So now we’re ready to write the code to write the login code on the server side. Excellent. So now that we’ve registered a user successfully in the system, let’s create the user login functionality. So let’s first open the user_model.go go file here and let’s create a model for storing the login data that will be passed from client to server once the user has submitted the user’s login details in order to authenticate the user’s security details with the magic stream website. So let’s create a strct named user login. Here we do that by typing type. User login is the name of our strruct and then we need to include the strruct keyword here. Open our curly braces and we want an email field like this as string and then we can include the relevant tags for our email field. So let’s open back to characters like this. Include the JSON keyword here and then the name of the field that will appear within the JSON data and then validate. Let’s include some basic validation here. So it’s required field is required, email must be in a valid email format. And then let’s include the password field like this string open back tick characters. So the field will appear as password within the JSON data validate. So it’s obviously required, min equals to six. So must contain a minimum of six characters the password in order for it to be valid. Okay. Okay, so let’s create the user response strruct which will be a subset. It’s a DTO and it’s a contains a subset of the fields contained within the user strct. So let’s type type the name of the strct is user response like that and then of course the strct keyword open and close curly braces like that. This will not actually map to a BSON field. So I’m going to remove that. So the BSON code doesn’t need to be included here because it’s doesn’t map directly. It’s kind of a subset of the user strct and doesn’t map directly to a document or a collection of documents within the MongoDB database. So we don’t need that bon those that bon tag there. But yes, we do need the JSON tag to stipulate how we want our JSON data to look. Okay, perfect. So, right. So, within the user response, let’s create a field called user ID as string as string. Let’s open the back tick characters like this. And let’s include JSON and within quotations user ID like this. Let’s include a first name field like that. Camel case is what we’re using. Not camel case. We’re using Pascal case where each word has its first letter capitalized. First name. Each word within the variable name or the field name in this case has its first letter capitalized. This is Pascal case. Okay. String. And let’s open our back to characters and include the JSON tag here. And we want our JSON to look like this. First underscore whoops name. Let’s just create a copy of this. Paste it just below. And this will be last last name. Okay. And let’s alter this tag to be last name. And then let’s include a field named email as string. Let’s open back to characters here. JSON colon email like that. Okay. And then roll string open back to characters JSON. We want the JSON to look like this. Roll all in lower case. And then favorite genres. Okay. And this is an array of the genre type. This is our userdefined strruct that we created earlier in the course. Okay. And let’s include the relevant JSON tag here. And we want it to look like this within the we want this field to be named like this within the JSON data sent back to the client. This model is a DTO which stands for data transfer object. So DTO stands for data transfer object. It’s a design pattern used to transfer data between software application layers such as between a backend and front end or between services in a distributed system. Here are some key characteristics of a DTO. It holds data only. It typically contains fields, properties, getters and setters and no business logic. It is a flat structure usually simpler and flatter than domain models. It is serializable designed to be easily serialized to JSON, XML, etc. for transmission over the network. Some common use cases are as follows. API responses returning data to clients without exposing the internal domain model. So in this case the internal domain model would be user and this is our DTO user response data aggregation combining data from multiple sources into a single object input validation accepting structured input from external sources for example form submissions. So by using a DTO we’re only exposing the data that needs to be exposed to the client. Great. So let’s write the code for our login user HTTP endpoint handler function. So let’s open the user_controller file here and let’s create the infrastructure for our handler function. So let’s type funk login user like this then jin dot handler funk. So we’re using gingonic to establish this function as a handler function an endpoint function handler and we’re leveraging gingonic the jinggonic web framework for this purpose and then let’s return the logic for this particular handler function. So we do that by return funk and let’s open brackets c and then this is of type star jin. context like this. Let’s open and close curly brackets for our anonymous function. We’re now going to implement the anonymous function that we’re returning from the parent login user function. We need to make sure that whoops we haven’t got open brackets there. So we’re going to open that bracket there and close it down here. Okay. And then let’s create a variable that’s named user login. And this is now in camel case where user has a lowerase letter starts with a lowerase letter and the next word login starts with a uppercase letter. So this is camel case and we’re defining it as type models dot user login like this. And of course user login we’ve just defined within our user_model.go file. It’s a userdefined type that we’ve created. Okay, great. So we’ve created our variable user login. And let’s bind the data sent in by the client which should only be the user’s email address and password to the variable we just created named user login. Okay, so we go if so we’re going to check the return of a method provided to us by the context of jinggonic which is called should bind whoops bind JSON. So we’re checking the error of this binding functionality and of course the amperand user login points to a particular address in memory rather than containing the data itself. This refers to a pointer in memory and that’s denoted by this amperand character preceding the variable name. And let’s include a semicolon and then an if condition. If error, we’re just checking if the return value is null. If it’s not null, we need to handle the error. Should bind JSON. Sorry, this has to be capitalized. JSON. The red squiggly line’s gone away. Okay, so C.JSON, let’s handle the exception. http dot we want to send back a status of bad request here comma jin.h error and the message we’ll send back is input oh sorry invalid input data like that. Okay. Okay. And then we need to include the return keyword so that execution ends at this point here. The function would have failed at this point here. If there’s a problem with binding the data passed in from the client, which is the user’s credentials, the email address, the user’s unique email address, and the user’s password is passed in here. And we’re binding that data to our user login strct here. The variable defined as the user login strct here. And if it fails, we’re handling the exception there. So the next step is to find the user within our users collection within our MongoDB database based on the user’s credentials. Let’s define a variable named found user. But I’m actually going to set up the context for our query where we’re finding the user within the users collection. So we’ve done we’ve written this code many times and this is just for handling resources. So even if our query fails for example within this function that the relevant resources are freed up with timeout like this open brackets there and let’s include context dot background and let’s so we want all the resources to be cleared up in 100 seconds. So time dot second like this and then we also need to defer cancel with this code here. So let’s define a variable named found user as the models do user type. So we type var found user models do user like this. Okay. And then let’s include our query to our MongoDB database. So we can do that like this. So [Music] assignment operator users collection. Oops, we don’t want that. So it’s users collection. Or was it user collection? What have I defined it up here as? User collection. Okay. So it’s not users collection. And it’s user collection. User collection dot find one. We want to find one document within our user collection. Let’s include the context here. And then let’s filter our query m on the email the the user’s unique email which is being passed in from the client because the user is logging on in this scenario. So while the user so when the user logs on and presses the login button the user’s email address and password are sent to this function here and we are filtering on the user’s unique email data in order to find that user within the MongoDB database within the users collection. So find one email and then user login. We’ve bound email. We’ve bound the what’s inputed from the client, the email and the password to this memory address here. We’ve bound to this variable. So it’s pointing at a particular memory address with the relevant client data. And we’re now querying on the email address to find the data. And if it’s found, we want to decode that into found user. Our found user strct like that, our found user variable email. Of course, the comma is the issue there. So, let’s replace the comma with the colon. Okay. And then let’s check the error and handle it if an error indeed exists. So, error. So this is the error caused potentially caused by the find one functionality here where we’re trying to find the relevant user within the users collection. So if error null if error is not equal to null whoops open our curly braces and let’s handle that particular exception. So we want to send back HTTP dot status unauthorized. Okay, we can’t authorize this user because we can’t find the user within the database. And then let’s use jin.h to hand to send back an appropriate error in JSON format to the user. invalid email or password and then let’s include the return keyword here. So that execution is halted at this point. So the function would have failed. If this method fails, it’s pointless carrying on with the execution of this code. So we handling the exception and sending back an appropriate error message to the client. Invalid email or password. Great. So the next step is to compare the password passed in from the user during the login process to the user’s saved password within the MongoDB database. But of course the user’s password is stored in the database as encrypted data. So we can actually see that if we go to compass, let’s look at our users collection quickly before we carry on writing the code. Let’s look at our users collection quickly. So let’s go to Magic Stream Movies. If we go to the users collection here, you can see that the password is hashed. So this is pretty meaningless to a human. You the user can’t log in with this password. It’s deliberately being encrypted to increase security. So nobody can steal the password if they somehow get access to the back end to the data store here. Okay. But we can use a particular package the brypt package to compare the hashed password with the password passed in. And let’s look. I think we’ve already imported the relevant package. Let’s have a look up here. And here it is brypt. So we don’t need to install anything or import anything. It’s already done for us. We’ve used it before. So let’s write the password comparison code. So equals to b crypt that’s the package we’re using for this comparison code compare hash and password. So it’s doing it for us. So we don’t have to do anything write anything fancy because it’s already encapsulated within this functionality here within the package that we’re using here. So we want to type cast found user password into a bite array here. Comma and then this is the use this is what’s being passed in. This is the password that has been passed in from the client during the login process dot password and we need to also convert this into a byte array and we’re using this code to do that. So we’re comparing like for like here essentially compare hash and password and that takes care of all the details of comparing a hashed password to a to a password that hasn’t been hashed. So it’s been done for us using the brypt package here. Okay. And then let’s write the code to check the error if something went wrong during this hash comparison process. Nil. Let’s go here. So let’s type c.json and let’s pass back http unauthorized. If the password doesn’t match up, this user cannot be authenticated and we sending back an appropriate message to the user as well as a HTTP status of unauthorized. So error like that and it’s the same error message essentially as before. Invalid email or password like that. And of course we need to include the return keyword here so that execution halts at this point because there’s no point in executing any code that might exist after this code here because an error occurred during the comparison process. is the compare comparison of the client’s password that the client has passed in during the login process to the password saved to the relevant users document within the users collection. So it will fail at this point and no further code will execute. If no error occurred and the user’s password matches the password saved for the user in the user’s collection within the MongoDB database, the user is authenticated and our code must now pass back to the client an access token. So what we are going to do now is create code for generating an access token and a refresh token which are generated once the user has been authenticated. So the access token is analogous to a key that can open the doors to protected resources. So this is great for when you have an application with disparate loosely coupled components that need to securely communicate with each other. In our application here, we have two disparit loosely coupled components. A client web application front end which we’ll create later using React and a serverside web API component. This is the component that we are currently creating using go engine gonic. So when the user logs in and is authenticated an access token is generated on the server. This is functionality we are about to create. The token generating functionality if you like. The tokens are then passed to the client. The client code can then send the access token to the server along with the relevant HTTP messages in subsequent interactions and won’t need to provide the user’s credentials i.e. email and password with every interaction with the server because the access token can now be passed from client to server which means through the access token the server code will know as it were what authorization privileges the relevant client has in relation to the relevant endpoints some of which will be protected. So we are going to write the code for generating the access tokens within a separate file. So let’s create a file within the utils folder named token_util.go like this. Okay. So we’ve got the utils folder here. Let’s right click go new file and we are calling this token util like this go. Great. So at the top of the file let’s declare the name of this package which will be util. So we just type package utils like that. Let’s create an import section like this. So import and we’ve done all this before and then we just open round brackets like this and we can include our imports here. Let’s import the MongoDB driver for Go packages here like this. So we’re going to we’ve already imported these packages before. So let’s just go to the movie_controller file and just copy these packages, these package references where we’re importing these MongoDB for Go driver related packages here. Let’s copy that to our clipboards. Let’s go to token util here and paste the relevant importation code here. Excellent. Okay. Okay. And we’ve got red squiggly lines under here because we’re not currently using any of the functionality associated with these to these packages here. We want to also import a package that references our database functionality here where we’re connecting to the relevant database and opening a connection to the relevant collections here with this reusable code. So we want to include a reference or we want to import this package into our token util file here or our utils package rather. Okay. So let’s press enter here and then we’re already importing the relevant database related package here. So let’s just copy this. Let’s go back to our token utils file and paste it here. Great. And to generate the tokens, we need this package. github.com/golang-jwt/jwt/v5. Okay. So, we actually need to install this package. So, firstly, let’s navigate to where the go.od file is. So, cd server slash mad whoops. Yep. magic stream movies server like this and then let’s run the relevant goget command. So go get GitHub here’s the path to the package that we want to install com/go and we need this particular package JWT and we need this particular package for generating our access tokens our JWT or Jot tokens jwt slashv5 five and let’s press the enter key. That looks good. And hopefully that installation process will go as expected. And then we can proceed with generating our tokens or writing the code for generating our relevant tokens. And that looks great. Excellent. So let’s clear the screen here and let’s import that package now. So to import the package, we just include this line of code here. github.com/golangjwt/jwt [Music] slashv5 like that. Okay, so we’re creating a model here and we’re going to call this sign details. This is all part of generating our token. So just bear with me here and follow along please. So we got email as string whoops first name string la whoops last name string. And these details will all be encoded within the relevant JWT. This is why we’re creating the strct. Then we got RO. So this can be admin or user. So let’s define it as string. is the enter key. User ID as string and then lastly JWT dot registered claims. So we’re including a strruct within a structure. Basically this is the JWT strct within this package here. So actually we need to alias this here. So let’s include JWT here just for readability purposes. So we can reference that within our code here. So JWT dotregistered claims and we’re basically including another strct within a strct and that strruct resides within this package here. So our token is going to be generated from this information. email, first name, last name, RO, and user ID. And then registered claims, which is actually a strct that we’re including within our signed details strct here. JWT.registered claims is a strct provided by the github.com/golangjwt/jwt/v5 package. It contains a set of standard claims defined by the JWT specification RFC 5719. And these fields include the issuer, which is who issued the token, the subject, the subject of the token, usually a user ID, audience, the intended recipients of the token, expires at when the token should expire, not before, when the token becomes valid, issued at when the token was issued. ID unique identifier for the token can be used to prevent replay attacks. Okay, so let’s create code to read a secret from an environment variable. So to do that, let’s go to the env file and include an appropriate environment variable. And our secret key will just be secret underscore key equals to and we’re just going to call this your secret key. You obviously want to include something a little bit more a little bit better than this. But for now, this is just a placeholder to indicate where we are storing a particular secret key which will be used to encode our token to create our token as part of the token creation process and you’ll see how we do that in just a bit. Okay, so we’ve included our secret key here. Let’s go back to our token_util.go file here. So let’s create a variable called secret key as string and set it to our secret key which has now been set within our env file. Okay. So we go var C whit key as string equals to and then we need to include the OS package dot get env and then the environment variable that we want to read is secret key. So we can just copy that. Go back to our token util.go file and copy it there. So we’re now reading the secret_key environment variable and saving it in memory within this variable here which has been defined as string. Now we need to import the OS package. So let’s go up here and within quotations include OS like that. Okay. Excellent. Great. So, I’ve noticed it does this annoying thing when you save your file and some of these packages are not being used that it actually just uh Visual Studio just deletes them automatically. So, if I go save, it deletes them because they’re not being used. So, um that can be a little bit annoying because we haven’t had a chance to use the functionality within those packages. So I’m just going to undo that and just just be aware of of the fact that it will remove those if they’re not being used and you go Ctrl S to save the file. Okay. So I want to keep those packages there and we’re going to start using the functionality within these packages. So those red squiggly lines will disappear. Great. So let’s create the generate all tokens function which contains the parameters email as string first name as string last name as string ro as string user ID as string and returns signed token as string signed refresh token as string and as error. So let’s create that function definition. So let’s type funk like this generate all token. So it’ll be an access token and a refresh token. We’ll discuss what a refresh token is a little bit later, but for now let’s just create the definition for our function email. So, comma first name. And you can see all these parameters are established using camelc case last name ro user ID. Okay. And we can include string. So we only have to define it for the last parameter. And all of these parameters are automatically defined as string because we’ve defined the last one as string and we haven’t included types for the preceding parameters here within our function definition. And then open brackets we go string string, error. So we’re returning two strings here and an error object from this function. Let’s open curly braces like that. Press enter like that. Let’s create a claims variable and assign it to the science details structure with its field set to the appropriate values passed in to the generate all tokens function. So let’s go claims. So we’re defining a claims variable using the assignment operator like this which both defines the variable as the relevant type. So it infers the type as well as it’s also doubles as an assignment operator. So let’s include what we’re assigning this variable to and that would be amperand and we know the amperand is refers to a memory address signed details like that. open the curly braces like this email and then let’s assign that email field to the email value passed as an argument to this function. Okay. And then let’s go first name like this colon and then the first name parameter value. Do the same for last name. last name and then we want to assign the role like this and then user ID oops user ID and that should not be capitalized ID like that and then registered claims and we can actually define the registered claims that we want to include registered claims. So this is a strct and we can define what our registered claims should be here. So we’ve defined the strct we want to include and we’re going to include the relevant fields that we want to include within our claims here. So as you can see what’s happening with this token is we are encoding the users specific details into the actual token. Okay. So the issuer is the app that’s issuing the token. So we go magic stream. It’s not issuers, it’s issuer like that. And then issue issued at like this colon. Then let’s use our alias for our package that we recently imported. New numeric date time dot now. Okay. Okay. And let’s just make sure that time time should in fact be imported here. So let’s include the time package here before it complains. So we got time now and then comma here and then let’s go expires at and include the relevant expires at value like this time do now. And let’s add 24 hours. And this is when when it will expire. So it expires within 24 hours. We might change this a little bit later, but for now, let’s just say 24 multiplied by time dot hour. So it expires in 24 hours. This particular token. Okay. And that looks good. Excellent. And the underline there is because we’re not yet using it. Okay. And then let’s create a variable under this claims variable. Let’s create a variable called token. Token assignment operator JWT. I know this is very involved, but just bear with me. This is pretty much a standard way to generate the tokens. It’ll be worth it in the end. Okay. So new with claims and we’re now passing our claims which just contains relevant details about the user sign and this is the algorithm that we’re going to be signing our token with which is uh ES256 here and we actually want HS I think. So let’s go sign. So HS 256. So this is the algorithm that we’re using to sign the claims. And then let’s include the claims variable there. So this is our token generation code. And then we want a variable called signed token like this, cuz this method is going to return two values. then our assignment and declaration operator and we go token dot sign string. So now we’re going to sign what we’ve got in the claims with our secret key byte. So we are converting the secret key from a string into a byte array for this method to work. So sign string accepts a byte array. So we must convert the secret key string into a byte array. And that’s what we’re doing here. Great. So we’re getting somewhere. And then and then and then we do the usual go thing. We check for errors. So not equal to null. And this is the thing I had to get used to coming from C to Go is the way errors are checked. It’s very procedural rather than using try catch code like you would in JavaScript C++ or C. Okay. So let’s write return [Music] and then okay so if an error occurred we’re we’re returning nil nothing an empty string for the first one. So the token will have nothing in it there. And the refresh token will also be set to an empty string. So an empty string for the token, an empty string for refresh token, and then the error object which will be useful for the client code. So the client code calling this function will be able to handle the relevant error sent back in the case where an error occurs. Great. So then for the refresh token is very similar. Okay. Okay. So, we’re first just going to go to the N file here and we’re going to do a similar thing here. So, we’re just going to create a key for the refresh token. Secret reresh key like this. Your refresh key. Of course, you’ll probably want to include something a little bit better than this, but for now, it will serve our purposes. And let’s go back to this file here. And then all we what we can do here now is just duplicate this code for the refresh token. Okay, so let’s do that. So actually we can just duplicate this code. I know this isn’t very good, but just for now to get to the next step. So, we can just generate our tokens. We can always come back and clean up this code a little bit later. So, let’s call this refresh claims. It’s obviously complaining because we got duplicate variable names now because we’ve duplicated the code to generate the token. We’re generating the refresh token now and we’re duplicating the code that we created for generating the access token. So we just need to change these variable names so that all these red squiggly lines will go away. It’s obviously complaining about duplicate variable names. Okay. So then we can just change that there. We can call this refresh token and use camel case for our refresh token variable and then signed refresh token here token and then of course our secret key which we need to read here now. So we’ve called it something different. So go secret re refresh key. Let’s just double check what we called it there. See? Yep. Secret refresh key. And we can just paste that like that there. Okay. Token refresh token. That’s these red squiggly lines are just because we we’re not currently using the variable. And same with that variable there. Okay. All of that looks pretty good. So that needs to change to refresh token. Refresh claims signed refresh token. Okay, we’re checking the error there to make sure no errors have occurred during the refresh token generation process if you like. Okay, so then um checking the error. So if no errors have occurred, the only thing left to do here is return the token, refresh token, and hopefully a null for the error. But if there’s an error, the tokens will contain an empty string and the error will contain a value. But we’re hoping that the tokens, the signed tokens, the access token, the signed access token and the signed refresh token do contain values in which case the error will be null and no error has occurred. Signed refresh token null. There we go. Whil and now do we have any red squiggly lines? No. And so it’s cleared out some of our references here because I saved it. So that those were the MongoDB for Go driver related packages. Let’s cleared those out cuz I haven’t yet used those. But we will be using those because we want to save these tokens ultimately save these tokens to the database next to the relevant user within the relevant user document within the users collection. Okay. So let’s actually write the functionality for that now because we want to include it within this file. So we’re going to write the the actual database related functionality now. So I’m just going to bring back those package references. It’s a bit annoying that it just deletes them um automatically uh because we’re currently not using them when you save the file. But okay, great. And see I just saved it and it went away. So undo that. We got our packages here and I just want to open and it’s also deleted the database reference too which we need. So let’s go here database. So we just need this database importation code here and let’s copy that here. And then let’s open a connection to the users collection. Actually, we’ve got the code here already. Um, here. So, let’s copy this code here. Let’s go to token_youutos.go. Paste it there. So, now we are using our database package. So, let’s create the function to save the token. So, we we’ve generated the tokens in this code here. We’re going to be calling that from the relevant controller code. We’ll generate the tokens and then we want to update the users collection with the tokens, the relevant user document within the users collection. So let’s create our function. We’re going to call this function update all tokens. So let’s type funk update all tokens like this open brackets. And we can go user ID like that. Token like this, comma, refresh. Whoops. Refresh token like that. And then just define this last one as string. This last parameter string, which by default will automatically define these parameters also as string. And then we just want to return an error object. So go as error like this. Open our curly brackets and let’s write the logic for this. So firstly I’m just going to create the usual resource clearing code if you like the housekeeping code that when time when the timeout is reached it clears up all the resources created when using the forgo driver related code. So width time out. Then we want context. Oops, not tudo dot background. We of course need the context package imported which which is it’s done that automatically for me there. Okay. And then let’s establish the timeout. And let’s just use our usual 100 times time dot second for the timeout. Okay, so it’ll time out in 100 seconds. And then we need to include the defer cancel code here. Okay, brilliant. Okay, and then date at we don’t need it to return the second value here. So we just include a placeholder here which is an underscore character and then we want to go time dot pars time dot r and this is just the standard rf c 3339 like that. Let’s go time dot now time dotn now dotformat and then we want it in a particular standard format like this. Okay. Now we’re going to create a variable that assigns the relevant values to the relevant fields within our relevant user document. So update data let’s define those fields and we can go bson dot oops bson dot m like this and then use the set command like this which is dollar symbol set like that colon and then another bson m like this and then we can assign the relevant values to the database fields like this. Token tokens being passed in as a parameter value here and then of course refresh token like this. Okay, refresh token like that and then update at which we’ve established here and this is really for auditing purposes. So when a document is updated in the database, we have the timestamp next to it when it was updated and we can go updated at like that there. So we’ve defined which fields will be updated in the database and with which values. Okay. And we need a comma here. Great. So we’re using this variable to interact with the relevant database collection which is our users collection. So let’s include underscore. We don’t need a value returned for the first return value but we want the error returned if an error occurs during this update process. Let’s go update one like this. So we’re calling the update one method using the MongoDB forgo driver related functionality. So let’s include our context here. And then user I whoops user ID field. We’re filtering this particular update. We’re filtering on the user’s ID. So we’re targeting a specific document within the users collection based on the user’s ID. Great. And then the update data variable that contains how we want our document updated, the fields and the values for those fields. So this code here, this set command will set the relevant fields in our document with the relevant values. and we’re using the user ID to target a specific document. So it’s a specific document for a specific user and we’re updating that particular users tokens, the access token and the refresh token. So that’s what this update one functionality is doing here. And then the usual go thing where we check the error. Hopefully, it’ll be null, but if it’s not, we’ll just return the error to the client calling code. And then if it gets this far, we return null for the error. Like that. And that should be good enough to update our database. Okay. Excellent. So we’ve now written the functionality for updating the relevant document filtered on the relevant users ID and we are ready to create our calling code within the user controller. So let’s go back to the user controller and back to the login user function and we can proceed from here. So we can now call our generate all tokens method from within our login user function to generate the relevant tokens. So let’s firstly do that. So to do that create a variable called token and then refresh token like this and then the operator the assignment operator u utils dot and I assume it’s already imported utils for us here. Okay. No, we haven’t yet imported the utils package. So to do that, let’s copy that there. Enter utils like that. There. Great. So now we should be able to go utils dot generate all tokens like this. And then we can pass in the found users relevant fields like this. So found user email. So these are the fields that are used for creating the relevant tokens. So then found user dot first name found oops found user.loast name found user dot roll like this. and then found user do user ID. And that’s the last parameter we need to include there. And there’s a red squiggly line because we’re not currently accounting for the third return value, which is the error if one has occurred. Okay, perfect. So that looks good. And then we’ve got to go through the motions here and check the error. So if not equal to null. So that obviously means an error has occurred. We can handle that using the gingonic context. So go there http status internal server error and then we can return wellformed JSON using jin.h like this. go error and then fail to generate tokens. So hopefully the code doesn’t reach this point and the tokens are generated as expected. And then we use the return keyword at this point because we don’t want the code to continue executing. Okay. And then at this point in in the code, we actually want to update the tokens within the relevant user document. So to do that, we go utils and we wrote update all tokens. That was the last function that we wrote within the utils package. So update all tokens. We need to pass in the user ID. So found user user ID like that. and then token and refresh token token and refresh token like that. So that the relevant tokens are saved to the relevant user document within the users collection. Okay. And then once the user’s logged in, we want to pass the user response. Now this is the DTO we created earlier. So if we go to models here, user model, we’ve got this user response which with which is our DTO. And what I didn’t include in this DTO are the tokens. At this point in the application development process, we’re going to return the tokens. But in subsequent iterations of the function, we’re actually going to save the tokens to what’s known as HTTP only cookies. But for now, we’ll return the tokens to the client through the user response DTO. And I’ll explain more about why we are doing this for security reasons a little bit later in the course. So let’s include token here as string and we want the JSON to be straightforward just JSON colon and token and then we want refresh token which is very similar like this and of course this will be called refresh token and we want the JSON to look like this using snake case with an underscore separating the words and this is of course Pascal case where each word each word’s letter is capitalized. Okay. And I think that’s looking pretty good. And then we can go back to our user controller.go go file and we can return that using the genonic context. We can return that user response strruct to the user in JSON format. So to do that we type c.json. So if the code gets to this point all has been successful. We probably should be returning an error at this point and handling it actually. So let’s just go update all tokens. Let’s go to that definition. Yeah. So, we’re returning an error there. So, we need to handle that error first. So, let’s go back to our code here and let’s handle the error first. So, if error not equal to null like this. And how should we handle this? We should just handle it in a very similar way. How we’re handling it there. failed to update tokens let’s say here. Okay. So we’re passing an appropriate error and an appropriate HTTP status back to the user here which would be internal server error 500. Okay. But if the code gets here all is successful and we can return a HTTP status of okay. So http dot status okay like that comma models dot and then our user response which is our DTOR here and then open curly braces and user ID found user dot user ID first name this is what we’re returning to the client react code found user dot first name and then last name. Found user.ast name and then email. Found user dot dot email and then roll found. Whoops. Found user dot dot roll. And then favorite genres. We also want to return these found user. And you guessed it, dot favorite genres like that. But we also need to return our token. So token and we’ve got our tokens here. So token token like that. comma refresh token. Refresh token. And that sure that was quite a lot of code, but that’s how we generate our tokens. That’s how we authenticate the user and generate our tokens. And at the at present, we’re just going to send them back to the client. But later on we’ll look at how we can save the tokens to HTTP only cookies because it’s a more secure method of passing these tokens between the client and the server. Excellent. All right. So we now are ready to test our login functionality. But something doesn’t look right here. Looks like we got a warning here. Oh, okay. So I forgot to assign the return value from update all tokens to an error variable. So let’s assign the return value from update all tokens to this error variable here. And the warning underline has gone away now and we’re good to carry on. So now we want to test our login functionality, our login user functionality here. So to do that let’s first run our code. Ah, we got to do we haven’t mapped our um we haven’t mapped our login user handler function to an appropriate route yet. So we’ve got to do that first. So let’s go to main.go here. Let’s create a duplicate of this code here just below it. And let’s call this endpoint login. Our handler function is of course login user like that. And now we have mapped our login endpoint to log the login user handler function and save that and then let’s run our code. So to do that let’s do the usual type go run dot here and it should be listening. Yep, it is on port 8080. Let’s minimize that and then let’s launch Postman. We’re going to test this through Postman because we’re doing a post request here. We can easily test get get requests through um our browsers, but post requests we should use a tool like Postman makes it a lot easier. So firstly, we want to make sure that this is set to post and then let’s include the appropriate path. So the path to our endpoint is login, not register in this case. So let’s adjust this URL here to the appropriate path. So for/lo is our endpoint and this is of course the base address of our web API where it’s currently running on our local machines. And then we want to make sure that we select body here and raw. And now we can include JSON data that we want to include within the body of the HTTP message. So let’s open our curly braces and include the appropriate JSON data. Let’s just quickly go to compass here and look at our data. Okay, let’s connect. We want to look at our user data because we recently registered a user named Craig Denton. So here you can see Craig Denton, the user that we recently registered and the tokens are currently set to empty strings. So when we log in using Craig Denton’s credentials through Postman, these tokens should be populated with the appropriate access token for the token field here and the refresh token for the refresh token field here. So that’s what we want to do here. Verify through Compass that the login has been successful once we’ve tested the login user functionality. So let’s go back to Postman. Okay. And we’re all almost set to test our login endpoint. Let’s just go and get the the appropriate data from Compass. Okay. Um so let’s copy we can copy this email address for Craig Denton. So the email address within the users collection uniquely identifies each user. And let’s type email here colon to include our in fact that should be within quotations. So we’re including appropriate JSON here. And then within quotations, let’s paste the appropriate email address for Craig Denton. And then let’s include a comma here. And then we want to include Craig Densson’s password, which is he chose this password because we just want to keep things simple for testing purposes. So it’s just password. It’s obviously not the most secure password in the world, but for testing purposes, this is okay. And this looks pretty good. Let’s just verify that the JSON is appropriately formed for what we want to do here. So let’s go back to our code and let’s go to the user model user_model.go file here and look at user login the user login model. Okay. So email is validated as it’s got to be validated as a as a properly formatted email address. And then we’ve got required here. So email is a required field for the user login strct and password is required and must be must have a minimum of six characters and that looks good. So you can see by this JSON tag that our email and password are appropriately named or are appropriately named within Postman. Let’s go back to Postman. email, password, and we’ve got the appropriate values next to each of these fields here within our JSON. And I think we’re ready to test the login pretty much. So, let’s do that. Let’s press the send button and see if our login works. Look at that. 200. Okay. And we’ve logged in successfully. And we can verify that because it’s created our access token and our refresh token. And of course, this is just encrypted data based on the users’s claims and various settings that we went through in this section of the course a little bit earlier. So let’s go to compass to refresh the data. We go view reload data. And now we can see token refresh token have the appropriate values that has been returned from our end point. And you can see that within the result returns to us via HTTP from our login user handler function. Excellent. So now the user if this say was a JavaScript or React client to access protected endpoints. These protected endpoints haven’t yet been created yet, but we will soon be creating these protected endpoints. And we can use these tokens. We can use this access token to access the endpoint because this verifies the relevant user has logged on and been authenticated successfully. So can now access the appropriate protected endpoints. Excellent. Okay. So I’ve actually noticed that the access token denoted by the token field value here is exactly the same as the refresh token. So something is not quite right with our code. So I want to go into the code and just see what could have caused that. We want those tokens to be unique. So okay, let’s have a look. We want to go to tok to token_util.go and because we duplicated the code for deriving the access token, we duplicated it in order to create the refresh token because the code is very similar. I suspect that’s the reason why we’re getting the same token generated. So first of all, I can see here this should be secret refresh key and let’s replace secret key with secret refresh key because we are signing the refresh token and not the access token here. So let make sure that’s secret refresh key. And what else? Okay, here we could up the expiry at field. So, let’s just multiply by 7. Whoops. Multiply by 7. Multiply by time dot hour. We’ve got a week before this refresh token expires. And we’ll look at refresh tokens in more detail a little bit later. But I think that looks pretty good. Let’s just test that. That now is creating two unique tokens. One for the access token and one for the refresh token. So, I’m just going to run this code. Go run dot. Okay, great. It’s listening on port 8080. I’m going to go to Postman now and just run this again. Okay, and we’ve logged in successfully. And let’s just compare the refresh token. And you can see here that this is now different. This is a different value. So the refresh token and the access token are different. So that looks much better. And we did find some slight issues in our code that we’ve now corrected. Okay, brilliant. So, we’ve got the login functionality now sorted. Let’s move on to the authorization code. So, we want to create our middleware that will authorize the user, authorize the client each time the client makes a call to one of our endpoints. So some of the endpoints are not protected but others are protected in which case we need to check that the user has been appropriately authenticated. So this is the code we’re going to create now and we’re going to create that in our middleware folder here. So let’s create a file within our middleware folder and let’s name that file or underscore middleware like this.go go. It’s ago file. Press the enter key. And we’re going to make this part of a package called middleware. Excellent. And then let’s create the import section. Open and close round brackets. Okay. Okay. And let’s firstly make sure that we import our utils package. So to do that, we need to include the root path first, which is the root path of where the code will eventually be pushed to on GitHub. So it’s github.com/gavanlon digital. Of course, this will be whatever your GitHub account is called. So mine’s Gavinon digital slashmagic [Music] stream Movies slashserver slashmagic streammov server. I think that’s correct. Let me just double check that. So, if I go to go domod, we can see it here. So, it’s actually magic stream movie server. So, let’s I’m just going to copy that straight from there. And you can do the same to make sure it’s 100% accurate. Paste it here and then forward slash utils like that. And now we’ve imported our utils package. Brilliant. So, let’s create a function called middleware called O middleware. So, or middleware where like that. Oops. Open and close curly braces. And this is a jinonic handler function, but will not be used in the same way as the other handler functions are used. We’ll talk about the details of what I mean by this in a bit. For now, it is important to understand that this function will be used to validate the access token and grant access or prohibit access to protected endpoints. Jin. So we need to hook into the jin gonic functionality. So it’s a handler function and that means we also need to return an anonymous function like this that accepts an argument of type jin.contextc and we have star jin. cont whoops context like this. So, we’re hooking into the Genonic functionality here. So, we have access to Ginggonics context and you can see that the Genonic package has automatically been imported up here. So, make sure that this Gingonic package has been imported in order for us to hook into the Genonic functionality here. Okay, let’s start writing our logic for the or middleware function. So the first thing we actually need to do is create a function to extract the token from the header of the incoming HTTP request. So I’m going to create that function in the token_youutils.go file. So let’s go there and create a function for that purpose. So within token_util.go, Go. Let’s create a function called get access token like this which accepts a an argument of type jin.context. So this is the jinggonic context object and it returns a string and an error. So the string will represent the string will be the token and the error will be the error object. the typical go error object that we need to return to the client so the client can handle the relevant error saying jin is undefined here. Okay, so we might not be importing the gingonic package here. Okay, so we need to import the gingonic package. So let’s go back to or middleware, copy this where we’re importing the gingonic package, go to token utils and import it here. Okay, so that red squiggly should go away now. Vanished. Yep. No red squiggly there. Excellent. But we’ve got a red squiggly here. And what’s that saying? Missing return compiler. Missing return. Okay. All right. Okay. So that’s because we’re not returning anything from the function at the moment. But let’s go through the logic step by step. So firstly let’s create a variable called token string and camel case like this. It’s just a local variable for the error object and then our assignment and decl declaration operator. C dot request because we’re going to be reading the token from the header. So C.reest request header.get and then we want the authorization name. So these are name value pairs within the header of the HTTP message and we’re reading the author authorization. [Music] Um we actually just want to return an orth header object here. So let’s call this or header. So we’re returning a reference to this name value pair within the all authorization setting within the header. So we need to return an object here. Firstly press the enter key. So if header is empty then let’s return nothing for the token. So an empty string for the token and then an appropriate error. So we go errors the errors object dot new. So that should have imported errors here. So we need to import errors here firstly for that to work. So we’ve imported errors. Okay. Errors dot new and then within brackets we can include an appropriate meth message and this method message can just be authorization [Music] header is required. Okay, perfect. That looks good. Okay, and then if we get to this line of code here, we can create our token string here. Now token string assignment and declaration operator and then or header. Now we can read the header and at the same time because the header will contain bearer in the first part of this authorization value. we need to extract um the token and leave out the part that has the literal text bearer space in it. So we can do that through this code here. So len the length of bearer of the literal string bearer space and then include a colon here. Okay, so this should be a little L here len like that and that should work. So here we’re extracting the token from the authorization setting within the header like this. It’s a name value pair. So authorization is the name. So we just want the token part of the authorization setting within the header here. So token string assignment operator or header and then we’re extracting the token with this line of code here. Okay, great. And then we can just check if token string is equal to empty then let’s handle the fact that token string is empty. So we’ve we haven’t been able to extract the token from the authorization header. So it’s empty. So let’s return an empty token. So no token. And then followed by errors new. And let’s return an appropriate error to the calling code. So we’re going to go bearer token is required. So it’s called a bearer token. So bearer token is required will be sent back to the client within the error object. else if all has gone well and we’ve managed to extract a token from the authorization part of the header we can return it to the calling client. So return token string and then an error object which will be null. So we return null for the error object here. Okay, brilliant. So that should extract our tokens appropriately. Okay, so get access token has now been written. Let’s go back to our orth middleware.go file. So middleware and then we can call our newly created get access token function like this. So token, and then assignment operator utils utils dot get access token like that. And we pass in the ginonic context to get access token here. And as always, we need to handle an error if if one has occurred. So we go if error is not equal to nil, then an error has occurred. So let’s use the context to pass valid JSON and uh an appropriate HTTP status response to the client. So we go status unauthorized in this case because we have received an error trying to extract the token from the header. So status oops status unauthorized comma jin.h says pass wellformed JSON back to the client with an appropriate error message error. And we’ll just pass what’s whatever is in the error object like this. Okay. Something is a miss. And it’s of course because I keep putting a comma there. And that should be a colon. Okay. And then we can terminate the execution of this function by including the return keyword like this. Brilliant. Okay. Now we want to check for an empty token string. And we can do that by going token equals to empty string. So if it’s an empty string, we can return a similar error. I’m actually just going to make a duplicate of this. And instead of error and then returning the error like that, let’s return a different message here. And we’ll go no token provided for this for this error message. no to token provided like that we actually need to use the gingonic context object to abort this function and you’ll see the significance of this uh a little bit later but so it’s the middleware it’s the the endpoint that we are calling before any of our publicly exposed endpoints are called by a client so here we just go see abort so it won’t continue. For example, if the user is not properly authenticated, it won’t continue to call the targeted endpoint because we we are using C.abort here before that endpoint can be called. And I think what I’m talking about here will become clearer uh as we progress with this part of the course. Okay. And then we can extract the claims from the token. But we need to validate our token first. So we need to create a method called validate token. So we’re going to go back to token_util.go and create another function. And this one will be called validate token. Okay. And I’m just going to fast forward the creation of this code for you. And then I’ll explain what’s going on here. Okay. And that looks pretty good. Let me briefly explain what’s going on there. So science details is a strct we’ve defined that embeds or extends jwt.registered claims. It’s where token claims will be decoded into. This uses the jwt.parswithclaims pass with claims function from the github.com/golangjwt/jwt/v5 package. It pares the token string into a token object. Decodes its claims into your claims variable. Uses a callback function to provide the secret key used to verify the signature. you return it as a bite slice which is required. This checks that the signed algorithm used is HMAC like HS 256. This is a critical security step. Attackers can try to spoof tokens using a different algorithm if you don’t check this. So then we check the expiry date here. So this checks whether the expiry at claim a time field is before the current time. If the token has expired, it returns an error. Great. Let’s go now back to the calling client code here. So we want to call our validation code now colon equals to utils dot validate token and then let’s pass in the token here. Okay. Then if an error is returned, let’s handle that case. If error is not equal to nil, C.JSON http status unauthorized and then gen.h H error and then invalid [Music] token. Okay. And then we can abort this by typing abort like this. Okay. We should actually also include a return keyword for each of these error handling cases like this so that the code doesn’t continue. So we’re aborting it from going to the next calling the next end point here. But then we want to also abort this function from continuing to execute here. Okay, great. And then we’ve got access to our claims now. So we’ve decoded our claims from the token and we can do something like this which is quite cool. We can set the we can use the gingonic context object and set certain values like this and we’ll look at the significance of this a bit later. Claims do user ID. So we’ll be able to retrieve the user ID from the Genonic context object in subsequent code when it calls when the when the relevant target endpoint is called we’ll be able to within that code extract the user ID from the Jonic context because we’re setting it here once the user has been authenticated see so the once the token has been validated which means the user has been authenticated ro and let’s also include ude the roll from the claims here within a name value pair named roll here. So claims roll here like that and then instead of this is the opposite of C.abort we can include C do next. So what that means is it will continue to execute the targeted endpoint which may which will be a protected endpoint. Excellent. And that’s our middleware created. Brilliant. Okay. So the next step is to tidy up our routter related code. So we’re going to sort out the code here. We want to create code for the facilitation of separating our unprotected roots from the protected roots. The difference of course between these types of roots is that protected roots require the user to be authenticated before accessing a protected route and an unprotected route does not require authentication before the relevant user can access the unprotected route. So let’s say we want to protect the movie endpoint here the and the ad movie endpoint and the register and the login and the movies endpoints are unprotected roots. The movies actually are displayed as soon as our website is accessed. So we want those to be publicly available. Um and of course in order for a user to register this has to be publicly accessible and of course to log to the website to actually authenticate the user needs to have access to the login functionality. So these this one and these two and this endpoint must be unprotected while these should be protected. So we only want users with special privileges to be able to access these endpoints here. So to separate our two types of roots, protected and unprotected, let’s create two separate files within the roots folder. So within the roots folder, let’s create a file called protected roots like this.go. Okay. And that will house the functionality to protect our protected roots. And let’s create a file called unprotected roots. Unprotected underscore roots like this.go. Okay. So unprotected roots. And let’s go to the protected roots file. And the first line of code will be this package. roots because we want this functionality to be part of a package called roots and we’ll do the same for the unprotected roots. They must be part of the same package called root. So let’s go back to protected roots. Let’s create an import section here. Okay. So firstly we want to include our controllers package within the import section. So, so let’s go to the main.go file. And I think we’ve already got a reference here. So, let’s just copy this code to our clipboards. Let’s go back to the protected roots file and include the same importation code within the roots package here within the protected_roots.go file here. And you can see we’re aliasing this package with the term controller. And because we want to protect our roots that we’re going to configure in this file, we also want to import our middleware package. So to do that, we can simply go to go domod, copy this root path here, copy this root path here, go back to protected roots, and paste it here. Put these within quotations and then forward slash middleware like that. And we can just verify that that is our package name by going to the relevant file. Yep. Middleware. Okay. Go back here. Make sure we’ve got middleware. We are importing middleware here. And that looks correct. And of course, we also must make sure that we’ve got the ginonic package installed. So, if we go to one of our controllers, one of our controller files, we should have the Jin Gonic package being imported here. And we do. And that’s the code we want there. Let’s copy that to our clipboards, go back to unprotected roots, and paste it here. Great. So, now we’ve we’re importing the appropriate packages so that we can create our protected root configuration code. Let’s create a function called setup protected roots. So to do that, let’s type funk setup protected roots like this. And then this uh this method signature or function signature must include a parameter of type jin. Like this. Okay. And then let’s firstly configure the fact that we are protecting our roots. And we can do that using the routter variable the routter sorry the routter argument that’s being passed into this method. And we use the use method like this. And then we pass in middleware dot or middleware. And of course our orth middleware is the functionality we wrote in the last section of this course which will protect the relevant roots because it’ll abort if the token is invalid. If the token is deemed as invalid or the token has for example expired, it will send an unauthorized HTTP status to the client and with an appropriate error message and then it will prevent the targeted endpoint from being called through this line of code here C.abort and that’s called on the Ginggonic context. So that we use we’re leveraging the Jinggonic web frameworks context to abort the execution of further code the execution of the targeted endpoint like that. So this is how we are protecting our protected endpoints through this middleware. So it intercepts the code. You can see we’re intercepting. We’re calling this before we’re configuring any of our protected endpoints to ensure that the token is valid. Great. So now the next step is just to configure the other endpoints. And we’ve actually already written the code for those protected endpoints. So let’s just we can just cut that there. This is one of our protected endpoints and we can paste it here. And now we’re configuring our movie endpoint here, which is a protected endpoint. And it is protected by this line of code here. The execution of code will abort if the token is invalid. So it won’t get to this line of code here. Okay. And the other one we want protected is the add movie endpoint. So let’s cut that. Let’s go back to our protected endpoint configuration code here and paste it there. Brilliant. So that’s pretty much it. That’s our configuration code set up for the protected endpoints like that there. And now we want to move on to our unprotected endpoint. So actually what I’m going to do to make this simple is to just copy this code here and okay we can just select all here and paste it in here and the only one we don’t actually need is the middleware. So we can remove that importation code here because these are unprotected endpoints and this function should be called setup unprotected roots and of course these roots are protected roots. So we don’t want these configured here. We’re already configuring them here within setup protected routes. So setup unprotected roots. Let’s firstly remove those. And then let’s go back to our main.go method where we are currently configuring our roots. Let’s copy the unprotected roots or cut the unprotected routes. So register and login and let’s place these here. And we’ve got one more unprotected route to configure and that is the movies endpoint which is publicly available on the homepage of our web application which just displays all the movies that are currently available to be streamed on our Magic Stream website. So let’s go back to unprotected and let’s configure that here. And there we go. And that’s pretty much it. So the next step of course we have to call these two functions from main.go. So we need to import our roots package in main.go firstly. So let’s do that. So we can just make a copy of this here and replace controllers with roots which is the name of the package that we want to import here. roots. So now we just need to call the routter dot setup unprotected roots method like this and pass in the routter object here which is from the Genonic web framework. So this code is enabling us to manage our roots through the Genonic through leveraging the Genonic framework. Um, so I’m calling routter here. That’s why I’ve got a red squiggly line. This should be roots because it’s the package name setup unprotected roots, not the routter um, returned from the Genonic, not the routter we’re leveraging from the Genonic web framework. This is our roots package and this is the method we are calling here. So let’s go back to main.go Go roots dots setup unprotected roots and then we want to call roots do setup protected roots like that there. Okay. So now we want to test our functionality. So let’s run our code. So let’s go go run dot to run our code. And let’s see if we’ve protected those roots. effectively. Okay, so it’s listening on port 8080. Let’s launch Postman. Let’s launch Postman. Okay. And I’ve already got this set up here. But um firstly, I just want to do a test that fails. So if we go to movies, sorry, movie, and then forward slash, and then we want a particular movie. So let’s choose the hangover which has an IMDb ID of TT111 9646 9646 like this. Okay, forget the body here. This is going to be a get. We’re going to make a get request. So um so this body won’t have any relevance here. What we’ve got we’ve still got this left over from when we logged into the system when we tested the login functionality. Um, so I’m just going to go out of that there. So we’re doing a test for a get request to the movie endpoint and we’re passing as a parameter to the movie endpoint this IMDb ID. So it should return the relevant data for the hangover the movie the hangover which is uh which has this unique IMDb ID. So let’s try that. Let’s press the send button and see what happens. Okay. And that’s what I would have expected unauthorized. It’s because we have protected the movie endpoint. So you can see here protected roots and this functionality is being called this functionality to validate a token which we haven’t which we were not sending through with the last call. That’s why we got this unauthorized 401 status response HTTP status response from our code here because basically there was no token. So it would have aborted no token provided. Let’s have a look at the error that we got. Let’s have a look at the error we got in Postman. Authorization header is required. So that’s right. There would have been nothing in the header. We haven’t configured that. So in order to configure the header correctly whereby we pass in a valid access token, we need to firstly log into the system, extract the relevant token and then include that in the authorization header and we can do that with this setting here from within Postman to test the authentication functionality through a bearer token. Okay. So to get the bearer token, we need to firstly log in to the system. So let’s go to the login endpoint and we need to perform a post request here. Let’s go to our body here and we’ve already got the relevant credentials we can use to login already here for us in JSON format within the body raw setting here within Postman. So we got Craig Denton’s credentials. So let’s log in. Okay, brilliant. So, that’s brought back Craig Denton’s details for us. And now we have we should have a valid token here because we’ve authenticated with our system through Craig Denton’s credentials. We can now use this access token. So, let’s copy this access token to our clipboards. I’m just going to paste that into Notepad here. Now let’s um include the relevant URL here for the movie endpoint. And let’s try again. And so TT 111 96 46 this is the IMDb sorry 46. This is the IMDb number for the movie The Hangover. So we want to we want the details for the movie The Hangover to be returned from this request. And of course it’s a get request. Okay. And then we can configure our authorization header by going to the authorization tab here and within bearer token. So we need to select bearer token here and then within this token field we can include our bearer token. This is the token that we just pasted into notepad after logging in as Craig Denton. So, let’s copy this token. Let’s go back to Postman here and let’s paste that token within this field here. And of course, make sure that this O type field is set to bear a token. So, make sure this dropown is set to bear a token. Then, paste in the token that has been returned to you once you have authenticated with the system through Postman. Just copy that, paste it into bearer token. Make sure that you’re calling the correct endpoint here, which we are here, movie, and then the IMDb number. This is a protected endpoint. So, we need to include a bearer token, a valid bearer token. Now, let’s see if that works. So we’ve already proved that our orth middleware has protected our endpoint because it returned a 401 unauthorized error to us when we tried to call this endpoint because we didn’t include a valid access token and we are doing that now through this mechanism here provided to us within Postman. Okay. So now we’ve included the token and let’s see if it gets validated and returns to us the relevant details of the movie hangover denoted by this IMDb unique identifier here. So let’s press the send button. Look at that. 200. Okay. So our token authentication functionality is now working. Excellent. And we’ve cleaned up our root code. We’ve cleaned up our root related code here by separating protected roots which we’ll continue to include within this function here from unprotected roots which we’ll configure within the setup unprotected roots function here. Excellent. Okay. So we’ve separated our endpoint roots into two main categories. Protected roots and unprotected roots. Protected endpoints can be accessed once a user has been authenticated and has the appropriate security privileges. The protected endpoints we have dealt with so far are automatically accessible once a user has authenticated. But for the endpoint that we are going to work on now, a user must not only be authenticated but must also be a member of the admin role. This endpoint will be created to enable administrators to update a movie review for a specific movie. For the sake of simplicity, only one movie review will be created per movie and this must be done by an administrator. The exciting part of this functionality that we are about to create is that we are going to use a technology called lang chain go to interact with open AI and extract the sentiment of the movie review which of course will be written using natural language. So basically we’ll prompt the AI to use one of five words excellent, good, okay, bad or terrible to describe the sentiment behind a movie review written in natural language. I’ve deliberately kept this example simple so that the basic principle of leveraging AI to extract a quantifiable metric from natural language can be easily understood. So to elaborate on the significance of the use of AI here, basically each of the words I have chosen to describe the sentiment behind a movie review has an associated number. Excellent has the associated numeric value of one. Good has the associated numeric value of two. Okay has the associated numeric value of three. Bad has the associated numeric value of four. And lastly, terrible has the associated numeric value of five. If we look at the movie documents in the movies collection of our magic stream MongoDB database, we can see we have the admin review field which contains an example of an administrator’s movie review which is of course written in natural language. Then we have this ranking JSON object here that has two fields namely ranking value and ranking name. Ranking name will contain the word that through the use of AI will be extracted from the administrator’s movie review to describe the sentiment behind the relevant movie review. Ranking value will contain the numeric value associated with the word extracted from the relevant movie review to describe the sentiment behind the review. So a simplistic example would be the administrator’s review says something like I loved this movie. The acting was simply sublime. In this particular case, we’ve got I did not like this movie. So, an even more simple example. And the ranking value is four and the sentiment is bad, which is denoted by the ranking name. So, the AI has extracted the word bad to describe the sentiment behind the movie review. So, bad is included as the value for the ranking name and the value of four associated with bad is included as the ranking value. So the ranking value essentially can be used as a quantifiable metric pertaining to the sentiment behind the review written in natural language. If however the review went something like this, I did not love this movie but I did not hate this movie either. The associated ranking name would probably be okay and the ranking value would therefore be three which is associated with the sentiment denoted by the word okay. So here we have a similar admin review to the one I’ve just described. This movie wasn’t great, but it wasn’t bad either. And of course, the ranking value for that is three associated with the sentiment, which is denoted by the ranking name of okay. And we’re using AI to extract these values from a review written in natural language. And this is how we can quantify a review written in natural language. And you’ll see later how this pertains to the recommendation service that we provide through the magic stream website. Okay, great. So let’s write the relevant endpoint HTTP handler function code. Right, so let’s go into our code and start writing the logic for the relevant code which is to do with updating a review for a particular movie. So we’re updating the admin review for the movie. Then a call is made to open AAI with which will contain a prompt and the review. And then the functionality that we’ll leverage through lang chain go will extract the ranking value and the ranking name from AI which basically describes the sentiment behind a particular movie review. Let’s go to the movie controller here. Let’s go to the bottom here. Okay, I’m going to call this function admin review update like this. Let’s hook into Jgonic as we’ve done many times before. Now, handler funk like this. Oops. Let’s create our anonymous function that we’ll return from this function. Okay, let’s include the context as a parameter here like that. And let’s write our logic. So firstly, let’s create a variable named movie ID. Let’s make this camel case. Movie ID. Let’s use our assignment operator here, which also declares the type of the movie ID variable based on the inferred type from the parameter passed through to our function handler here or our handler function. So, it’s going to pass in a parameter named imbore id. So movie ID, this will be the IMDb unique identifier for the movie that will be passed in with the URL to our handler function here. Okay. And then we firstly need to check whether our movie ID indeed contains a value. So if movie id is equal to an empty string then let’s handle this specific case. So we want to send back a particular HTTP response to the user. So HTTP status bad request. So this would be a bad request by the client if the IMDb ID parameter value is empty is not included within the URL. Okay. And let’s send back wellformed JSON. So jin.h open the curly braces there. include error here which is the name and the value of the error is movie ID required. So it hasn’t been included for whatever reason here and we are returning an appropriate message via HTTP. Okay, so we need to include this code of course here um within the return function. Okay. So, and then we can include the return keyword here so that the code halts at this point. If no IMDb parameter value was included in the URL, we don’t want to continue with the code. Okay, let’s create a local strruct named req. We’ll go var like this strruct and let’s define the strct and it just contains the admin review an admin review field of type string and let’s create the JSON tag here because we want this field to look like this within admin code within sorry within JSON code review like that. Okay. And the red squiggly line just means we’re not currently using it. Okay. And then we let’s create another strct, another local strct. And let’s call this one rest short for response. Strruct like this. And then ranking name is our first field string. And we want ranking name to look like this within the JSON. So ranking name like that and then admin review. We’re also going to be returning to the user and you’ll see the significance of this when we write the client code client react code JSON and then admin review like that. Perfect. Okay. And now we’re going to bind the passed in body. This is going to be related to a patch HTTP request from the client. So the body will contain the field and fields and values that we want to update within the movie within a movie document within a targeted movie document denoted by the unique IMDb_ID parameter value. So let’s type if declaration C. Let’s use the context and then we’ve used this before. Should bind and then let’s include the amperand which relates to a specific part of memory where the data for this strct is stored. So amperand w colon error we need to check the error now in a if condition error is null sorry is not null then we want to handle the exception so we go c.json JSON and send back an appropriate HTTP response which is the same as the previous error handling function. Uh so it’s a bad request here and jin.h error invalid request body. So we were unable to bind it to our wreck strct here, whatever was included in the body. So we must send back a bad request response from our server code with an appropriate message. Invalid request body there. And let’s as usual halt execution with the return keyword like that. Okay, great. Now we’re getting to the exciting bit where we’re going to use AI to extract the sentiment of the admin review that will be passed in to ultimately bound to our strct here which is the admin review be passed in through the HTTP body through the HTTP message within the body of that message and we’re going to bind it to the wreck which we’re doing here. And if it gets to this code, the binding has been successful. Okay. And now we want to pass that admin review, sorry, this admin review here into open AI using lang chain go. We’re going to use lang chain go for this. And I’m going to show you how we can do that now. And then it will return based on our prompt. It will return the sentiment. Okay. Okay. So firstly, let’s sort out our prompting code. Okay. So let’s go to env. We’re going to store a prompt, a base prompt because the the ultimate prompt is going to include our base prompt and the administrator’s review. So let’s first include our base prompt here. This will this base prompt will be included with every prompt. So it’s a generic part of our prompt. And then the other part of the prompt will be the actual admin review. So, so let’s create the base prompt within our env file here. Okay. So, let’s call this base prompt template like this. Equals. And then the prompt is return a response using one of these words and then within curly braces we include a placeholder called rankings and this will be added dynamically. We’ll first query the database for all the rankings that are available. So you can see here not ranked as a out of limits value 999 not ranked and then we’ve got one excellent, two good, three okay, four bad, and five terrible. So this makes it extensible. So we can add to that ranking list if we want to within our database. And then that will be added to our prompt dynamically through our code. Okay, great. Let’s finish our prompt. And then we want to say the response should be a single word. And oops. And should not contain any other text. So, we’re being very specific in our prompt here. The response should be based on the following review and then colon and then we’ll include the review. We’ll concatenate the review to the string uh denoted by the base prompt here. We’ll concatenate the administrator’s review to the base prompt and send that through lang chain go to open AAI and that will return a response a single word which will either be excellent, good, okay, bad, terrible and you can extend the values included within the prompt by adding it to this collection here, the rankings collection here. At the moment, we’ve got one excellent, two good, three okay, four bad, and five terrible. Okay, excellent. Great. So, let’s go back to our code here. So, let’s create our function that will encapsulate the functionality for prompting the AI. So, let’s create a function down here. Funk. Let’s call this get review ranking like this. And then let’s include the admin review as a parameter here. A string and we want to return a string which will be the sentiment denoted by one word. Excellent, good, okay, bad, terrible. int will be the value associated with the relevant word that denotes the sentiment and then of course an error object like this if one occurs that will be relevant to the way the client handles the return from this function and now we need to create a new function which will get all the rankings. So, it’s going to query our rankings collection and get all of these rankings and so that we can add them to our prompt, our base prompt within the placeholder here denoting the rankings. So, we want to get a list of those and we’re going to add them as a comma delimited string within our prompt here. Okay. Okay. So let’s go back to our code. Yeah. So we need to create a function now here called get rankings. Let’s go funk. Get rankings like this. Okay. So this get rankings function will return an array of models.ranking ranking to the calling code and an error object. So let’s define a local variable called rankings and this will contain a collection of all the rankings that are saved to our rankings collection within the MongoDB database. So models dotranking like that. Okay, let’s do the usual. Let’s create the context the resource handling functionality. So ctx cancel equals to context dot with timeout and then context dot background. Okay. And then let’s include the defer cancel code here like this. And now let’s create a cursor variable because we want to return the collection from our rankings collection within the MongoDB database. So we want to return all of these documents. So we want to include all of the items within this rankings collection, all of the documents from a particular query. So let’s go ranking collection. and we haven’t created the ranking collection yet. So to do that, let’s go up here and let’s create the ranking connection variable. And similar to this code, but not exactly the same. So we call this ranking collection here. And we’re querying the rankings collection within our MongoDB database. Let’s take that. Let’s copy that to our clipboards here. So ranking collection. so that we can query the ranking the rankings collection within our MongoDB database dot find like this. Let’s pass in the context sorry pass in the context and bon m which means we want all the documents returned and put into our cursor here. all the documents returned from the rankings collection and put into our cursor variable there like that. Let’s check for any errors like this. If error not equal to null then we go return null and like that and then we can write this functionality to make sure that the cursor is closed. So this is also housekeeping functionality to make sure that resources are cleared up pertaining to the cursor variable. So cursor dot close and ctx like this. Okay. And then if assignment operator cursor do all cct. So we’re putting in the cursor whatever is extracted from the cursor. We are now putting that what whatever is in our cursor we are now putting that into our rankings variable at a particular memory address. Okay. Okay. So that’s the rankings variable we defined up here as the strruct ranking. Okay. And then let’s check if is not equal to null. Let’s handle that error. And we just return null because we can’t return anything if an error occurred. And let’s return the error object to the client. And then if it gets to this point in the code that means all has been successful and we have successfully extracted our collection of documents ranking documents and put them into our rankings array here. So we want to return that to the client and we can do that with this code here rankings nil like that. Okay, brilliant. And then let’s go back to our calling code which will be the get review ranking function. So, rankings whoops rankings, uh, operator equals to get rankings like that. Okay. And then let’s check the error object. And if it does have an error, we want to return nothing to the calling code zero and error like that. Okay. However, if an error hasn’t occurred, we’re going to create a variable called sentiment delimited like this. Colon equal let’s just initialize it to an empty string. And then let’s create a for loop. So for underscore ranking operator equals to range. So we want a range of values. Rankings the range of values stored within our rankings variable. Open that there. If ranking dotranking value is not equal to nine. Whoops. 999. Then we want to include that within our sentiment delimited, which is a comma delimited string. We want to include the ranking name within our sentiment delimited variable. So you can do sentiment delimited equals to sentiment delimited plus ranking ranking name like this plus and then a comma like that. Great. Okay, that’s looking good. Okay. And then we want to trim off the last comma and we can do that with this line of code. So we can just go oops sentiment delimited equals to strings like this strings.trim and I don’t think we’ve yet included strings within our importation code. So we must do that. Yeah. So let’s include strings here. Okay. Go back here to our code. strings.trim and then sentiment delimited and we want to trim off the last comma like that. Excellent. Okay. So now what I want to do is load the base prompt into memory. So we’re going to read this value here into memory into a variable in memory. Right? So let’s do that. Let’s go equals to go dot env.load like this. We got to load our environment file here. Environment variable file. So we need to import the package with this functionality to read the env file. So let’s go up to our importation code here and include this. We need to include this particular package here like that. Go.v here. Okay. And we’re loading the environment file if it exists. And then we can just do a quick check to see if the file exists. This actually only applies to when we’re running our code locally. We’re using thev file, but when we deploy this, we’ll use the hosts facilities to set up the environment variables in the cloud. So, this only applies, this code here only applies when we’re running it locally for test and development purposes. So, this error will actually occur when the code is running in the cloud. So all we want to do is just log the fact we don’t need to stop the code or anything like that because it’s not a mission critical error. So it’s just a warning we’ll give. It’s just a warning that we’ll log here. ENV file not found. So in some cases this will be an expected error and we need to import the log package here and we can do that with this code like that. Okay, excellent. We now want to read a particular environment variable which is actually going to contain our API key. We we have to have a valid API key in order to communicate with open AI. So, we’re going to be sending our prompt to OpenAI in order to extract the relevant word, the sentiment associated with particular movie reviews. So, if you haven’t done so, you must sign up for a an OpenAI platform account. So, to do that, we go to openai.com like this. And then we go to the login dropdown here and then we go API platform. So you must log you must sign up for an API platform account. And to do that we click this particular option here. Okay. And if you haven’t yet signed up just go to the sign up here. The only downside to this is you’re going to have to pay a minimum of 5 lasts a very very long time. So with each request you get debited a tiny really tiny amount. So your 5 in order to use the the facilities provided by OpenAI platform here. So, I’ve actually already signed up for an account. So, I’m going to log in with Google with my Google credentials. Okay? And then what you want to do is once you’ve signed up, you’ll have a dashboard. So you go to the dashboard option here and then you want to create a new API key and this is what we’re going to configure within ourv file an API key that we can use to communicate with open AI uh and we’re going to do that via a technology called lang chain go. So you can see I’ve created a few keys here uh just to for demonstration purposes I’ll create another one. So, you’d go create new secret key. And let’s I’m just going to name this um uh I’m just going to name this magic stream key. So, you can name it whatever you want. And then you just go create secret key like this. And then you copy that secret key to your clipboard like that. And then let’s go back to our code here. And we can configure that within thev file here. I’m just going to call this setting open AI API key equals. And then I’m going to paste that key in here like that. And uh so obviously I’m going to deactivate this key once the course is being finished. So you need to create your own a your own new API key through the open AI website so that you can follow along with the next part of the course where we’re going to prompt the AI using a custom prompt in order to extract the sentiment from a movie review. So I urge you to create your own API key and then configure it here. Let’s go back to our code. Okay. So firstly we need to install the lang chain go package. So to do that we use the go get command and then followed by the path to the relevant package which is github.com/tmc/lang [Music] chain go uh slm/op ai like that. press the enter key. So we’re installing the lang chain go package and that is installed successfully. And then we can just include the relevant package importation code which is this code here. Then we are importing lang chain go here like this. So this is the code for importing blank chain go into our application and we’re about to use that package now to prompt open AAI. Okay. So let’s continue. Okay. Okay, so we’re now going to communicate with OpenAI and we need our unique key which we have just saved within the ENV file here. Okay, let’s go back here. Okay. Okay. And let’s write the code to read that key. So, open AI API key operator assigned operator OS dot and we’re reading the environment. We’re reading the particular environment variable which we’ve named open AI API_key like that. Okay, it’s saying that OS is undefined. So, we need to import OS also. Okay, great. Okay, so that will read the environment variable into our open AI API key variable there. Let’s just handle the case where no environment variable is available for this particular environment variable name. If no value is available, we need to handle that. So if open AI API key is equal to empty string, let’s return the following to the calling client code zero errors dot new could not read and then the name of our environment variable. able here like that errors and we don’t have errors imported. Let’s go up here and import errors and we’ll import it here. errors and let’s now because we we should have our open API key now read in from our environment variable then we should be able to communicate with the AI using the lang chain go package. So we go open AI new like this and then open AI and then oh sorry AI with token like this and then we can include our open AI key here in order to communicate with open AI and you leverage their functionality. So we’ve signed up for a valid Open AI key and we are now passing that through lang chain go to open AAI so that we can leverage their services here so that we can extract a sentiment from a movie review which is denoted which is going to be included within a custom prompt that we’ll use to prompt open AAI. Okay. And then we check if error is not equal to null. Return empty string zero error. So the ranking name we haven’t found a ranking name because an error has occurred. Zero is the ranking value and then the actual error will be returned to the calling client. Now we want to read our base prompt template. So we want to read we want to read in this value here base prompt template like that we want to read. So we want to read in this value here the value here for the base prompt template environment variable. So let’s go back to our code here. Let’s create a variable called base prompt template and then assignment operator OS dot get env. Okay. And then let’s include our key which is base prompt template like that. Okay. And now we want to now that we’ve read in a delimited string containing all of the available ranking names or the sentiments available excellent good okay bad terrible and a delimited string we want to replace let’s go to env here we want to replace this with the actual delimited list of sentiments or ranking names So let’s go back to our code here and let’s do that. So we’re extracting the base prompt here. And now we’re going to replace that value with the value extracted here that we got from the get rankings function that we created. Okay. So let’s do that. Let’s go base prompt. So we got a new variable base prompt. This is the template and we’re going to replace in curly braces rankings with the delimited list of actual ranking names. It should be okay. And let’s go strings dot and let’s use the replace function. And the first argument to the replace function is the base prompt template variable followed by the value that we want to replace within our base prompt. So rankings like this and then the delimited string sentiment delimited one like that. And that is how we replace this placeholder value within our base prompt with the actual list of delimited ranking names or the various sentiments. Excellent, good, okay, bad, and terrible. Okay. Now, we’re going to actually call OpenAI. We’re going to leverage their API functionality through Langchain Go. So, we go llm.all context background. Oops, that’s not what I meant to do. Context dot background. That’s more like it. And then we pass in our base prompt. But we also we don’t just want the base prompt because if you look here at the prompt itself, the last thing we’re saying is the response should be based on the following review. So we want to concatenate the actual review, an actual movie review. Okay. So let’s go back here and let’s do that. And we can concatenate a string by using the plus operator. And then the value that we want to concatenate is the one passed in as a parameter value to this get review ranking function. So we do this. Whoops. Where am I? plus review like that. Okay. And then we want to do the usual thing and check if an error was sent back. If it’s null, if it’s not equal to null, let’s handle the error. And we just want to return an empty string. Zero for the ranking value. So the empty string represents that there’s no ranking value extracted here. Zero. No uh no ranking name extracted here. Zero representing no ranking value and the actual error object gets passed back to the calling client like that. And let’s initialize rank val here to zero. Okay. And we need we’re going to do a verification here with a for loop to check that the ranking sent back from the prompt that we’ve sent to Open AI is indeed in our rankings collection. So we’re going to loop through the range of rankings like this. So all the rankings excellent, good, okay, bad, ex uh bad, terrible and we want to test through an if statement whether that ranking exists, whether the ranking name sent back, the sentiment sent back from open AAI is valid response like this. So the response will contain the sentiment name or the ranking name. Excellent, good, okay, bad, terrible. And we need to loop through our collection to make sure that what is sent back is valid. And we go ranking. And then we can also extract the ranking val. So we’re quantifying. This is how we’re quantifying qualitative data, which is the movie review. So the qualitative data gets sent with the prompt. So ranking val equals to ranking dotranking value like that. And then we want to break out when it’s found when the response is found. We want to actually break out of this for loop here. So we can use the break keyword for that. And then we return response, rank val. and nil for exception because if it gets to this point everything has gone well and we can now return the ranking value sorry the ranking name excellent good okay bad terrible and its associated value which is a value from 1 to five one being excellent terrible being five and we’re returning that to the client there and that’s our code for the get review ranking so here we’ve interfaced with open AAI using lang chain to return a quantifiable value from qualitative data in the form of a movie review. And we’re returning the ranking name and the ranking value to the calling client code. So let’s go back up to the actual admin review update function and let’s call our get review ranking function. Okay, great. So let’s call it sentiment. So this is the value we want returned the actual sentiment and this is the value the ranking value and the error object assignment operator and then let’s call our function get review ranking like that and then wreck. So this is passed in to our HTTP patch request. The admin review is passed in through the body of the HTTP message. We’ve extracted it and we’ve bound it to the recstruct here and we are using the admin review field that is then passed to the get review ranking function. So this is part of the prompt. The admin review is part of the actual prompt that gets sent to OpenAI and it returns the sentiment associated with that admin review. Excellent. And also of course the ranking value quantifiable value from 1 to five. Then let’s check for an error not equal to null like this. And then if an error has occurred, let’s pass back an internal server error or 500 HTTP response to the client. Status internal server error. And then let’s pass back wellformed JSON for the error message like this. And the error is error and the actual message is error. Whoops. Error getting review ranking like that. Okay. Excellent. And then let’s include the return keyword here so that execution halts if an error occurs at this point here. Okay. Okay. So now we’re actually going to include the update code that updates a movie document within the MongoDB database. And that movie document is based on the IMDb parameter value that will be included in the URL to target this functionality here. add admin review update functionality. Okay, so let’s create a filter. We want to filter on the IMDb parameter name. So bson M open curly braces. Let’s include the parameter name IMDb ID and then our movie ID variable which will be the actual IMDb ID unique value that denotes a particular movie on the internet and let’s go update. So this is the actual update information that we’re going to be relaying to MongoDB telling MongoDB which fields we want updated within our database with which values. Okay. And to do that we use the set command like this. So it’s dollar set. I’ve used percentage not dollar. That’s not good. We want dollar here. Okay. And then colon bon m oops m open curly braces admin review colon rec and then the actual admin review. So we want to update the admin review and then we also want to update the ranking object within the movie document. So we are updating the ranking object with this code. So we go bon oops do m and we open the curly braces and let’s update the relevant fields. So ranking value let’s include the value which is rank val here and then let’s include ranking name colon and this will be the sentiment extracted from openai we actually need to include a comma here and then a comma there and a comma there like that and that’s the update code. And now we can actually include the code to interface with our database. So let’s include the usual. I’m just going to paste this in here because we’ve written this so many times now. And this is just for cleaning up the resources after we’ve performed our update functionality against the MongoDB database. Okay. So we’re going to use the update one method on the movie collection variable for this purpose for the update to be executed. Result assignment operator movie collection dot and we go update one because we’re only updating one document and we include the context. cleaning up the resources filter to target a particular document within the movie movies collection and then the actual update information here we’ve stored in this variable the update that needs to be the update information that needs to be passed to MongoDB to describe which fields need to be updated and with which values. Okay, great. And let’s just check if any errors have occurred for the update. So if error is not equal to null, let’s pass back an appropriate error response to the client. So HTTP response of status internal server error 500 will be sent to the client in this particular case. if the update has failed. H so we go error and then colon whoops colon error updating movie like this and then let’s include the return keyword there. Okay. Okay. And we also need to check the result returned from this operation here. This update operation. So if result dot match count. So it hasn’t found it hasn’t been able to target a particular document based on the IMDb value passed in as a parameter value to this handler function method. It hasn’t been able to find that document. So if that equals to zero, there’s no match sent within the result of this operation. we need to handle that case. So C do.json and then the HTTP response is that the resource was not found. So HTTP the resource we wanted to update was not found. So we pass back to the calling client status not found like this. And then let’s include an appropriate error message. And let’s use jin gonic to pass back a well-formed well-formed JSON data. Error colon movie not found. Brilliant. And then of course we need to include return keyword so that our code halts because it hasn’t been successful. If the code gets here, our function handler, our handler function method has not been successful. If the code gets to this point, the document wasn’t found. So, we haven’t been able to update the sentiment and the ranking value, the ranking name and the ranking value fields or the admin review within our database. However, if the code gets to this point, everything has been successful, let’s create a response. So, risp dot ranking name. So we’re setting the ranking name to the sentiment variable and restpadmin review equals to rec. And then we can send back an appropriate response. C.json we want to send back a HTTP status. Okay. Response if the code gets here. All has been successful. And then let’s pass back whatever is in the response strct. which should be the ranking name which would be excellent, good, okay, bad or terrible depending on the sentiment extracted from the movie review extracted by the open AI functionality that we’ve been able to interact with through the lang chain go technology. Great. And that is our code written. So in this part of the course, we’re going to create a movie recommendation service for our Magic Stream web application. We have just created a facility to update reviews for movies and we’ve also implemented the AI functionality that extracts the sentiment denoted by one word from the movie review. So in our recommendation service, we can use the ranking value to recommend five movies and order how the movies are displayed to the user by those ranking values. So we can order the way the movies are displayed to the user in ascending order from one which is mapped to the sentiment denoted by the word excellent to five denoted by the word terrible. So how the recommendation service will basically work is our code will query the movies collection based on the user’s favorite genres that the user chose when registering with the magic stream application. Then we can select the top five movies from those genres which will be ordered by the ranking value associated with each of the movies. So, our recommendation service recommends the top five movies in the users’s favorite genres and displays the movies in a ranked order from one to five. So, yes, it’s a very basic recommendation service that we are integrating into our Magic Stream application. I thought this would be a great opportunity to integrate AI functionality that extracts sentiment from a review written in natural language and then quantifies that sentiment and we can then use that sentiment value to rank the relevant movies and in doing that recommend the top movies that pertain to the relevant users favorite movie genres. As described earlier, a review is added to the system by an administrator and AI chooses one word from a list of options provided in our prompt to the AI. The word to describe the sentiment at the moment can be excellent, good, okay, bad or terrible. Each of these ranking names or sentiments has an associated value stored in the ranking value field. Excellent. The value for the ranking name has a ranking value of one. Good has a ranking value of two. Okay has a ranking value of three. Bad has a ranking value of four. And terrible has a ranking value of five. And you can of course extend the sentiment or ranking options if you like. But for the sake of simplicity, let’s stick with the aforementioned five rankings. Okay. So let’s go to the movie_controller.go file. And let’s create an endpoint handler function. Let’s first go to the bottom here and let’s create an endpoint gingonic handler function. And let’s name this get recommended movies like this. handler. Whoops. Handler funk. Of course. Oops. And of course, we need to return a function from here. Funk that accepts C star the genuine context as an argument dot context like that. Open up curly braces and now we can write the logic for our get recommended movies or movies as I put here. Forgot the V movies. Okay, a user has to be logged in in order to use this recommendation service. So let’s first extract the user’s ID from the Genggonic context. Remember in our middleware that protects our endpoints. If the user security token which is a JWT token is validated, we are setting the named value pair value within the context object provided to us by the Genonic web framework to store the user’s user ID value. So we can now use this value to query the user’s collection for the user’s favorite genres which the user chose when registering with the application. So if we look at our orth middleware here, you can see that we’re getting the access token there and then we’re validating the token here. And then if the token has been validated, we’re setting these two named value pairs like this from the claims. So we’re setting the user ID, which means we can extract the user ID and subsequent code and use that user ID. In this case, we’re using that user ID to query the database for the genres that the user chose when registering with the system. Um, and we can do the same for the role. So, we can check what role the user is in also. Okay, great. So, let’s go back to user_controller.go here and let’s write the logic. sorry movie_controller and let’s write the logic for our get recommended movies function. So to extract the user id value from the context let’s create a function named get user ID from context within the token util file. So within the utils package. So before we write the logic here let’s actually write a reusable function within the token util.go Go file here and let’s call this function get user ID from cont whoops context like this. Okay. Oops. So this function is fairly straightforward. So we need to pass in we need to include a parameter of star jin.ontext context here so that we can use the context to extract the user ID from the named value pair that we saved. Once the user is authenticated when the user calls a protected endpoint and is authenticated, we’re saving the user’s ID from the claim stored in the token. We’re saving the user ID to the Gengonic context. So now we can extract that uh value from the Genonic context. So let’s do that. user ID, comma, exists. So the exists, this is clearly going to be a boolean value. This exists value. So it’s returning two values from C.get from the C.get method and then user ID which is the name of the value that we want to extract from Jinonx context here. And then let’s check the exist boolean value to see if the user ID value exists within the context. Okay, so oops, sorry, no, we don’t need that. And then let’s open our brackets. So we’re saying if not exists. So we’re checking a boolean value which can either be true or false. So if it doesn’t exist, return an empty string. And then let’s return an error. And the error we can return can be something like this. User ID does not exist in this context. Okay. Us and the reason why we’re getting a red squiggly here is because I haven’t included in the definition what we are returning from this function. So we want to return a string which will be the user ID value and we want to return an error object so that the client can handle any errors that may occur during this code. Okay, great. Then let’s proceed. id, okay, so this what we’re going to do here is return the user ID as a string. So we can do that with this code. And if all has gone okay, so we need to check the okay boolean value here. if not. Okay, we want to handle the exception. And this code will be similar to the one we’ve already already written. We’re just handling potential errors that may occur here. And we’ll just go unable to retrieve user ID. Unable to retrieve user ID here. So if it’s not okay, if this code fails, we want to return an exception to the calling client. Else if it gets to this point in the code, we can return the user ID. And because no errors would have occurred, we can return null for the error object. So we’re returning those two values to the calling code. String error, string will be the user ID, null or error in this particular case there. Okay, let’s save that. And we can now go back to the logic here within the get recommended movies and we can extract the user ID from the gingonic context and we can do that by calling the get user ID from context function. Okay, so let’s do that. Let’s go user id, and then assignment variable utils. We’re going to use the utils package which we have imported already into this package. Get user ID from context and we’re passing in the gingonic context here so that we can extract the user ID value from the gingonic context. Okay. And then we want to do the usual go thing and check for any errors. So if error not equal to null then let’s handle that. So an error has occurred. C JSON HTTP status bad request and we’ve done this a few times at this point and then let’s pass back an appropriate error using jin.h. So this will pass back wellformed JSON to the client and the client can handle the exception. ID not found in context. Excellent. Okay, so now we want to query the database for the user’s favorite genres. Now that we’ve got the user ID, we can query using that user ID to target a a specific user document within the users collection. And we’re actually going to create a method called get users favorite genres. We’re going to create a function called get users favorite genres to extract the users’s favorite genres before we proceed with the get recommended movies logic. Let’s create the new function below. Get recommended movies. Okay, let’s create a function called get users favorit genres like this which accepts one argument of type string named user ID and returns an array of strings which will contain the user’s favorite genres and an error so the client can handle any errors that may occur. Okay, excellent. And let’s write the logic for this. So let’s first create and I’m not going to explain this at this point because we’ve done it so many times in this course. We’re just creating a context which helps clean up resources. And then we’re going to create a filter for our query assignment operator bon. We’re creating a filter. And we want to filter our query on the user’s ID. So user ID like this. And then we’re passing in the user ID here as an argument. And we want to include that within our filter here. Okay. And then we want to create a projection variable like this bon m. Okay. Okay. And the projection must include favorite favorite underscore genres whoops dot genre name. So favorite genres do genre name. So we want to include that one there. And we don’t want to include the underscore ID within the projection. Okay. And we can set this to zero like that. And these red squiggly lines just mean that we’ve created we’ve declared the variables, but we’re not currently using them, but we’re about to use them anyway. So, okay. And then let’s set the options. So, opts assignment operator options dot find one and then set projection. So we’re setting the projection from the variable we’ve created here check. Okay. And then so options this might not yet be imported into our code. We need to import a package there for options. So let’s go up to our importation code and import the relevant package so that we can use the options functionality there. And it’s just a case of including this package here which is part of the MongoDB forgo driver functionality. So we’re including that there and we can now use the options functionality down here within our query. Okay. And you can see the red squiggly line under options has now disappeared. Okay. Brilliant. Let’s continue with the logic of our code. Okay. And then we need to call user collection.find one. So we’re targeting the user collection. Oops. We’re targeting the user collection and we want to find the relevant data based on the criteria we’ve established through these variables, the options, the filter, the projection. So let’s pass those in to the find one function. So let’s pass in the filter and let’s pass an opt here. And then we decode those results into a variable that we haven’t yet defined called result. So let’s first define the variable called result. So var result bon m. So the this result variable is defined as bon.mm. And we’re the any results that are found within our query here will be decoded into this variable here. So we’re going decode and then oops and then we want to include a pointer. So this is in fact just a pointer denoted by the amperand character preceding the variable. So it means it’s the data will go into a specific location in memory and the result will point to that memory address. That’s what the amperand denotes here. Okay, brilliant. And then let’s check for any exceptions that may have occurred. So if is not equal to null, let’s and then we want to actually nest an if statement here and check if a spec for a specific error if No documents. So if no documents were retrieved, let’s handle that. will return an empty array and null. So it’s not an exception, but the user’s favorite genres were not able to be retrieved because they don’t exist within the database. So that’s what we’re doing here. Okay. And now we want to if it gets to this point favorite genres array we want to put the result in the fav genres array variable. So let’s use the result to extract the results we want. So favorite genres like this bum. A like that. And that’s how we are able to define the type of the variable as well as return the favorite genres that the user chose when the user first registered with the system. Okay. And then we can check if not okay. In other words, if this code is not returning the expected result that’s this boolean value is set to false. So we need to handle that by returning errors new unable to retrieve favorite genres for user. So if we were unable to extract the users’s favorite genres from the database, we are handling that case here by returning an error stating unable to retrieve favorite genres for the user. However, if our genres were retrieved, we want to return an array of genre names to the calling code. So, an array of strings, which is going to be just the names of the genre, is what we want to return from get users favorite genres. And these are the genres that the user chose when signing up to our Magic Stream website. So, this code is pretty involved. So, just bear with me as I write this code. It’s just basically looping through the results and putting the genre name because the uh the genre object contains a value for the genre or an ID, sorry, for the genre as well as the genre name. We’re looping through all the genre objects. If we go to the database here, you can see the genres here. So, it’s got a genre ID and a drama. and we just would want drama, western, fantasy, etc. We don’t want the genre ID returned. So, we want an array of genres that the user chose when the user signed up to use our magic stream website. And you can see them here. Favorite genres. For example, Ben Madison chose comedy, thriller, sci-fi. we so if it was Ben Madison that had logged on we we would get three objects returned to us from our query and we’re now looping through those objects those genre objects favorite genre objects and we’re extracting just the genre name putting them into an array and returning that array to the calling code that’s basically what we’re doing here so v genre names this should be genre names and it’s a string array okay let’s create a for loop loop to loop through the results. So underscore just means no value needs to be returned there. So it means that the method we’re about to call returns two values, but we want to omit the first value. We don’t need this value returned. We just want the item returned. Okay. Range. That’s the range of values stored within favorite genres array. fab genres array variable here and then if genre map okay assignment operator item so we’re checking the type here bund and we’re these types come from the MongoDB for go driver function functionality. So it must be bon of oops must be of type bon.d and if that’s okay. So this is a boolean variable returned here. So if it’s of bon d the item is of bon.d okay will be true and we want this code to run. So we do another for loop for lum. uh the element operator and then range genre map. So genre map was returned from this operation here and then we proceed with our logic here. LM dokey we check the key must be equal to genre name. So of course genre name if we go to compass here we can see the genre name will be comedy for example thriller sci-fi. So we’re just checking that that is the name of the field genre name. We’re checking that in this code here. Let’s carry on. So if all that checks out, name, okay, operator lm value must be a string value. That’s all. Okay. Then we want genre names. We want to append the relevant value extracted. So the relevant genre extracted and put into our array genre names and name. So this here is being added to the genre names array here. Brilliant. And then and then we can return that if it gets to this code. So our for loop would have been successful. We’ve basically extracted the genre names from the array which would contain an array of objects which would also include the genre ID but we want the genre names and we’re extracting that genre name and putting it into the genre names array and we want to return that array of genre names to the calling code and we just go return genre names and null for exception because if it gets to this part here if it gets to this area here. All has gone well and we can return an array of genre names and no error denoted by these definitions here within the main definition of the get users favorite genres function. So we’re returning an array of genre names and do no exception. Excellent. So we can go back to our calling code. So, we’re going back to our get recommended movies function here, and we can now call the get users favorite genres function to get the users’s favorite genres that the user chose when signing up, of course, to use our Magic Streams website. Okay. Equals get users. Oops. get users favorite genres and we want to pass in the user ID to get users favorite genres. So we’ve got our users favorite genres. Now let’s check if an error occurred. If error not equal to null, let’s handle the relevant exception like this. http status. So we’ll send back a an HTTP response of status internal server error 500, [Music] gen.h. And we’re going to pass back wellformed JSON denoting the error that occurred here. If one occurred at all errors. Okay, like that. Error, not errors. And of course, I’ll put a comma there because I don’t know, I’m out of excuses. Should be a colon. Okay. And then let’s include the return keyword here. So that code halts at this point if the favorite genres were not retrieved. Okay. And then let’s include this code here. Go. env. Of course, if this is running in the cloud, the env file may not exist. So, we’ll be checking for an error here because we’ll configure our environment variables differently using the hosts facilities. But when we’re running it locally, we’re configuring our environment variables within the env file. So, what we want to do here, it’s not a fatal error. In fact, it’s an expected error if it’s running in the cloud. So, we’ll just log that fact. We’ll just call it a warning here. V file not found. Okay. So, it doesn’t really matter if the env file is not found. Okay. Var it will sorry, it will matter if you’re running it locally. Thatv file must of course be found if um running locally. So recall let’s create a variable called recommended movie limit vile because we only want to bring back five records from our query 64. So we’re defining it as in N64 and we’re going to assign it the value of five. We’ll configure this in a environment variable at a later point because we don’t want magic numbers hanging about in our code. It’s just not good programming practice. Okay. So in fact let’s do that now. I’m going to go to the MV file here and let’s include and let’s configure that environment variable here like that. So recommended movie limit five. Okay, great. And now we can read that value within our code. Okay, assignment operator OS.get env. And we are reading this environment variable here. Okay, that there. Um, and the reason why that is kicking up a fuss is because there’s a type mismatch here. Go is actually a strongly typed language. And what we have here is a type mismatch because this variable is defined as N64. And this will bring back a string. This get env code code here will bring back a string and we’re assigning a variable that I’ve just defined as int 64 a string value. So that’s why we’ve got the red squiggly line there. So this should actually be a new variable. And I’m going to change this to str. So it’s a new variable. And the red squiggly line has gone away there. And this is because we’re not currently using the variable. But we’re using this to to read the string from the environment variable into memory here. Okay. So let’s just check if recommended str is not equal to what we can do is convert strr con using this str con package. So we need to import that str conf. So we’re going to convert the string red from the environment variable. We’re going to convert it into an integer using str conf using the str cov package here. Okay. So equal str nv dot par int. So we’re parsing what we’ve read here into an int 64 data type. Okay. And that looks good. Great. Okay. Let’s create the find options variable now. Find options. This is for our query that we’re about to send to our database where we’re going to find our recommended movies. Five recommended movies for the user based on the users’s chosen genres and the ranking values that have been assigned to each of the movies through the use of AI through the use of lang chain go. Okay. So find options dot set sort. So we’re going to want to sort the data by this key. Oops. By this key ranking dot ranking value. So um okay. And we want it in ascending order. So value one for ascending order like that. And we’ve got an issue here. P. This is an object here. So we need to include more curly brackets there like that. And that’s now correct. Right. So the key is ranking.ranking value. If we go to compass, you can see if we go to the movies, if we go to the movies collection, we’ve got the ranking value and we’ve got the ranking name. So, we’re sorting by ranking value here from 1 to five. So for example, if the this uh collection of movies included Highlander 2 and Jack Reacher, this would appear first because we’re we are sorting the movies array. We are sorting the movies result retrieved from the movies collection by ranking value. So this would appear first followed by Highlander 2 if for example these were included within the results. So that’s all that is saying there. Essentially we’re we are ordering the returned results by ranking value. So the returned movie results by ranking value in ascending order from one to five. Great. And let’s include the filter for our query. Okay. And we’re querying baston. M on genre name and this must be in quotations genre do genre name like that. Then bson dom. So we only want to print values back from the movies collection that include the relevant genres. The user’s chosen favorite genres and you can sort of start to perhaps understand this query now. So if the user chose comedy, thriller, drama, we only want movies returned from the movies collection that are of those genres. And this is basically what this part of the query is establishing. Okay. So then we want to include this usual context code. I’m just going to copy it there. This is the cleanup related code that we include when we use the MongoDB forgo driver functionality. Okay. So let’s create a cursor for our query assignment variable movie collection. We don’t want to count the documents dot find. And now we pass in find options the filter and the context. So firstly the context. Brilliant. And then we can just check the object. If not equal to null, let’s handle the error. C.json http status status internal. So we want to send back an internal server error response to the client. In this particular case, if our query fails and let’s send back an appropriate error message in well-formed JSON format. And this can just be oops, comma again. It’s not good. Replace that with a colon error fetching recommended movies. Perfect. Let’s include the return keyword here. Let’s make sure that we clear up. Let’s use the defer method to clear up our resources within memory. So, we’re doing a bit of housekeeping here. Okay. Then, var recommended movies. Let’s define this an array of model dot dot movie. So we’re returning we want to return an array of movies. This should be models. Sorry, our package is called models. That’s what that red line that’s. That’s why that red line was there. So we’re defining a variable called recommended movies of type models.mmov here. Okay. So if assignment equals cursor do all. So we’re using the all method to place all the results from within the cursor into our recommended movies array. Okay. And let’s target the memory address of recommended movies like this. So this variable this here points to a particular address in memory and that’s denoted by the amperand character here. Okay, then let’s check the error. So if it is not equal to null, let’s handle that error. So I’m going to just copy this here because it’s quite similar and it’s also going to be a status internal server error if an error occurs there. But we want to send a different error message and we’ll just send whatever is returned by the dot error method. We’ll send that back to the client like that. Excellent. And now we’re in very good shape. We can now if it if the code gets to this point, we can send back the results to the client. JSON http, status. Okay. So, we’re sending a status okay response. And of course, okay, needs to be in capitals. We’re sending the a response of status, okay, to the client. And then, of course, our recommended movies array in JSON format to the client. Brilliant. And that’s basically the recommended the recommendation functionality already written here. Okay. And it’s got a problem with this declare not used. Right? So I’m missing one thing here. And I noticed that the recommended movie limit val had a red squiggly line under it. Because it’s declared and not used, we of course need to use this. We’re reading it a limitation value of five. And if that is not found within the environment variables, we’re actually setting that limitation um by default here with this line of code. But we’re not actually setting the limit for our query. Set sort, we’re setting the sort, but we need to set a limit. And so after set sort here, because we want to limit the results to five movies for the recommendation for our user. So to do that we can use the set limit method like this. So find options dot set limit and then we want to pass in the integer value here. This integer value stored within this variable here. And where we’re reading it from the environment variables we’re actually converting it from a string to an integer with this line of code here. So we want to pass that integer into the set limit method that we’re using on the find options variable here. So we’re setting we’re using the set limit method here to set a limit on the amount of results that we want returned from our query and we want five movie documents returned and no more than five movie documents. So we can do that with this set limit method here and pass in the limit that we’ve defined within by default and within an environment variable called recommended movie limit. So if we go to env you can see we’ve defined an environment variable named recommended movie limit and we’ve set it to a value of five. Let’s go back here and we’re passing that value to the set limit method and that pretty much means that our function is written and ready to be tested. Okay, so we’ve written the functionality for our get recommended movies for our recommendation service. Our get denoted by our get recommended movies function here. So we’re now in a position where we want to test the functionality through Postman. So firstly we of course need to map this to an appropriate route. Map this get recommended movies handler function Jarnic handler function to an appropriate endpoint path. So to do that let’s go to the protected_roots.go file. So, we may as well define the end point within the setup protected route section here because we’re going to need the user to log on. We’re going to need to get a token to test this functionality because it’s user specific. We’re getting recommended movies based on the user’s ID. So, in other words, the user in order for the user to get a valid user ID, they must be logged into the system. So this is why we may as well define the route for this even for testing purposes within the setup protected roots method here. Okay. So let’s define our end point. So for slashremented movies like this, controller dot and this one is get recommended movies. So, we’re mapping an endpoint recommended movies. This is part of the path to the endpoint recommended movies and we’re mapping it to the function we just created called get recommended movies and that’s within the protected section because we need this or middleware to run so that the user ID is saved within the genic context here and we are of course grabbing that user ID in our code here because it’s a very important part of our query where getting the user’s favorite genres because the recommended movies are based on the users’s chosen genres that the user picks when the user registered with the application. These favorite genres were chosen by the user when the user signed up to the magic stream service. Okay. And this is the basis for the recommendation service that we’re providing through the magic stream website. And this function has been defined for handling the recommendation service functionality. Okay. And you can see what we’re doing in this function is we’re getting the user’s favorite genres within an array here. And then we’re querying on the movies collection for all of the movies that are tagged with the users’s favorite genres here. Great. So we’ve now defined our endpoint here and we’ve mapped it to the get recommended movies function. This is the handler function, the jinggonic handler function and we’ve mapped it to a an endpoint recommended movie. So we can now test our code. So let’s run our code. So let’s go go run dot run our code. Excellent. It’s listening on port 8080. Let’s go to Postman. Let’s launch Postman here. So let’s firstly type in the log login login endpoint like that. So this is the base URL followed by the relevant endpoint. The login endpoint. We’re sending a post request to this endpoint because we need to log in with the relevant credentials and I’ve already set up those credentials. So we’re going to log in as Craig Denton here. And this is just a standard password to make things simple during the testing and development phase. Okay, post. And that’s it. That’s all we need. And let’s log in as Craig Denton. Great. Excellent. 200. Okay. The server side code for the login functionality has returned to us a valid token. This is the token which contains the user’s claims, the claims for Craig Denton. So Craig Denton’s user ID can be extracted from this token when that token is passed through to the serverside code so that the recommendation service can be leveraged so that user ID can be extracted in order for the relevant query to go through and bring back the recommended movies for Craig Denton. So let’s copy this token to our clipboards. And now we want to change this end point to recommended movies like this. And then we want this to be a get request. And then we must go to the authorization tab here and include our bearer token. So I’m just going to delete whatever is in here and replace it with the token that we’ve just received after logging on as Craig Denton. Let’s paste that in here into this field. And now we should be able to leverage our recommended movies endpoint which should bring back the recommended movies for Craig Denton. So let’s try that out. Let’s press the send button. Excellent. And it has brought back Jack Reacher. Okay. Um which is tagged up with the these genres action and thriller. Okay. Okay. And you can see this is the admin review we gave it fairly recently when testing the AI functionality and it’s been ranked as one excellent. That’s why it is listed first within the list of movies returned from our query. Okay. And then we’ve got Knives Out which is also which is a drama and a thriller. It’s tagged up with drama, thriller, and mystery. And it’s also got a ranking value of one. ranking name excellent. And then we’ve got one that has been also excellent. So, we’ve got three movies returned to us that are excellent for Craig Denton. And this one’s called Blitz, the Jason Staithm movie. And then we’ve got one that’s just okay and it’s a comedy and it’s Airplane. And then we’ve got another one that’s okay. So you can see that it is ordered the movies from one to five based on the ranking value. So all the ones, there’s three ones, three that have been ranked as excellent and then two that have been ranked. Okay, so it’s returned the movies in the appropriate order and it’s returned five a limit of five movies to us. And we can try again with a different user this time. So, let’s log in as someone else. Okay, let’s go to Compass and find a different user here. Um, so let’s log on as Ben Madison. So, Benmadison, benmadison@hotmail.com. I’m just going to copy that there for the credentials. And the password doesn’t change. Okay. So, I’m going to the body here, raw, and I’m going to replace this email address with Ben Madison’s credentials. The password doesn’t change. So, I’ve just made this easy for testing purposes. This must be a post request. So, let’s set that to post. And let’s send that to log us in as Ben Madison. And we got a token that’s been sent to us here. Let’s copy the token. Now let’s change this to recommended movies and this is a get request and let’s replace the token authorization bearer token here. Let’s select all of that. Delete what’s in there and paste in our new token for Ben Madison. And shouldn’t have a quot the quotations should be omitted here. Okay, there shouldn’t be any quotations within the token. So, let’s test our recommended movies end point for Ben Madison. Let’s see what movies get returned for Ben Madison. So, let’s send that through. Excellent. Okay, it’s got Jack Reacher again at the top and then it’s got Knives Out. Okay. Blitz airplane and this time it says so there is a difference here. So they obviously had very similar genres selected and then we’ve got the undiscovered country which was different from the last set of results and it is ordering these movies in the correct order from one to five. Okay, for good measure, let’s log on as someone else. So, let’s log on as Bob Jones who’s an administrator. Okay, so let’s grab his email address here. Let’s go to login here. Set this to post. Okay, let’s go to authorization. Set this to no orth. And we’re going to go to the body here. Replace the email address with Bob Jones’s email address. The password is the same. And let’s send that through to login. Bob Jones. Okay. Good. And you can see Bob Jones’s favorite genres here. He just likes comedy and fantasy. Okay. And so let’s grab the token for Bob Jones here. Copy it to our clipboards. And let’s change the end point here to recommended movies like this. This must be a get request. Let’s go to authorization. Select bearer token and let’s replace what’s whatever’s in here with the token for Bob Jones. Okay. And now let’s test. Bear in mind Bob Jones’s favorite genres are comedy and fantasy. and let’s see what movies are returned for Bob Jones. Okay, send. Okay, so fantasy. You liked fantasy and we’ve got Harry Potter and the Philosophers Stone. And this movie’s been deemed as okay based on the admin review. This movie wasn’t great, but it wasn’t bad either. So, it’s okay. Three, we got Airplane. He liked comedy, so that’s correct. Airplane. And it’s also been ranked as okay. And then the next movie is Gone Girl, which is a drama and a fantasy. Okay, so fantasy has been tagged for Gone Girl. I’m not sure if that’s accurate, but in our data, it’s been tagged with fantasy. So that’s why Gone Girl has come back and it is ranked as okay. So that is ordered correctly within the results. The Hangover comedy three. Okay, so they’re all okays basically, [Music] right? So our recommendation facility is working correctly based on the data that’s saved to our database. Obviously, this is just test data, so don’t expect it to be 100% accurate in terms of the genres and the rankings, but our code is working correctly. This verifies that our code is working correctly. Our recommendation service is working as expected. Excellent. So if you’ll recall, we created this end point and we temporarily added the code to configure the endpoint i.e. map it to the relevant handler function, the jinggonic handler function. And we initially put this configuration code within the setup unprotected routes just for testing purposes. So it would made it easy for us to test the functionality within this function here which provides functionality so that an administrator can add movie reviews to the system and update those movie reviews within the system. Um so if we go to compass here you can see those movie reviews. Go to the movies collection. Each of these has a movie review. This is a lovely movie and it’s been rated as excellent by the AI. The sentiment detection functionality that we wrote which engages with AI through lang chain go brought back excellent as the sentiment behind this movie review. So it’s all this functionality we’re concerned with but only an administrator should have access to this functionality. So we don’t want ordinary users to be able to add reviews to the system. And that’s what we’re going to do now. We’re going to protect the relevant update review endpoint. So the first step to do that is we’re going to grab this code here. Let’s cut the code and let’s paste it into the protected end point configuration code here within the setup protected roots function here. So now this is protected but we haven’t yet written the code to properly protect this route because this is a special route if you like because the user must not only be logged on but that user must be part of the admin role. So that’s the code we’re going to implement in this section of the course. So let’s do that. Firstly, we want to be able to extract the role from the claims, which we’re doing here. And we’re inserting the role within a named value pair within the context, the ginonic context, which makes it easy for us to extract the role in later code. Um, so we can extract whatever role once the token has been accessed from within the middleware and it’s been validated. We can extract the role. This is what we’re doing here. We’re extracting the role and placing it within a named value pair storage facility provided to us by the Genggonic context. And then it goes see next and it’ll move on to the targeted endpoint which in our particular case will be the update review endpoint and we’ll be able to access that role through the jinggonic context. Okay. So if we go to movie if we go to yeah movie_controller and we go to our add review functionality. So we’ve got our admin review update function here. The first thing we want to do is access what role the logged on user is part of. So to do that we can let’s first go to the token_util.go file and let’s just create a copy of this. And then let’s just change the bits appropriate to what we’re trying to do here, which is instead of access the user ID, we want to access the user’s role. So get RO from context is the name of this function. And of course, we need to change this to RO. And let’s make this variable RO instead of user ID. So roll does not exist in this context. If the RO is not if you’re not able to extract the RO from the Genonic context, we need to handle that error there. And then let’s use our RO variable here. Let’s name this member roll camel case like that. And then we can return the member roll to the calling code. And that is our reusable function written for extracting the role from the jinggoni context. And let’s go back to the movie_controller.go file. Okay. Okay. And now we can use the the function that we’ve just created within the utils package to extract or to return the users ro to this calling code here. So utils dot get roll from context and then we must pass in the gingonic context variable here or the gingonic context argument here and then let’s handle the error if an error occurs when trying to return the role the user’s role let’s handle that exception. Okay. So, C.JSON. Let’s send back a HTTP bad request in this particular case. Status bad request. Oops request. And an appropriate error message. And we can use the gin.h functionality here to return wellformed JSON. Let’s include a colon there. And then roll not found in context. Okay. And then let’s of course include the return keyword so it halts the execution of this function. We don’t want this function to continue if the role is not found because the user that’s trying to add a review must be part of the admin role. So it’s crucial to this functionality that the user’s role is accessible. Okay. And then now we can just do a check here. So if roll and I’m just going to leave the code like this for now just for simplicity. We could put this in a configuration file but I’m just going to leave it like that as a literal string admin. And just for simplicity. and let’s handle the exception. So if the user is not part of the admin role, we need to pretty much kick the user out so that they do not have access to this functionality. And it’s a status bad request again. Okay. And let’s use jin.h H to send an appropriate error back to the client code. And we just say user user must be part of the admin role. And it’s that simple. And then we use the return keyword to stop the execution of the code within this function. Okay, brilliant. And that is pretty much it. So now the user has to be part of the admin role in order to be able to leverage the functionality within this handler function. The function that handles the endpoint configured here, the endpoint update review. Okay. So we can test that code now. So now let’s launch Postman and let’s go to login. We need to log in the user. Okay. And we want to log in this user as Bob Jones. Okay. Let’s go body raw. Okay. Great. We’ve already got Bob Jones’s credentials here. And this is a post request. Okay. Brilliant. Okay, so let’s run our code. Okay, brilliant. It’s listening on port 8080 and let’s go back to Postman and we can test our login functionality. Let’s press the send button here. Excellent. Bob Jones is logged in. Bob Jones is an administrator. You can see here his role is set to admin. So, he’s an administrator. And we just want to copy Bob Jones’s token here. We want to copy Bob Jones’s token to our clipboard. Let’s do that. Copy. And then we want to test the update review endpoint. So we go update review like this. And we want to update the review for a particular movie. So, let’s go to the compass and let’s find a movie that we can update. Okay. And as I’ve said, I hate this movie for um Unforgiven. And that couldn’t be further from the truth. I love this movie. So, actually, let’s test a review for Unforgiven. Adding a review for Unforgiven. Great movie. Gene Hackman, Clint Eastwood. Excellent movie. So, it’s currently set to five. Terrible. I hate this movie. And let’s create a review that is a little bit more glowing. Well, a lot more glowing than that. Okay. So, so I’m going to copy the IMDb for unforgiven here. Okay. And I’m going to paste it here for the parameter so that we can target a particular movie for the review. And then let’s set this to patch because we’re only partially updating the movie document. We’re not replacing an entire movie document. We’re just replacing this part, the admin review. And then our AI functionality will automatically extract the sentiment and save it to this movie document, this part of the movie document here. Okay. So great, we’re set up to test this. So it’s a patch request. We’ve copied the token to our clipboards. So that’s the next thing. Let’s go to authorization here. Bearer token. Let’s replace whatever is in here that’s left over from the last time we tested an endpoint through Postman. And let’s paste the new and let’s paste the new So, let’s paste the Let’s paste the token from that Bob Jones received once he logged on. and paste it here. Okay, brilliant. And now we need to update the body. So we need to include a field within the JSON here, admin review. And let’s create a glowing review for unforgiven Clint Eastwood as always was magnificent. What an amazing amazing cast and story. Okay, Clint Eastwood were Clint Clint Eastwood, as always was magnificent. What a what an amazing cast and story. Okay, so that’s our very simple movie review for Unforgiven. And let’s just make sure that this is appropriate JSON data that we’re passing through. So, I’m just going to go back to the code here. And if we go to the handler function here, let’s just check. So yeah, it’s expecting admin review. And the JSON must look like this. Admin review. You can see that by the JSON tag here. And then whatever we pass in here gets mapped to this wreck strct here. Okay. Within our code. So that looks good. And then we can just test that. So we’ve got the endpoint URL which includes the movie parameter, the IMDb value. Update review. Let’s just make sure that the endpoint is 100% accurate. So if we go to protected roots, update review, and then followed by the parameter. So let’s go back to Postman. And that looks good. We’ve included our review within the body of the HTTP message we’re about to send through to our endpoint. We’ve configured the token because we just logged in as Bob Jones who’s definitely part of the admin group. So, this code should actually work. And let’s see if it does. We’ll press the send button. 200. Okay. Excellent. Yep. I would have expected excellent to be returned as the sentiment behind the admin review that we’ve just created for Unforgiven. Brilliant. So, that has worked. we’ve been granted access to the endpoint because Bob Jones is a part of the um admin role. So that has worked. So if we go to compass, we can see now if we refresh the data, reload data and we go and find unforgiven after we’ve refreshed the data. There it is. And let’s look at the rankings. So it’s updated our review. Clint Eastwood as always was magnificent. What an amazing cast and story. And then we’ve got the ranking section here. And the ranking name is set to excellent. So the AI deemed this movie review as having an excellent sentiment. And of course the value for excellent is one. So that has worked perfectly. Brilliant. So I guess the thing we can do now is just deliberately perform a test that fails. So to do that we can type in login here and we can login as just an ordinary user. So we’ve got Craig Denton. So let’s change this back to email and this toradent hotmail.com. And the password for all of our users is just a very simple password. Not very secure password, but this is just for testing. Um, and it is, I believe, at one exclamation point like that. So, we’re logging in as Craig Denton. We need to set this to post. It’s a post request for the login functionality. And then we can just send that over. Brilliant. 200. Okay, we’ve logged in as Craig Denton. So, let’s copy Craig’s token to our clipboards here. Oops. Here it is. Okay. Copying Craig Denton’s token here. Let’s go to authorization and replace the token left over from the last test. Let’s remove that and add in our new token for Craig Denton’s login. Okay. And then let’s update the endpoint path. So it’s update re view and unforgiven. Let’s just copy that token again. We’ll stick with the same movie unforgiven. Let’s include it as part of the URL. So, it’s a parameter within the URL that targets a particular document within the movies collection. So, we’re targeting unforgiven with this URL here. So, we’re updating the review for unforgiven. At least we’re going to try. So, we’re actually hoping that this will fail because we want users who aren’t part of the admin group. If we look here at Craig Denton, at Craig Denton’s details, he’s just a user. He’s not part of the admin role. So, this should we should get a 401 unauthorized error. So, let’s see if that works. Let’s see if Craig Denton is prevented from adding a review to the database by our code. Okay. So, let’s hit the send button. 404 not found. Okay. So, that’s not what we wanted. Review. Update. Review. What? Okay. So, that’s not the error. This should be a patch. That’s why we’ve received this 404 not found. So, let’s try again. It should be a 401. Okay. So, not a 401. We’ve actually sent back a 400. bad request and that’s just because that’s the way that I wrote the code. So if we go back to the code that is correct. We have been kicked out. We weren’t able to use the update review functionality but I think this would be more appropriate as a 401. So this should be status unauthorized. Yeah, you don’t have the authority to access this endpoint. Let’s save that. So let’s save that and let’s test that again. So go run dot enter. Okay, it’s listening on port 8080. Let’s launch Postman and let’s try that again. Okay, we’ve still got our settings there. Last time it sent us back a 400 bad request. That’s because of the way we wrote the code. The logic is correct, but we should be passing back a 401 unauthorized exception rather than a 400 bad request because this user is not part of the admin role. It’s a valid user, Craig Denton, that can be authenticated, but not as part of the admin role and only administrators can add reviews to the system. So, we want Craig Denton kicked out when he tries to access uh the update review endpoint. So, let’s press the send button. 401 unauthorized user must be part of the admin role. Great. And that’s pretty much the server side code all written. And we can now move on to writing the client code, the React code. So, we’ve coded the server side part of our code and have successfully tested each aspect, i.e. each of our endpoints successfully. Hopefully you’ve noticed that at times during the code creation process I have flagged in the way of including arrows and appropriate text in post-production that even though the code works there is a better way of writing the relevant code. So this is what we are going to cover in this section of the course. We are going to look at some practical examples of a few go jinggonic best practices by appropriately amending just a few pieces of code that we have already created and successfully tested. So we are going to address a few minor coding issues in this part of the course. We are not fixing any code per se but we are amending the code in order to adhere to best practices. Please note that any keys for example secret keys like these ones here or API keys like for example open AI API key here for interacting with endpoints on open AI i.e used for accessing third-party endpoints for security reasons can come from a key management system from a cloud provider and therefore in production would not be embedded within the ENV file as we are doing here. We are reading in the values from thev file here. Let’s look at our code. For example, you can see here we’re checking to see if that env file exists because we’re running it on our local machines here. But regardless of where we’re actually reading the relevant environment variables from, this line of code is actually reading in the value. And this could come from a key management system or it could come from the cloud provider’s own environment variable facility. Environment variable management facility. Um, so that code doesn’t actually change. But here we are just checking whether the env file exists and we’re not throwing a fatal error. We’re not logging a fatal error if the env file doesn’t exist. So when the code is actually running in the cloud, this will actually result in an error occurring because thev file will not exist on the cloud, but all it’ll do is log a warning. It won’t log a fatal error. And this line of code here gets called regardless of whether the code is running in the cloud or locally. So it doesn’t actually matter where the environment variable is coming from. But we are performing a check here just to make sure that it is on our local machines so that it can tell us that our code can tell us as it were if the environment variable exists on our local machines um or not. And ob obviously when we’re running it locally the environment variables thev file must exist on our local machines but it won’t exist in the cloud. So this will be logged in the cloud which is fine because it’s just a warning message. It’s not a fatal we’re not logging a fatal error. So it is best to store the relevant key values. So we got these key key values here. For example, the well, we’ve got this key value here, OpenAI API key, and these uh secret key values here that sign the relevant tokens. It’s best to store those in the relevant cloud hosts key management system, as a security measure, as an extra security measure. Right? So if we go to let’s perform our first best practice code amendment if you like. So let’s go to database_connection.go here. So here we are handling our connection to our MongoDB database. We also have a reusable function called open collection here which is reused throughout our application where appropriate code is executed to open a specified collection within our MongoDB database. So we are using the MongoDB for go driver here within our code to interact with our MongoDB database and we are centralizing this reusable functionality within this file. Now here’s the actual issue. when we import this package, what we’re doing here, which is not really good, is we are actually automatically dialing the MongoDB uh instance. So, we we’re dialing MongoDB here when we import the package. We actually don’t want our code to automatically dial MongoDB. We want it to only establish a connection. So, by dialing, I mean establishing a connection with MongoDB. We only want to do that at when when the MongoDB connection is actually uh necessary when it when it’s going to be used. So we don’t want to do this uh like like we’ve got it here currently. So we’re going to copy this to the main method. We’re going to call this code from the main method and pass in we’re going to inject the client into the where we’re setting up the roots here and then we’ll inject it into the relevant handler function methods. So that’s what we’re going to do. But before we do that, this DB instance isn’t a really good name for our method. So because we’re using the DB prefix here, which has special meaning for MongoDB, so instead of calling it DB instance, I’m going to change that to connect. So we’ll keep the method name very simple and just call this connect like that. And then we can change this code over here to connect. But we’re going to have to change this code further because we’re going to import this database package from within the main package. And then this code will be changed to database.connect. But let’s cut this code. Ctrl X. And I’m going to go to the main method now here. And I’m going to paste it in before this line of code here. uh sorry after this line of code here just before where we’re setting up our roots I’m going to paste in this code where we will connect to establish a connection with MongoDB. So we need to import the database. So let’s go back to database connection there. And we’re also going to want to import these. We’re going to want to import this package here. So let’s go back to main.go and make sure that we’re importing that package. So let’s ensure that we’re importing the package there. Okay. So that underline has gone away now. But the problem here is we haven’t imported the database package. So let’s go here where we’re importing the database package and just copy that importation code. So this code here and let’s paste that here. like this. And then we can call. Is there a problem there? Uh, yep. Let’s paste that in. It’s because I saved it and we’re not using it. It removed this line here. So, I won’t save it. But we want this um importation code in place because we’re going to use the database. We’re going to use database.connect here. Dot connect like that. And now we’re going to pass in the client to where we’re setting up the roots here. And in fact, this should be a lowerase C for client because it doesn’t need to be available to any calling code. So we’ll keep this a local variable client and the underlying CR because we haven’t actually included a parameter definition for these client values here. So that’s the next thing we need to do. So if we go to setup setup protected roots protected_roots file.go, we now want to include the client here. So client which is of type mongo.client like that. Okay. And we don’t have the package. So we need to paste in the package where we we’re pasting it in here. So we need to paste that in within the roots package also. So let’s include that there within the sorry protected_roots.go file so that we have access to the relevant type there. And then we’re going to also inject this client into all of these roots here. These handler functions, these genonic root handler functions if you like endpoint handler functions, they need the client in order to connect connect to MongoDB. Okay. And we need to do the same in the unprotected roots. So let’s do the same here. And we need we can just paste in this package importation code here like that. And then we need to include this parameter definition within the relevant method like this. And then we of course have imported that. So that’s working correctly. And then we want to just pass in the client like this. And of course you’ve noticed that there’s red squiggly lines there because we don’t we haven’t included a parameter definition within these handler function these jonic handler function methods. So um we’ll do that in just a bit. Let’s just copy let’s just paste the client in like this so that it is being passed in to these handler functions here these gingonic handler functions. So these functions handle the logic for when these end points are called via the relevant HTTP methods get post get and post in this particular case. So the next step as you can see is to include the client this definition the client definition for parameters within each of these handler functions. Let’s handle this one first. So movie imdb. So it’s get movie here. So get movie. We can actually just navigate to it by selecting the method, right clicking and go to definition. And let’s include that definition like that there. Great. Let’s go back there and now add movie. We need to do the same here. So go to definition and let’s paste that in there. And we just repeat it. You can see the red squiggly lines are disappearing as we make the relevant coding amendments. So go there and paste this definition in there. Let’s go back to protected roots there. And we want admin review update. We need to amend that code. And let’s do that there. So we’re passing in the client there. Okay. And let’s do the same now for for the the roots for the protected roots the the sorry the unprotected root handler methods if you like the function the handler function methods here. So let’s go to get movies and let’s include that definition there. And let’s just go down and repeat this process for each of these handler functions like this. Okay. get genres. Okay. And then refresh token handler. Great. And then let’s just go through each of these functions and make sure that our code is amended appropriately. Okay. So, next thing I need to point out is these variables. We actually don’t want to do that here. Um, it’s quite cheap to create these connections to or open these collections. It’s quite cheaply done behind the scenes. So, we can actually just open these collections as we go. So, I’m going to cut this code here and I’m going to include it here just above this line of code here like that. And now we need to pass in the client like that. We’re going to inject in the client like this for this open collection method. And now we need to amend this open collection method. So let’s go to do definition here. And we need to include this definition here. like that because we’re going to be passing in the client like this here. And let’s make this a small C where we’re connecting to the relevant collection like that. So we’re opening the collections in a slightly different way now and we’ve amended our code. There is this reusable code which is centralized within the database_connection.go file here and then we can just go back to moviecontroller here and that should be pretty good. That’s okay. And I think that’s all we need to do in regards to opening the relevant collection. Okay. And then let’s go to the next method. So let’s go to protected roots. We got add movie here. So let’s go to the definition and we want to do the same thing. Okay. So we actually amended get movies there. Um we want to do the same for get movie. Okay. So it’s the same kind of code. We can just paste that in there like that. We’re passing in the client and we’re handling the movie. We’re opening the movie collection in a slightly different in a slightly different way now. like that. Okay. So, let’s go back to protected roots there. So, we’ve done ad movie. Let’s make sure that we well we have amended ad movie. Okay. We haven’t. So, we need to do the same thing here. So, we can open the collection here like this for the add movie method. Okay. Let’s go back to protected roots. So we’ve done get movie, add movie, and we want to do re get recommended movie. Get recommended movie, sorry. So let’s go to definition there. And it’s just the same repetitive task of making sure that we are now opening the collection appropriately. Let’s go back to protected roots here. And let’s deal with admin review update. So let’s go to definition there. And where we are making our colle our connection where we are opening our movie collection. We need to include this line of code here just before our code engages with the movie collection here. Great. Okay. So, we’re now handling our movie collection, connecting to our movie collection appropriately for the protected routes. So, no red squiggly lines here. We’re getting there slowly. It’s a bit of a tedious process, but it is worth it. Okay. And then we’ve got get movies. And we actually I inadvertently did that thinking it was get movie. So, we’ve actually amended this code here already. And all we’re doing is opening the movie collection before we’re using it. We’re opening it locally here, not up here. We don’t need to do it up here anymore. And we’re going to do the same for all of these collections in just a bit. So, we’re now opening these collections in a different way locally when we need them. And we’re doing that because it’s fairly cheap to open these um collections behind the scenes. So, it’s not a problem to do it this way. Okay, so let’s go to So we’ve handled that unprotected roots. We’ve handled get movies register user. So let’s go to definition. And of course now we’re changing the code regarding this user collection. So let’s cut that and we want to paste it in here just before we’re using the user collection. like that there. And that’s all we need to do. And that’s not all we need to do because we haven’t passed in the client. We need to pass in the client like this. And then let’s make a copy of that code. Let’s go back to the unprotected roots code here. And login user. So we’re going back to the same controller and amending login user with the way we are now engaging or now with it. the way we are now opening the relevant collections and we’re just pasting that code in there so that we’re opening the user collection appropriately before we use it within the login user method and then let’s go to unprotected roots again log out will be the same thing. So let’s go to definition log out here. Okay. And with log out we’re updating all tokens. So we’ll have to pass in the client here to this update all tokens method within utils. So let’s include the client there because we’re now passing in the client to each of these handler functions. So we include client there and then we need to um we need to copy this definition here so that we can update the update all tokens method within our utils package and we need to include this definition here client MongoDB and we’re using the now the user collection at this point here so we need to amend we need to include a line above this code that actually makes that connects to the colle that opens the collection. Okay, so here it is here. So we can just cut this code here and paste it in just above the line of code where we’re actually using the collection. Of course, we need to pass in the client like this. Okay, let’s go back to unprotected roots code, the setup unprotected roots function. And now we’re dealing with the genres here. So let’s go to that method. Go to definition. So you can see here we’re using the genre collection. So we want to eliminate this code here that we’ve defined outside of any particular function. We don’t want to do it like that anymore. We want to do it the way we’re handling the get movies collect the opening of the movies collection. So we need to go back to sorry this unprotected route get genres handler function and paste that open collection code just above where we’re using the genre collection and we want to pass in the client like this. Okay. And that is looking pretty good. So we’re getting there. Let’s go back to unprotected roots. And then we got refresh token handler. I don’t know if we’re actually interacting with the database in this code here. Validate refresh token. Yeah, we are. We we’ve got the we’re engaging with the user collection here. So, we need to update this method with a with code to open the user collection here. Okay. So we can just copy the relevant code from uh yeah let’s go back to unprotected roots refresh token handler go to there and then just above here include as I said earlier if you get stuck at any point and you’re not able to follow along with this code or you find that you get lost, um you can check out the final code at this URL. The URL to the relevant GitHub repository has been included below in the description. Okay, so we’re updating it there. Find one, right? And then we’ve got update all tokens. We need to pass in the client here. Excellent. That looks good. And then I saw a red squiggly line up here. There it is. Refresh token. Update all tokens. We just need to pass in the client. When we’re logging in the user, we’re updating the tokens within the user collection for the relevant user. So, we need to pass in the client object here that connects to the to MongoDB. Okay. So, that’s the connection to MongoDB there. And we’re now handling opening the relevant collections as we need them within local function handlers or handler functions. Okay. So one thing we haven’t done is we haven’t eliminated if we go here we are still using this rankings. We’re still opening the ranking collection in the old way. And this of course is now not going to work. So what we want to do is cut this code here and we need to find the function called get ranking. Now it’s this is a method that’s called from within one of the handler functions. So it’s not being called directly by an end an end point. So we need to actually include the client definition here. So let’s do that. client uh star dot client like this. And then let’s include the code that we just copied to our clipboards that we cut and let’s paste it here. And then let’s pass in the client object like that there. So now we’re opening the collection in the in the in a consistent way. And we’re doing it now for the rankings collection. So we’re only opening the collections when needed within the local within the various local functions. Okay, great. And then we need to find out where this has been called. So we can go find all references. Go here and we need to Okay, we’re going to need to pass in the client down to here also. So I’m going to include client as star mongo.client like that. And we need to pass in client there. And then we need to find where we’re calling this. So we can find all references there. There we go. And here we’re defining client within the ad. This is the actual handler function, the jinggonic handler function. So we’re handling an actual endpoint in this function here. And we’re calling these these functions from within the parent handler function. So we’re calling get review ranking which calls get ranking. And we need to pass in the client here. So let’s client there. And the red squiggly goes away. And now we are handling opening the relevant rankings collection in an appropriate way. if you like. Okay, we’ve got a problem there. Let’s go and see what the problem is. Undefined user collection here. So, we’ve so this is also a function that’s being called by one of the handler functions. Um, and we don’t have the client object available to us. We’re not opening the user collection in an appropriate way yet here. So, we need to handle all of those things. So we need to find where we are opening the user collection or users collection and let’s go back there and let’s go to where the problem is and let’s paste in that code. Now we’re opening we’re opening the collection appropriately but we don’t have the client object. So we need to include the relevant definition for the client here the MongoDB client. So client and that’s defined as mongo.client like this. Okay. And we’re passing it in as an argument there like that. Okay. We still have a problem. Okay. Yep. Let’s go to the problem and of course we this is the get recommended movies handler function which handles an actual endpoint call. This is the logic for an actual call to an endpoint using a HTTP method from the client. So we need to pass in the client that is passed into this handler function to this method. Like that we have no problems now. Excellent. So we’re also opening the rankings collection appropriately now. So what we’re doing here now is we we’re only opening the collections when we need them from within the relevant functions. Whereas before we were opening the collections outside of any one particular function call which is not what we want to do. So this is a better way of doing it. Excellent. Okay. So the next thing we want to do and I would say this is actually quite an important fix is we want to pass in this ginonic context object to the width timeout method here. And let me explain why we want to do this. Okay. So this is quite an important fix. We are currently not handling this resource clearing related code correctly within our Jonic application. Here we are passing in the context.background object to the with timeout function. We should be passing in the jinic context here. Jin implements context in a way that it embeds the requests context.ext. So you can safely use it wherever a context.ontext is required. Why not context background like we’re doing here? If you use context.background background instead you lose request cancellation propagation if the client cancels the HTTP request closes the connection navigates away etc. Jyn cancels the jin context so using C as the parent context means your timeout context is automatically cancelled when the request is canled using context.background background ignores client cancellation. So your handler may continue doing work even after the client has gone away. That can waste resources. You lose deadlines and other request scoped values. Jyn may attach request scoped metadata or timeouts in its context. Using C ensures you inherit them. Use C. Your context respects client cancellations and request scoped values. Use context.background. Your context is independent, ignores client cancellations and could lead to resource leaks. So we must pass in C the gingonic context here rather than context. Right? So let’s handle this. Let’s make sure we are doing this. So let’s pass in C here firstly. Okay. And so that’s basically what the fix is. It’s very basic. So we need to do it wherever we see this. We need to pass in the gingonic context instead. So let’s select that and let’s go to find all references and let’s go through them one by one. We’ve handled that there and we need to handle it here. So we uh pass and see there go there. Pass and see here. Okay, select this one. So, we need to pass in C here for admin review update there. So, we’re passing in the ginonic context object that is passed into this anonymous function here. This is where our root handling functionality exists. This is the handler function for an endpoint. So, we’re passing in the jin context object instead of context.background. background um for the resource handling code here. Okay, excellent. Let’s go to the next one. Okay, passing C here. Next one. Pass in C here. Okay, go to the next one. We need to pass in C here. Now, this one’s a little bit different because this is called from an actual handler function. So, we’re not implementing a gingonic handler function for this method. So, we need to pass in C to this method. So, C. So, we can do that at the end here. Let’s go comma C and let’s define it appropriately as sorry Jin dot context like that there. And we of course need to ensure that we’re passing the context to this um to this function here. So let’s go find all references and we need to pass in C here to fix that there. Okay, brilliant. Find all references and you can see we are here. This one here we’re not yet. This is uh this is calling get rankings from another function that’s not a gingonic handler function. So we need to also pass in the C here the Gonic context. So let’s do that. C uh as star jin context like that and then we can pass C down there to get rankings function and then we can just find all references here and make sure that we’re passing C in here like that. And that looks pretty good. We’re now handling this clearup code, this resource clearup code effectively by passing in the gingonic context to the width timeout method here, which clears up the relevant MongoDB resources. So the next code I want to amend is within the main.go file, the main package here. So it’s actually good practice to ping the database before we set up the roots here because if we don’t have any connection to the database then our code is going to fail. So this is just a better way of handling the case where the where we are unable to establish a connection to MongoDB. So we can do that with this code. If equals to client. We’ve got our client object. Now we’re connecting to we’re estab trying to establish our connection here. And then we’re going to try and ping. We’re going to try and see if we can if our mong if MongoDB is receptive to us connecting to it. So uh this is not connect context do background like this and then null. We don’t have jin gonic context at this point. So we can use context.background. background as the context here and then null and then on the same line we can check the error to see if this object has a value. So if is not equal to null. Okay, we want to handle an error if we are unable to ping. Of course, null has one L. If we are unable to ping MongoDB, we actually want to log a fatal error here. So we use fatal LF like that. And we can include an appropriate message here. Failed. We’re not reaching the server. failed to reach server. So we’re flagging a fundamental issue here. So we don’t want the code to continue. There’s a fundamental issue and by logging it we we should by examining the logs understand what is actually happening. So there’s a problem with us connecting or reaching um the actual server. So our client can’t ping MongoDB. So we need to actually stop execution here and log an appropriate error and it’s a fatal error. So we don’t want the code to continue and set up the roots because we know that it’s going to fail downstream anyway. So we can handle that issue at this point here in the code within the main function there. And the other thing I want to do is um make sure that we’re disconnecting from the database and we can ensure that we disconnect at an appropriate time from the database and this is to do with clearing up resources. So defer funk and then we want to equals to client disconnect like this disconnect. Okay, we need to pass in the context dot background method there like that. And then we can check our error object and see if it’s null. If it’s not null, we want to log a fatal error here. Okay. And we can now let’s amend this message so that it is appropriate to what is occurring here. So we’re trying to disconnect at an appropriate time. We’re trying to disconnect from MongoDB. Say for example, an error occurs, we want to disconnect from MongoDB. And if that fails, we need to log that fact so that we’re aware of what has happened. So failed to disconnect from MongoDB like that. Okay. And we’re logging the appropriate error. Open and close brackets there. Space there. And that’s pretty much it. So, we’re just making sure that these cases are being handled appropriately, making sure that we can connect to the database to MongoDB before we set up the relevant routes so that it fails here at this point um before it’s going to fail anyway if the if we’re unable to connect if we’re unable to reach the server at this point here. So, that’s what this ping functionality is all about. And then this defer functionality is just to make sure that we’re disconnected from MongoDB. We don’t want to have those resources hanging about in memory unnecessarily. We need to make sure that we’re disconnected from MongoDB. Okay. Excellent. And the other thing is I’m not currently checking whether the environment thev file exists before calling the code to read in the appropriate environment variable value here. and on our local machines that’s quite important. So let’s include the relevant code. Um, so it’s just colon equals go do go.env.load and then within quotations env like this. And then we just check our error object. If it’s not equal to null, an error has occurred. And we just want to log a warning at this point because this error is going to occur when it’s deployed to the cloud. But locally this um warning message can be helpful for us because then we know we haven’t created thev file and we need to do that and this is the reason why certain code is failing. So at this point we’re checking to see if that.env env file exists before trying to read one of the environment variables here. And this code doesn’t change whether it’s running locally or in the cloud. It’s going to read the relevant environment variable in the same way regardless of how it is stored. And it will be stored differently in the cloud to how it is stored locally in thev file here. But we need to just check whether that file exists. And it won’t exist in the cloud. So this it will just log a warning message in that particular case. It won’t be a fatal it won’t log a fatal error. It’s logging a warning message. Unable to find [Music] file like that. Okay. Excellent. Okay. So now it’s time to create a front end for our application and we are going to use React for this purpose. You can see here we’ve uh created a client folder. So our serverside code is within the server folder within the root folder, the magic stream movies root folder. We’ve got a server folder and we’ve got a client folder. So within the client folder, we’re going to create the react code. Within the server folder, we’ve got the Go Jonic MongoDB related code. Here we are going to create a React project. So we are going to use a technology called Vit for creating our React project. Vit has an advantage over traditional build tools like create react app and bundlers like Webpack. For example, it offers faster dev startup and rebuild time, simpler configuration, and out ofthe-box simpler defaults. So, we’re now going to use VIT to create our React project. To use Vit, you must have NodeJS version 14.8 plus, 16 plus or newer. We also need npm, which comes with Node.js JS and is used for installing dependencies. So if you don’t have NodeJS installed, navigate to NodeJS.org and you can download and install the latest version of Node from here. Excellent. And you can check what version of of Node you’ve got installed simply by typing node dash V like this. And you can see version 2401. That’s my version of node. And if you want to check what version of npm you’ve got, type npm slash sorry -v like that. Excellent. So once you’ve got that set up, i.e. uh an appropriate version of node and an appropriate version of npm, we’re ready to go. So firstly, we need to be in the client folder. So let’s type cd client because we’re going to be creating our client code within the client folder and we’re about to create a react uh project using vit. So to create the react project using vit we then type n pm create like this vit and then at latest like that. Okay. And then to proceed, we press Y. So it’s telling me that I need V 7.1.0. Need to install the following packages. So I’m going to say Y here. So we need to give the project a name. I’m going to call my project magic stream client like this. Press the enter key. Excellent. And then you can use your down arrows and you want to select React here. Press the enter key and then select JavaScript from this menu here. And now it’s instructing us to type the command CD to navigate within our Magic Stream project, the root of our React project. And then we want to type npm install which will install all the necessary dependencies to run a react application. And then we can test our project by typing npm rundev. So let’s follow those instructions. So let’s go into magic stream client stream client like that. Okay. Okay. And then we want to install all the relevant packages within the magic-stream-client folder. So to do that we can use npm. So type npm install. Press the enter key and it will install all the necessary dependencies that we need in order to run our React project. As always, we just need to be patient while it installs those dependencies on our local machines. Excellent. That wasn’t too painful. And that’s all done. Okay. Found zero vulnerabilities. That’s good. Let’s clear the screen. And now we can we should be able to run that. So you can see here it’s created our folder, Magic Stream Client. It’s created the node modules folder which has all our React dependencies. Great. So we’ve created a basic infrastructure for our React application using Vit. And there’s a lot of advantages to using Vit over the traditional create react app facility. Right. Okay. So we’ve now got the infrastructure for our React project set up. So vit has set up a basic project infrastructure. So there should be some very basic functionality included in that and we can test this by typing npm rundev like this. Okay, brilliant. So it’s listening at port 5173 on localhost. So let’s copy that URL. Let’s go into our favorite browser. In my case that’s Chrome. And let’s test that the infrastructure has indeed been created for our React project. Excellent. Look at that. And there we go. We can test the interactive functionality by pressing this count button. And it counts within our browser. As we click the button, it creates a it increments the value within this button. We can see that our user interactive functionality is in place and we can start developing our client application now. That’s brilliant. So make sure you get to this point before you start developing the client side for our magic stream application. Excellent. So let’s go out of that and let’s go back here and we can press Ctrl C to cancel out of this. So it won’t be listening on this port on our local host. So, let’s press Ctrl + C and let’s clear the screen. We’re going to get started with an easy win and we’re going to start with a functionality that just displays kind of a gallery of movie posters on the front end. So to do that, we’re firstly going to create a folder within here which will house all our components. So, new folder and I’m going to call this components. So, we’re within this src directory which was created by VIT by default. We’re creating a components folder here. So we got our components folder and we’re going to create our first component within a folder named movie. So our first component will be a reusable component that will represent one movie. So it’ll be represent basically a front end a card which will contain the poster of the relevant movies. So we’re going to create one movie, one movie component that can be reused for all of the movies throughout the application. So that’s what we’re going to start with. Okay. So let’s create our movie component. And we’re going to create that in the form of a JSX file. So let’s select the movie folder here. Press this icon for new file and create a file called movie.jsx. So this is a JSX file. This should have a capital letter. So let’s stick to conventions here. And let’s make sure that the movie file name has a capital M here. Okay, great. So we’re going to be using Bootstrap as our CSS framework for styling and layout purposes. And an easy way to utilize or leverage Bootstrap is through a package called React Bootstrap. So we want to install React Bootstrap. So, we must make sure that we’re in the magic-stream-client folder. So, to install React Bootstrap, we type npm and then I or install. Let’s type install and then react Bootstrap like this. Excellent. And we’ve got React Bootstrap installed. So, let’s start building our movie.jsx jsx component, our movie component within the movie.jsx file. Okay, great. So, I’m going to start by creating the infrastructure for the movie component. So, just const and then movie with a capital letter like this and then equals to and then this is where we’ll pass in our props. And we’re going to pass in a movie prop to this particular component. And then let’s implement the structure for our component here. So this is the the basic structure for our movie component done here. And then we can return the relevant JSX code to the calling code like this. And then below here we want to export export the component as default so that the calling client can easily import this component and we’ll look at how to do that in just a bit. Great. And now we simply write the code the JSX part of the code for the movie component. Firstly, we need to import a few things. So in fact at at present we just need to import one package and that’s the button package from React Bootstrap. And we can do that with this code here. So, React Bootstrap slash button like this. But there’s a few other little steps that we need to carry out in order to be able to leverage Bootstrap the way we want to within this component and other components. So, so we need to go to the main.jsx file here. There it is. Main.jsx file. and include this line of code import within quotation marks bootstrap slashdist/css slash bootstrap domins like that and this will enable us to leverage Bootstrap some of the Bootstrap classes from within the components of our application. So let’s go back to our movie.jsx file and implement the code for this. So we first want to create a div. And I don’t want to focus too much on the styling code because it just take too long to explain every class that I’m including here for styling purposes or layout purposes. So, I would urge you to look up these classes on the Bootstrap 5 website or the React Bootstrap website if you want to know more about the details of these classes. Okay. And it’s kicking up a fuss because I’ve included a dash here. So, we need to include an equals. Oops. An equals there. Great. That looks a lot better. Okay. And let’s proceed. So this movie, by the way, this is what this movie prop will be the object that contains all the data that we’ll retrieve from the server. And we’ll do that a bit later. So just take it for granted that this movie data has been returned from the server and we’re now able to leverage its various fields within our movie component. Okay. So let’s create another let’s create a nested div here. Class name equals 2. And then let’s include our class names here. Card. And these are all our Bootstrap class names that are used for styling and also for layout purposes within our movie within our movie component here. Okay. Okay. Let’s include another nested div here. Okay. So let’s go class name like this equals to and then card body. These are bootstrap. These are standard Bootstrap classes that we’re using to style flex deflex and we’re using flex grid. So we’re leveraging the flex grid technology through Bootstrap here for our card. And within our card, we’re displaying all the relevant information about the movie. So H5, let’s include the H5 element here. Class name equals to card title. So this is where we’re going to include the card title. And then this is how we can display variables within and mesh those variables within the HTML code. And this is just JSX. So it’s not actually HTML code, but it represents HTML code if you like. And then we can wrap any variable data that we want to display on the front end within curly braces like this. So movie dot title like this. So we’re displaying the movie title within the card within this H5 element here. Okay. Okay. And below that, let’s include a P tag like this. Type class name equals to and then let’s include an appropriate Bootstrap class. So this is just for styling purposes. You’ll see the significance of this styling when we run the code once we’ve finished some basic code here to display the movies on the front end. Okay. So we got card text and MB2. That’s just margin bottom two. That’s what that represents. And within curly braces, let’s include moo v dot. And we’re going to include the IMDb ID within this element here. So, we got the title and the IMDb ID displayed so far. And this is within the body of the card that will contain all the movie data. Okay. Below this, let’s include the ranking value. This is the sentiment that we discussed earlier when we were creating the server side code. So if ranking ranking name So if ranking name is truthy, let’s include this code here to display the ranking. So let’s include a span element like this. Okay. And a class name class name equals to and this is just more Bootstrap classes. So we’re using the badge class and this is just for styling purposes. Makes us makes it easy for us to style our code without having to go into the details of the actual CSS. And let’s include this with a font size. This is just our own custom CSS so that we can override whatever font size is there at the moment. And we’ll include and this needs to be within further curly braces. So we need to wrap this in two curly braces because we’re including an object. So font size and then let’s include the font size which is just one rim here. Okay. Okay. And then within curly braces, let’s include the ranking name, which is just the sentiment of the movie. You know, excellent, good, okay, bad, or terrible ranking name like that. Okay, brilliant. So, we slowly building up our movie component. And then up here, we want to actually include the poster image. Now, this is obviously the most important part of the display for the movie, the movie data, the poster image, because without the poster image, you’re just going to have a boring title and the IMDb value, which isn’t particularly visually appealing. So, let’s include the poster image here. So, to do that, I’m going to create a div here. Okay, div. And I’m actually going to include my own custom inline style here. So style equals and then open curly braces. And then let’s include an object position uh relative here. So this is just inline styling here. And this is how you do that. You create an object to represent your inline style within the JSX element like this. So position relative. And you’ll see the outcome when we test the the code in just a bit. But for now, let’s just create the component. So we want to include the poster image here. And then for the source, we want to include a variable here. So we don’t want those in quotes. So it’s src equals to movie dot poster path. And that’s the poster path that we want displayed for the movie on the front end. Okay, brilliant. And then for the alt, we’ll just include the title like this. Movie dot title like that. And let’s include a class name like this. So card image top like that and then our own custom styling here. So let’s include our CSS code here. So object fit contain like this. And I’m assuming you’ve got some knowledge of CSS. It would take too long to explain all of these properties, these CSS properties 50. But please, by all means, look them up. Very important if you’re a web developer to understand at least basic CSS percent, right? Okay. So, I think that’s okay for now. for our component. It probably isn’t perfect, but hopefully it’ll display all the relevant data on the front end. Okay, so within the components folder here, let’s create a new folder called movies. So this the folders should be just to stick to convention, the folders should have a small letter in front of the folder name like this at the beginning of the folder name. So the first character should not be capitalized. So we got movies and then within movies let’s create a file called movies.jsx like that. So let’s create the code for our movies component. So firstly we want to import the movie component and we can do that like this from and we go dot dot because we’re going back a directory and then you can see in sense has suggested the folder. So movie and then the component is movie like that. Okay. Okay. Movie is declared but it’s not used. Okay. So let’s create the infrastructure for our component. So const movies equals to and then we pass our props through like this and we want movies. So that will be a collection of movie data that we’ve that we will in a bit retrieve from the server. So we’ll contact the movies endpoint and bring back the data. And then this data is passed into our component by the calling client which we’ll create in just a bit. So this one we want to include a movies prop and a message prop here. And then let’s create this as an arrow function like this. And let’s return the appropriate JSX for our movies component. And once again, I’m not going to go into detail about all the the various Bootstrap classes that I’m using here. So class name equals to container. So this is just a bootstrap class and margin top four. This is just wraps or abstracts away the CSS code for the margin top setting. Let’s create another div here. And I’m just going to set the class name to the class named row. And this is for layout purposes. So we’re using the Bootstrap grid system for layout purposes here. And let’s open curly braces like this. movies. We want to check that movies contains a value or is truthy and movies.length. So there is indeed data within the movies collection being passed as a prop to this component. We can do that with this code. Movies.length is greater than zero like this. So if this condition is true go question mark and we can handle the true condition like this. If the condition returns true we want to use movies the collection the array dom map. So we’re using the JavaScript map function to loop through the movie data within our movies collection. And this is how we can do that. So we go movie like this and we use an arrow function to establish our logic. So, so if movies is truly and the movies collection contains movie data, we’re looping through the movies collection and we are going to display our movie which we’ve just created within the movie component and we need to include the key attribute here and set it to a unique value and we can do that through this code. We can set the unique value to underscore the underscore ID property within our movie data. Bear in mind, of course, this data is coming from the MongoDB database. And we’ve got this ID underscore ID value established within the movies collection to uniquely identify each movie. And we’re using this as our key for our movie element here. So this helps react identify each unique element. Okay. And then we go movie. We want to pass down the prop movie to the movie component. So we go movie equals to within curly braces. Then the movie data gets passed down as a prop to the movie element like this. Okay. We can just Close this element like that. Get rid of that. Excellent. Okay. And we have a little issue here. Let’s just check. We’ll check that out in a bit. Let’s just finish off our if else condition here. So, if that’s true, we want to display a movie. We’re looping through all of the movies. So if the movies collection contains data, we’re looping through all of the movies within the collection and displaying the movie on the front end appropriately there. Else here, so the colon signifies the else condition. And then we can just include the message that will be passed down to this component as a prop within this H2 element like this. Okay, H2. and then within curly braces message like that. Okay, let’s just take care of this issue here. So, let’s look at what’s going on here. So, we’ve got our movie component. We’re exporting it as default here. Movie doesn’t look like there’s anything wrong there. We’ve got it components movie.jsx and then within movies. Okay, I’m just going to write the export functionality here. So, we want to export this as default. Export default movies like that. Okay, brilliant. And now this is obviously worrying me here. Uh, I know what actually is going on here. It’s because it’s actually painful when you do this in React and it complains. It’s because I initially um created this file with a with a small M at the beginning of the word movie. And I think the this is why it’s flagging this as erroneous because then when I changed it, it’s not picking up the change for some reason. When I changed it and capitalized this M, it’s not picking up the change. So, this is a bit painful. But to get around that, um, I’m just going to cut that cut that code, put it in my clipboard. I’m going to delete this file. I know this is a bit of a pain. Move to recycle bin. And I’m going to create the file again. New file, this time with movie.jsx. Like that. And I’m going to paste that code that I just cut into this new file. and save that. And let’s see if that’s that has resolved it. Yeah. So, for some reason, I get an issue when I first when when I change a file name, it doesn’t seem to be to propagate through. And if you initially didn’t get the file name right, it can complain when you’re trying to import a particular JSX component within another component. And that’s what was happening there. So, that’s a bit painful, but that’s resolved now. Okay. So, uh, and then we want this displayed on the homepage. So, I’m going to create a component called home. We want the movies displayed on the homepage. So, I’m creating a folder called home. And let’s create a folder called home like this with the name of the folder. The first character of the name of the folder will be in lowerase here. So, we’re sticking to that convention. And then, let’s not make the same mistake. The actual JSX file will now have a capital letter for the first character of its name. So, home.jsx. And we’re now going to create the code for the home.jsx component or file. Right? So, screen there. Right? So, let’s uh do that. So now within this component, we’re actually going to be calling the serverside code. So before we start writing the actual code for the home.jsx file, I’m going to create a folder within src called API. And this will house the code for connecting to the server. Just the basic code for that. I’m going to create a new file called aios config. We’re going to use the package the axios package the axios dependency to handle the infrastructure for calling the server side code. So we actually need to install Axios first before we configure of of course before we configure Axios. Yeah. To install Axios we simply type npm here at our terminal prompt. npm install aios like this. Brilliant. So we’ve got Axios installed and we can now write the just the infrastructural code, the configuration code for using Axios to call our server side endpoints. So let’s go Axios import Axios from oops Axios like this. Okay. And we want to read in a base address. So it’s going to be hosted on local host initially, but we want to be able to configure the base address for where our serverside code is running so that we can easily change this value when we eventually deploy our code. So let’s create an environment file aenv file on the same level as this src directory. So we want to actually create that within magic stream client here. So let’s create our env file like this env. And let’s configure an environment variable. And you need to adhere to a naming convention when you have created your react application using vit. So to do that we have to include vit like this and then our the name of our environment variable. So I’m going to call this API base URL. Okay. And we’re going to initially be running our server side code at local host colon port 8080 like that. So we’ve configured our environment variable. Let’s go back to Axio’s config here and let’s read in that environment variable. So API URL URL like this equals to import and this is how we read an environment variable using VIT because we’re using VIT. We’ve used VIT to create our project. So we have to adhere to certain VIT conventions. API underscore base URL like that. Okay, we can include colons here. Okay, so we’re reading in the base address for our server side endpoint here. And then let’s configure Axios. So export default Axios dotcreate. Let’s open the brackets there and then let’s configure Axio. So the base URL property must be set like this base URL API URL and that’s of course the the value that we’ve read in from thev file. So it’s the environment variable, and then within the header, we want to include this configuration here, headers. And this is an object JSON object content dash type colon and then application for/json like that. We didn’t close this quotation here and that’s why it’s complaining. Okay, brilliant. So we’ve got Axios configured now. Great. So now let’s go back to the home component. So firstly I want to import these commonly used hooks for React. So use state, use effect from React. Whoops. Okay. Then I’m going to import the Axios client. So we’re importing the component that we created it just earlier from the appropriate directory API. So it’s in the API directory and then it’s picked up that it’s Axios config. So we’re importing Axios config component here. like that. Great. So, we also want to import the movies component from this directory. Movies whoops dot movies with a capital letter. And let’s create the infrastructure for our components. A const home. And we do that in the form of an arrow function. Okay. And then we include the arrow. Open curly braces. Okay. And we want to create state for a movies variable. So state variable. And then along with the state variable, we’ve got the set movies function which sets or changes the state of the movies state variable. And we go equal to use state and we pass in an empty array by default. So no movies yet. But we want to populate this movies state variable with an array of movies, a collection of movies which we will retrieve from the appropriate endpoint on the server. Okay. Okay. And then we want to create a loading state variable set loading like this equals to use state and then by default we want this to be false. So it’s a boolean state variable and then a message if in case an error occurs we need to display a message to the screen. set message equals to use state. And now we’re going to use the use effect hook. Use effect hook. So that when this component loads, we call the movies endpoint on the server and populate. populate the movies collection with the appropriate movies data that we retrieved from the server. Okay, let’s go const [Music] like this equals to we want this to be called asynchronously. So we use the async keyword here and we include another arrow function like this open curly braces and within this we create our logic for calling the movies endpoint. So and we’re going to use axios to call the movies endpoint. So we set loading equals to true because we’re about to call the serverside code and we want to display a loading indicator on the front end while the serverside code is retrieving the relevant data and returning it to us. Uh let’s set this message to nothing here. So we’re initializing the message to an empty string here. Let’s include a try catch block here so that we can handle errors if they occur. Okay, there’s that. And a finally block here which gets called whether an error occurs or not. And here we can use this finally block to set the loading boolean value to false because we want the loading indicator to no longer be displayed whether an error occurs or not. So we can include this code within the finally block and set the loading state variable to false here. So we’ve set it to true up here and we’re about now. And now within the try block, we’re going to call the relevant endpoint to get all the movies data. So we’re using Axios client and we go get and this is how we call our endpoint on the server just for/mov which is our endpoint that will return the relevant movies collection to our client React code. And then let’s set the movies state variable using the set movies function to response. data which should contain the relevant array of movies. Okay. And then if response do data. So if an empty array of data is so no movies are found on the server equals to zero then let’s set the message to set the message of the message state variable to an appropriate message. So there are currently no movies available. So if the say the database is set up but the movies haven’t been imported or no movies have been added to the relevant collection in the MongoDB database, this is the an appropriate message to display on the client. Okay. And let’s handle the error. We’re just going to console log the error here like this. Okay. Error fetching movies will be good enough I think. So error oops error fetching movies go like this and then comma error like that. and then set the message to set the message state variable to an appropriate error message. And this is the same basic error message, just error fetching movies. We’ve included a bit more detail in the console window. if an if this if an error occurs at this point if an error occurs when we’re trying to use Axios to fetch the movies from the server. Okay. And that is our use effect hook written. So this hooks into a React life cycle. So when the component loads we’re sending off a call, an asynchronous call using Axios and hopefully the endpoint returns us valid movie data here. And we’re setting that state variable m movies here appropriately. We need to pass an empty array like this to the use effect hook. So that means that when this when this when the component loads this code fires and populates hopefully populates the movies collection with the movies data so that we can now return JSX to to the calling client. Okay. So let’s include empty tags here. And then if the loading state variable is true, we want to just display a loading message. And we can just do that by returning say H2. We’ll include a proper spinner later on as a loading indicator, but for now, let’s just include the text loading dot dot dot and then else condition fires. If the else condition fires here, we actually want to display a gallery of movie posters if you like the movie data. movies equals to and then the retrieved movies within the state variable. The movie state variable should be retrieved and then if there’s a message also passed down the message state variable as a prop to the movies component. We have a few problems here. Okay, what’s going on? Okay, we need opening bracket here. We need an opening bracket here and a closing round bracket here to wrap the else part of the if statement. Okay, so I’m not currently closing this movies element and that’s why we’re getting all these red squiggies. So use a closing tag there. And now our code looks good. So within this fragment here which just means that uh this is a substitute for an element but we don’t want a particular element here like a div or p element for example. So we can this is a way that we can include a parent element without including a particular HTML element for the parent element. So we’re including a react fragment here. So just we can do that by implementing empty tags like this to house JSX elements here. So if so while the data is being loaded while the data is being retrieved from the server this loading indicator should display or else we want the movies displayed to the user. Okay. So now we’ve got all these components set up. It’s still complaining. I don’t know why it’s giving us the same problem as we had before. But I I think I seem to remember that I got it right with this one. Okay. So, this has got a small letter. This should have a capital letter here. So, I’m probably going to have to do the same thing. I’m going to copy all of this code and cut it and delete this file here. Sorry about this. And then create the movies JSX file with a capital letter at the beginning. JSX and then just paste that in. Shouldn’t really have to do that, but So, do the same thing here just to fix this ridiculous problem. Delete that there. Paste that in there. Okay. And that should fix everything. So I don’t know why that seems to cause a lot of problems if you initially get the file naming convention incorrect and you want to change subsequently change it. It seems to cause havoc. It seems to flag a lot of associated errors. So it’s best to get this correct from the beginning I guess. But um to get around it, you can just copy the code to your clipboard, delete the relevant file and create a new file and make sure that the first letter is capitalized. So okay, great. So that’s done. So I think the next step I think we can now that our code looks good, we can go to the app.jsx code here. And one thing I actually want to do is just get rid of all of this here. Okay, I’m going to just get rid of the the app.css code so it doesn’t interfere with our CSS code. So app.css and the other one is index.css. I’m just going to delete all of that. And then we can go to the app.jsx file here. And we want to replace all of this code here. now with just a call to our home component. Okay, we don’t need all this here. And I’m going to import the home component. So, I must make sure I’m exporting it first. So, oops. So type export default default home like that. Okay. So export default home. So firstly we want to import the home component within the app component. So import home like this from comp from then within quotations dot slash components slashhome slashhome like that. And we’re importing the home component. And then we can just include the home element here within our code for now just to test whether we’re able to call the movies endpoint retrieve the data and then display the relevant movie gallery if you like to on the front end. Okay. So we want the server side code now running because we’re going to contact the endpoints running on our local machines. So it’s going to be localhost colon 8080. But in order to run our code now you can see here we’re dealing with running our client application. These are two disparate entities on the on the web now. So we need to be able to run both of these projects, both our client and serverside code simultaneously. So I’m going to create another terminal window to run the server side code. So let’s go into server and then magic stream movie server like this. Oops, got that wrong. CD server. It’s magic stream movies. That’s why that went wrong. Movies server like that. Okay, let’s clear the screen and we can run our code. Let’s go. Go run dot like that. So let’s see what happens here when we go to our client code and run it. So let’s run. So, npm rundev like this to run our client code. Okay, so we’re missing something here. Okay, so when we try and run our React code, we’re getting this issue here. Failed to resolve import B bootstrap disc CSS bootstrap and we we installed React Bootstrap, but we didn’t install Bootstrap. We have to install Bootstrap in order for this to work. So this will mean that we can include the you know the react bootstrap allows us to include various elements within our code various React Bootstrap elements like button and things like that. But in order to use the actual Bootstrap classes and for this importation code to work we actually need to install Bootstrap separately from React Bootstrap and that’s why we’re getting issues here. So, I’m just going to press Ctrl C. And to install Bootstrap, we just type npm install bootstrap. And that should resolve our issue. Excellent. Let’s clear the screen. Let’s try run our code again. npm run dev. Okay, looks good. And let’s launch our client code. Okay, so that hasn’t given us the same error, but I think there is another problem and I’ll explain what that problem is now. We should, if I’m not mistaken, get a cause error. Oh, okay. So, let’s run our code. mpm rundev, our client code here. We’ve still got our server side code running here at this point, 8080. And let’s copy this URL for our client React code. And let’s paste that here. Press enter. And nothing’s happening. And let’s see if there’s any issues. And there’s no issues apparently. So what could be going on? Okay. So I strongly suspect that I’m not calling this fetch movies function. we still have to call this function. We’ve created it, but we’re not calling it anywhere. So that would be the problem there. So all we do to fix the problem is below the function. So within the use effect hook, we call fetch movies like this. And so now when the home component loads, this fetch movies function should be called. So let’s error fetching movies. Great. So that’s better. Okay. Okay. So, let’s look and see what errors have been printed to the console. And it’s an Axio error. And here’s what I wanted to see. So, this is actually an expected error. What’s happening here is we’re getting blocked by cause a cause policy which is by default active within your browser. And so our client on port 5173 is being blocked on the server here so that we we’re not able to access this movies endpoint by default. First of all, what is cause? It stands for cross origin resource sharing. This is a default security precaution built into browsers that serves to prevent malicious websites from accessing sensitive data from another domain. And you see we’re running the React client at a completely different disparit location to where we’re running the server. So we’re running the client on port 5173 on our local machines and we’re running the server side code on port 8080. So this is considered two disparit domains. And then cause kicks in and stops our client from accessing the endpoints on our server. So cause stands for cross origin resource sharing. This is a default precaution built into browsers that serves to prevent malicious websites from accessing sensitive data from another domain. So basically the client and server code are running on different domains. So our browser is stepping in and not letting our client communicate with the servers, not letting us call the endpoints on the server from the client. So to resolve that, we need to include appropriate code on the server. Uh, so I’m just going to stop the client from running by pressing Ctrl C. Cancel out of that. And we can stop the server from running too. So let’s go to this terminal and do the same. Press Ctrl + C. Clear the screen here. And then let’s go to our main method in the go code. So we’re returning to our go code, our server side code. And we need to write code to prevent cause. And we can do that by explicitly including code to prevent cause within our serverside code within the main function the entry point function of our serverside code. So firstly we need to include a package to deal with this cause related issue. So let’s use the goget command to install the appropriate cause package so that we can leverage the cause functionality to prevent cause from interfering with our client communicating with our server. Okay. So to install the relevant cause package to help us prevent cause from interfering with our client communicating with the server, we need to install this package. So we can use the goget command for that. So go get and then github.com [Music] sljin dash contrib like this and then slashcores press the enter key. Okay, it’s downloading the cause package for us. Excellent. So that seems to have been successful and then we can include the relevant importation code at the top of our main file here. So within our main package here we include this package importation code to import the relevant cause package so that we can handle cause appropriately. Okay. Okay. So now we can create a variable called config and we can configure our cause related code appropriately cause config like this and then let’s configure cause appropriately. So configow I’m going to say allow all origins and I’m going to set that to true. Now note that we will have to change this code before we deploy it because allow all origins will not work if we’re using HTTPS. So to secure our full stack application, we are going to use HTTPS when we deploy this code to the production environment. So we won’t be able to use allow all origins equals to true like this boolean field like that. But for now, let’s include that for testing purposes. We can we can do that for now. Okay. Allow all origins and then config dot allow methods. So we’re allowing the following HTTP methods with this line of code here string. And let’s include get post. And we’re actually just using get post and hatch within our code. So I’m just going to include these methods here. Um this should be within curly brackets and not round brackets like that. Okay. And then config dot allow headers like this equals to and it’s another string array and let’s include origin content dash type and authorization like that. allow these headers. Okay. And then config dot expose headers equals to string array. So these have to be curly braces. on content hyphen length like that. Okay. Okay. And then let’s configure router use and let’s configure our cause settings dot new and then config like this. Okay, this is a bit of a pain to have to set all this up to bypass cause, but okay. So, one more uh configuration settings. So, config dot whoops dot max age equals to 12 times time do hour like that. Okay. And you must make sure that you’ve got time package, the time package imported here so that it can be used down here appropriately. And the other thing that I haven’t done quite correctly is these this code here must be above where we’re setting our roots here. So this code must be called before the root configuration code that we created earlier. So that cause will be handled effectively. Okay, great. So now let’s go to our server side. Let’s go to the terminal prompt for to run our server code. So go run dot. Excellent. So it’s listening on port 8080. So let’s run our client react code. And to do that, we go npm rundev like that. Okay. And we can now copy this URL to our browsers to test our code. Excellent. So, as you can see, we’ve got round we’ve got around the cause issue and our movies are being displayed in kind of a gallery, a grid within from within our React code. Excellent. So, that is a great start for the creation of our React client code. Okay. So, we’ve now created the functionality for displaying all the movies. available on our Magic Stream website to users which are and these movies are publicly accessible to everyone. But in order to stream them, you must log on and authenticate before streaming the relevant movies. So the movies are being displayed on the home component. We’re drilling down to the movies component here. And then each movie is displayed. Each movie is being displayed here within a loop through the movie component here. And this is the movie component here, which is basically just a card which establishes the layout for each of the relevant movies. And we’ve got Bootstrap here for styling the relevant cards displayed in like a card deck if you like or like a gallery with the poster images displayed in a gallery for each movie. Before we create the front end user registration and login functionality, we need to add links to the front end so the user can navigate between the main pages. We’re going to establish the relevant links within our header component. So the header will reside at the top of the website and will contain all the navigation to the main pages, login, registration, home etc. We are going to leverage the react routter DOM package to manage our client roots because the linking to the relevant pages is all um part of the rooting system. So each page has to be configured within our rooting system and what’s governing that rooting system if you like is react rout. So this is a package we need to install react routed DOM. So to do that let’s go into the client forward slash client magic stream client like this. And then within this directory we can install react rout. And to do that, we type npm install react router DOM like this and press the enter key. Excellent. So we’ve now installed React router DOM and we can make use of that within our header component which we are about to create. So let’s do that. Let’s create the header component. So within the components folder, we simply create a folder called header. Let’s make sure we stick to protocol and the folder has a lowercase H at the beginning of the word header like this. And then let’s not make the same mistake as we made earlier with the movie and movies component. And let’s make sure that the JSX file first character in the name is in uppercase. So we call this header with H and uppercase JSX like this. JSX. and we’re going to create our component within this JSX file. So let’s start at the top and let’s import the button from React router. Sorry, React Bootstrap. So React Bootstrap slash button. So we can make use of the button element imported from React Bootstrap. If you want to learn more about React Bootstrap, please navigate to their website within your browser. the React Bootstrap website where you can get more details about the relevant uh components available to you through React Bootstrap. Okay, let’s import container. So, we’re importing the container element from React Bootstrap. React Bootstrap and then forward slash container like this. Okay. Whoa, I haven’t yet mastered the art of typing and talking at the same time. I am improving, but uh just bear with me. Okay, so then import Whoops. See, look at me. Import nav like this from And we’re going to import the nav element like this. React dash bootstrap nav. And you’ll see these elements will be placed in their relevant context as we develop the code for this component. So okay, so navbar and we want navbar import nav bar. So all of our navigation is going to reside like for example buttons that link to the register page or the login page are going to reside within the navbar situated at the top of our front end. Okay. So react bootstrap slashnavbar like this. Okay. And let’s now import the relevant components from react router DOM. And this is these components are extremely important because this is how we can establish a centralized routting system for our gosh for our front end. Navigate. So use navigate hook. We want to use navigate hook. nav link like this and link and you’ll see how we use these in our code in just a bit from and react router [Music] dash dom like that. Brilliant. Okay, let’s create the infrastructure for our component. So const header equals to we won’t pass any props in at the moment but a prop for logging out of the application will be passed in at a later stage when we get to that functionality a little bit later. So const navigate like this equals to and then I’ll use navigate hook like that. Okay. And then let’s return I think. Whoops. That doesn’t look quite right. Something’s not quite right. It’s because I haven’t made that an arrow function. And that’s what all the fuss was about there. And let’s go return. Okay. And then within round brackets, we can include our JSX code. So let’s start with a navbar which we’ve imported from React Bootstrap here. So let’s include a navbar element like this. And just bear with me while I include certain attributes here. And I’m not going to explain all the attributes, but you can, as I said, go to the Bootstrap 5 or Boot or React Bootstrap websites and you can read about and you can glean whatever details you wish from the content published on on those websites. Okay, LG. Okay, like this. Uh this should let’s just close that nav bar. What’s the enter key? So expand equals large. Okay. Then class name equals to shadow SM like that. Okay. And then within the navbar let’s include the container element. I’m just going to create the tags and go navbarbrand. But ordinarily, Visual Studio helps us out and creates the actual tags for us when we press the tab key after entering the name of the element. Okay. And for now, for the brand, I actually am going to include a brand. I’ve created a an icon that we can use for Magic Stream brand. But for now, I’m just going to include Magic Stream here like this. This is the name of our fictitious company. So, for now, we’ll just include that for our branding. We’ll get back to the the actual branding code in a bit. Okay, let’s include our tags first. This is one with a dot in it also. So, bar dot toggle and the significance of the navbar. Okay. And this is a we can close the navbar. We can close the navbar toggle element like this. Okay. Get area controls equals to main navbar nav like this. And these are just Bootstrap 5 classes that we’re using here. And the significance of the nav toggle is that on smaller screens the menu you can close and open the the menu bar using a particular toggle which is useful for smaller screens. So on a desktop computer for example the browser being in full screen uh you won’t need this toggle but it will appear automatically on smaller screens so that the menu bar can be opened and closed um as required. So it’s for responsiveness and that’s basically one of the advantages to using Bootstrap in general is for screen responsiveness. So that’s why we’re using this nav toggle this navbar toggle element here. And let’s create the actual collapse functionality. So this is what we want to include in the collapse of the menu. the items, the navigation bar items that we want to include. When the menu bar is expanded and collapsed. So when the menu bar is expanded, we want the following items included within this. So if you want to create the tags and the element at the same time within VS Code can make your coding a little bit more efficient. Just type nav that’s the type of the element and press tab like that and it’ll create the opening and closing tags for you. Okay. And then let’s include class name like this equals to me auto. Okay. So within this nav element, this parent element, we want to include our our link, our home link. So let’s create our tags. Then nav.link like this. And let’s close that off. nav.link. So we’re including a link within the nav element. nav.link element. We go as equals. We want this to be part of the React router DOM functionality. So we include the nav link element here. And this is what we’ve imported here from React Route DOM. And this is how we’re establishing this as a link. So Bootstrap knows that this is a link from React Route to DOM and will manage this linking functionality for us. Okay. And then we include the to attribute and this is where we want this link to link to. So the page will be just forward slash which is the homepage and then within here we’re just going to include the label for the link which is just home. Okay. So this is the homepage for slash the root of our navigation. So when the user clicks on the home link it of course takes the user to the homepage. Okay, excellent. This is when the user logs on and the user wants to see the user’s recommended movies. And we’ll create the functionality for this component a little bit later. So recommended is the name of the page that we’re going to create name of the root if you like that we’ll create in a little bit later. And let’s label this as recommended like that. Okay, brilliant. Um, and I’m just going to temporarily create a state variable here. So, we haven’t yet imported use state. So, we need to do that. So, let’s import use state like this from React. Okay. And I’m just going to create a state variable. So for now I’m just going to create a boolean state variable for this purpose just for testing purposes equals to use state and I’m going to set this to false ie the user is not yet logged on. Okay and this is just for testing. Okay so we got those links home recommended which will be displayed to the user. Okay regardless of whether the user is logged on or not. Okay. And let’s now create the links within the navbar for the registration and login buttons and the logout button. But as I say, depending on what mode you’re in, but depending on whether the user has been authenticated, different buttons will show up for the different scenarios. And let’s look at the details of this now. So let’s click, let’s type a nav like that. So we’ve created our nav elements. Within this nav element, we want class name equals to the following classes. MS auto space align. Whoops. Align items center like this. Okay, great. And then let’s include empty tag. So a fragment here. Let’s include a span. So this is for the scenario where the user’s logged in. So I’m just going to type span close span. And we’re not doing the login functionality just yet. So for testing purposes, I’m just going to go hello and then within strong whoops within strong HTML elements, I’m going to say name like this. So hello name, but this will be replaced with the username of the actual logged in user later on. Okay. And then below this, we’re going to include the log out button. So button. So this is when the user is actually logged in. Let’s go variant equals to and this is just for styling purposes. We’re including these bootstrap related classes out line like this and then size equals to SM. Okay. And then we’re going to include an on click, but we’ll include that later because we’re not going to include the authorization functionality till a bit later. So we’ll leave out the on click function for the buttons for now. Okay. And let’s go log out there. Great. And then so for this one if orth is truth the if our state variable is truth the then and then open brack open round brackets like that and we need to actually put this bracket at the end here like that. Okay. So if orth and then question mark we want this true when the author is set to true or is truly we want these elements to be displayed. Okay. And then here we close the round bracket and we include a colon and we want to include now what happens when author’s falsy meaning the user is not logged on. Okay, we need to remove this round bracket here. Oops, put another question mark. Okay, like that. And that looks much better. Now we need to include the code here. And this code will be for displaying the register and login buttons. In fact, I’m just going to copy and paste these in to save time. And you can see here now we’ve got the register button here and we’re navigating to to the login and we’re navigating to the register form here. Sorry, the register page here. Okay, let’s just tab that in a bit. Okay, so if that means the user’s been authorized, we’re displaying the log out button. And if the user hasn’t been authorized, we’re displaying the login and registr and registration button here. And we you can see we’ve got an onclick event included within this button here. So we need to we’re going to in order to navigate to these pages which we haven’t yet created. We need to use the use navigate hook. So we’ve got our navigate variable here which has been retrieved from the use navigate hook here and we’re using that method here to navigate to the relevant pages. Okay, we’ve got one little issue here. This collapse element should be closed down here. We want all of these as part of a collapsible navigation bar. So we must include collapse element here. We must close it down here. Okay. And the other thing is the container element. also close the container element down here. Everything, all of these navigation links or buttons must be included within the container like this as well as the collapsible menu bar. Okay, great. Nav should probably be tabbed in a bit. Okay. Okay, let’s make our code look beautiful. Okay, there we go. I think that’s pretty good. So, if we go to app.jsx, JSX. Let’s include the header component here just for testing purposes for now so that we can see what’s going on. Header from header. So we’re importing that. Oops. And I don’t think I’ve exported it yet. So that’s something that we can’t forget. So let’s export header as default from this component. header component default header like that. And now we’re importing it within the main parent component, the app component here. And then we can just include it above include the header element above the home element. And we can see kind of what’s going on now. So for testing purposes, this should be good. Um, and let’s open another terminal window here. Go CD server. We want to run the server. Magic stream movies server. Hopefully that’s correct. It is. And let’s run the code. Go run dot. We’re running our server side code now. And let’s run our client side code. So, npm rundev like that. Okay, let’s copy this URL to our browsers. Let’s see what we got here. Oops. Okay, we got a problem. Failed to resolve import components. Oh, home. So, let’s leave that up there. Um, okay. Right. So, we’ve not quite got that correct. Of course, this needs to be header. Let’s save that and see if the error goes away. So, far. Okay. New dependencies. Okay, let’s just see what’s going on here. So, nothing’s displayed. We have issues here. See, go to dev tools. Error caught use may be used only in React router DOMJS routter component. Okay, so we can’t actually use that yet. Okay, let’s resolve this, right? Okay, I understand what’s going on there. Okay, so we need to go to the main.jsx file here and we actually in order for those for that routting functionality that we’ve included in the header component, we need to wrap our app component within a parent component that’s taken from the React rout package. So let’s do that. Okay. So let’s import the following from react rout. So import curly braces browser router. We need to include that there. And then roots like this. And then root. We need to include this code here. So we need to take that out there. Include browser routter like this. Within the browser routter parent component, we include a component or element called roots like this. And then within the roots element, we include a an element called root like that. and we go okay and we can close this like that. So let’s remove that and then path equals to okay so for slashstar like that and then element equals to app like that. Okay. And I think that is correct now. Um, see what happens. There we go. Look at that. Okay. So, we’ve got our login. We got our register recommended. We’ve got all of that now in place. Excellent. We go to a small screen. You can see what I was talking about with the toggle. You can now toggle those open and closed if you on a smaller screen like that. And we can neaten up all this a little bit later. But yeah, so our routing is pretty much working. I mean, our header functionality is pretty much in place. That looks pretty good. Okay, good. Okay, so the next thing we want to do is complete our roots. So to do that, we’re going to import the necessary components from React roottodom. So root. Sorry that should be capitalized. Brute roots. and then use nav gate from react rout. Okay. And now we can configure our roots. So this pretty much stays where it is. So let’s include the roots element. Okay. And then our fish root and this is for the home component. So path equals to. So this is a route to the home page. Home component element equals to then within curly braces we can include our home element like this. Okay. Okay, we need to include them within this tag like that. Need to include the root like that. The home element within the for the element attribute set to the element attribute within the root element like this. And this is how we configure our roots and react rout. Okay, great. Let’s create one for register 2. So path equals to register like this. We’re going to create a component for register in a bit. Like that there. And let’s do the same for login. For the login route, let’s just make a duplicate of this route and change the relevant bits. So login like that. login. Um this should be capitalized login. So we need to create these components. So let’s do that. So within the components, let’s create a folder for register. So red, whoops, this should be in lower case register like that. And then within the register folder, let’s create a file called register.jsx. register.jsx [Music] like this. Enter. Okay. I’m just going to create just the basic structure for the register component. We’re not going to include any of the functionality just yet. We just want to get it navigating correctly to the register and login pages first off and then we’ll create the functionality for these two pages. So for now, let’s just include very basic functionality. So let’s return just H2 uh register like that. So it’s just very basic functionality to include an equals here of course. So we need to export the register component. Export default register like that. Okay. And now we need to create the login component. So let’s go to components there. So let’s create the folder for the login component. So login then within the login folder, let’s create the login.jsx file with the L capitalized login.jsx. Enter. And I’m just going to go to register here. I’m going to select all, copy and paste. Let’s paste that within our login.jsx file and change the relevant bits. Login, export, login, login like that. Okay, excellent. I think that’s pretty good. So, we’ve got the components in place. So now we need to import them within our app.js file and create the relevant and configure their roots. So header let’s go red just like that reister range. Okay, let’s duplicate that and do the same for the login element. Login component. We’re importing the login component here like that. Excellent. And now we can create Oh, we’ve created the roots of course. So, they’re already in place and we should be able to navigate to those roots now. We should have very very basic rooting functionality in place now and we certainly don’t need that anymore. The counts that was added by default through vit. We should be able to test that now. Let’s just see what’s going on there. So our serverside code is still running. Excellent. So let’s run our client code. MPN npm rundev like that. and let’s copy the URL. Paste it here. Okay, look at that. Looks pretty good. And let’s see if we can navigate to the login page. Login. Excellent. So that routting functionality is basically working. Let’s go to register. Login. Let’s go home. So we got home. We got login. We got register. And now we just need to create the functionality for the login and register components. We’ve created our header in its most basic form and now we want to create the login and register functionality. Excellent. Okay. So in order for users to make use of the magic stream service, a user must sign up with the application. To do this, the user will access the page that we are about to create and provide the user’s relevant registration details. first name, last name, email address, password, as well as the user must also choose the user’s favorite movie genres, thriller, drama, comedy, action, crime, that sort of thing. Our system needs this information, the genre information, in order to recommend movies that the user may find most appealing. That is one of the core services that the Magic Stream website offers, a recommendation service. So once the user registers with the user’s relevant details, the user will be able to log in to the application by entering the user’s email address and chosen password. The user will then be able to make use of the applications services. Okay. So before we create the registration form, we’re actually going to go back to the server side code and we’re going to create an end point where we can get all the genres. And if you look at Compass quickly here, I’m just going to launch Compass. Let’s connect. So if you look at the Magic Stream Movies database here, and we go to this collection called genres, you can see that I’ve created a collection that we imported earlier on in the course. This collection contains all the relevant genres that will display within a multi select box within a multi selection box that the user so the user can select multiple genres before the user registers with the system. And as I say, this is all part of the registration functionality. The user will select one or more genres that the user most appreciates. So the user basically wants movies in the relevant chosen genres recommended from our recommendation system. That’s what it basically means. So firstly, we’re going to create a endpoint on the server before we actually create the client side registration functionality. We’re going to create this this server side endpoint within the movies controller here. Movie_controller.go. So the the um the controllers package within the controllers package within the movie_Controller.go file. We’re going to scroll right to the end here and we’re going to create a handler function for an endp point that can be used to retrieve all of the genres that are stored within this genres collection here. Okay, great. So, let’s do that. Let’s create a function. And this is just a basic function. Nothing too hectic. Let’s go funk and let’s get genres. This is the name of the handler function and we want this to hook into genonic. So jin dot handler funk like this. We’ve done this all before. Let’s open curly braces and let’s return an anonymous function that accepts a jinggonic context as an argument. So a C parameter of type jin.context context is part of the function definition of the anonymous function that we are returning from the get genres handler function and we’re going to include the logic within this anonymous function here. So this allows us to hook into JgonX web framework and map this function to an endp point that the user can access from the client via HTTP. Okay, so firstly we do the usual. Let’s just in fact I’m just going to copy and paste this code and I’m not going to offer any explanation but we know that this is part this is a way for the resources to be managed. So once the relevant queries for the MongoDB for go driver have been executed there may be resources hanging about in memory and this ensures that whether the query fails or works that the relevant resources are cleared out of memory appropriately. So this is just housekeeping code that we need to include here. And let’s create a variable called jean and it’s of type model. So it’s an array of model dot genre types. And it’s in fact models. So that should be models do.re. And this is where we’re going to store the movie genres that we retrieve from the genres collection that resides within our magic stream movies database. Okay. Excellent. And of course, if we go to the models package, so we go to movie.mmodel, we’ve got the genres, the definition for the genre type or strct here. Excellent. So, let’s go back to movie_controller.go and let’s complete the logic for our get genres handler function. Okay. So, we need to create a cursor as we’ve done before and we want to create an error object here. And then let’s use the operator and genre collection. And I’m not sure we’ve created a genre collection yet. Find Okay, let’s just check if we’ve got a genre collection here. I don’t think we do. We haven’t got a genre collection defined. So, let’s do that. Let’s duplicate the rankings collection here or ranking collection. And let’s make this genre genre collection. And let’s call this genres because that is the name of the collection within the database here genres. So we’re creating the collection variable here. And we can go back down to the bottom now and continue with our code. Genre collection dotfind. And now we’ve got IntelliSense. Great. And then let’s pass the context here. And then bson.m so we want an empty bson.mm here because this is to do with the bson collection that we are retrieving from the database. This is the type. It’s an bison format that we’re collecting that we’re retrieving from the genres collection within our MongoDB database. So that’s what that is there. And then we do the usual go thing. We check if error is not equal to null. Meaning we need to handle an exception caused by executing this query. We can handle it like this. C.json and open braces htt http.status internal server error comma and then jin.h. We’re using uh we’re using generic jin functionality here to return an error in JSON format to the client. And let’s give it an appropriate let’s uh include an appropriate error message. Error fetch fetching movie movie genres like that. Okay, brilliant. And then let’s include the return keyword here so that no further code executes from within this get genres method. And then this is all part of the resource clearing code. Defer cursor. So all the resources related to this cursor must be cleared. And this is what we’re doing here. Let’s pass in the context ctx. We’re making sure that those resources are cleared. No matter what happens within this function, whether an error occurs or it’s successful, we need at an appropriate time, we need the resources associated with this cursor cleared from memory. And that’s just good programming practice. And let’s carry on. So if equals to and then cursor. Now what we’re doing here cursor, we’re calling the all method. And we’re going to put we’re going to put what’s in the cursor into the genres array. That’s basically what we’re doing here. And remember the amperand refers points to an actual memory address where that data will reside. So the genres the genres variable will point to a memory location where the genre’s data will reside. Okay. And then we continue on the same line here. So we’re checking we’re we’re returning an error object here. It’ll be null if this is successful or it will contain a value if it’s not successful and we can actually using this semicolon we can check the error on the same line like this. So if it’s not equal to nil here open curly braces and we can handle that exception http dot and it’s just a status internal server error and let’s use jin to return wellformed JSON to the database uh to the client. Okay. And we’ll just return what’sever in the error whatever gets returned in this case from the error method here which is called on the error object. Okay, this should be cursor and let’s press the enter key and include the return keyword there. And then we’re in good shape if the code gets to this point and we can return JSON with a HTTP status. Okay, http sorry HTTP dot status. Okay. And then the genres collection. And we’ve now written our end point. And let’s now go to the unprotected roots where the unprotected roots have been configured. Um, so we go to the roots folder here, unprotected roots, and let’s configure it. And it’s just a get request. So it’s get, and this will be just genres. So this will be the path part of the path to the end point. So it’s obviously the endpoint consists of a base path which will be localhost colon 8080 when we’re testing it on our local machines forward slash genres to get those genres and then we map it to get genres the function the handler function that we’ve just created. Okay, that hooks into Jenonic. Excellent. So now we can test that. So I’m going to run the server code. Let’s go to this terminal window here and type go run dot to run our code. Okay, so it’s listing on port 8080 and let’s go to Postman and just test that endpoint is valid is working correctly before we create the registration functionality. So here let’s replace this with genres like that. Let’s change this into a get request and let’s run it. Okay. And has it returned the genres? It has. Look at that. We’re in good shape. Excellent. So, let’s move on and create our registration client side registration functionality. So, we can press Ctrl C to cancel the Genonic web server listening on port 8080. And let’s go back to the client code. And now we can develop the registration functionality. So let’s do that. Let’s go to the client code here. Let’s go into src components. And remember in the last section of the course, we created this registration folder and the register.jsx file. And within this file, we’re going to now create the registration form. And you’ll see a little bit later how we need to call the genres endpoint to retrieve those genres and then we’re going to display those genres in a multis select box that the user can exploit to select the various genres before registering with our system. But let’s take care of the basics first. I’m actually going to do a lot of copying and pasting. I’ve prepared this code offline. We don’t necessarily need to go through all of this code line by line. It’s just going to take too long to do that. So I’m going to just paste in all of these importation lines of code here. So we’re importing state use effect container button form. We’re going to be uh creating a registration form. So we’re going to make use of this react bootstrap form element here. And then of course use navigate and link. We’ll make use of the use navigate hook and the link element. And then let’s go through this fairly quickly. So I’m going to just paste in this code here. So you can see here we are creating a state variable for each of the fields that the user must fill in when completing the registration form here. So, first name, last name, email, password, confirm password. The genres in the set genres function here will be to do with the endpoint that we’ve just created. The favorite genres and set favorite genres are to do with the genres that the user selects from the multi select box that we’ll include on the registration form. Okay, great. So, I think we’ve got the gist of what we’re doing here. Let’s move on. So let’s just remove this JSX code here. Create a container here. And within the container, we’ll include the form. So let’s close container off here. Okay. So let’s let’s include the form and I’ll just explain what’s going on here. I’m going to just include all the fields within the form within our code. Okay. So I’m going to copy and paste that in there. So this is the form. that denotes our registration front end form here. So, we’ve got to handle submit. We have to handle submit once all the fields have been filled in by the user. We’ve got the first name field here and we’ve got a placeholder within the text box. Enter first name. We’ve got value equals first name here. So, this is the state variable that we’ve created up there. And then we’ve got set first name as the as to to set that first name state variable here when that text box changes. And the pattern is exactly the same for the last name. And then same for the email. And we’ve got a form control. And you see this code is all available to you in the relevant GitHub repository. And that GitHub repository URL is included in the description below. And you can just literally copy and paste this code. Um, so I don’t want to go through and explain every all this code line by line because I think it’s fairly self-explanatory and you can always look up the various elements from the React Bootstrap website or the other Bootstrap websites that exist where you can learn about Bootstrap and styling and layout and that sort of thing and CSS. Of course, that’s not what this course is really about. This course is more about developing a full stack application using Jin Gonic and React. So it’s not necessarily about the styling and um all of the front-end code that goes with it. That’s more ancillary to the end goal of this course. So please by all means look up all of this code here if you’re not sure of what it’s doing. Okay, so here we go. We got favorite genres.m map. So this one needs a bit of explanation. Form select here. This is where we’re creating that genres, that collection of genres within a select box that the user can use to select the genres that the user prefers over all the other genres. And that’s to do with the recommendation service that is offered by Magic Stream. So, genres.m we still need to write the functionality to call the genres endpoint. So, we can populate this genres collection here on the client. And then it’s adding the various genres to this select box here. And you can see each option denotes a genre. Excellent. And then down here, we’ve got a button to actually register while the users information is being processed on the server. spinner is displayed within the actual register button here and that’s what that code is there and uh the the button will be disabled also while the the various fields are being processed but as I say please go to the go to where the code is hosted on GitHub and you can just copy and paste the code I’m not going to go through and explain this line by line because that will just take too long and then we’ve got onsubmit equals to handle sububmit and we need to write that method but Before we do that, let’s include a header. And I’m just going to paste it just above the form here. Okay, brilliant. We actually need to close the div. That div is associated with that div there. And we need to close the div here, just above where it says container here. And that’s just a header. Okay, brilliant. Excellent. So, create your Magic Stream Movie account is the header there. Um, we’ve got a logo image which we will include later. So, I’m actually going to just delete that for now and we’ll create that a little bit later so that we can brand our website appropriately. Okay. But let’s get in the basic functionality. Let’s get the basic functionality working for now. Okay. And then above return here, now we want to include our call first of all to the get genres um function and we’re going to use a use effect for that. So I’m just going to paste in this code here. You can see here we’re including the use effect hook. We’re calling the genre’s end point and we’re populating the genre state variable here. And of course, if an error happens, we’re just handling that error by writing the relevant error to the console window. And then of course we have to call the fetch genres method that we created here from within the use effect hook. And what this means here, this is an empty array. So when this component, the registration component or the register component first loads, we want this to immediately get called and populate the genre state variable there. Okay. So we created that use effect there. So we need to create a function here which is being called um handle genre change. So every time a user selects makes a different selection within the genres selection box, we need to update the favorite genres variable. And this is what that is doing here in this code. Handle genre change. So, we’re updating the favorite genres state variable which stores an array of genres or a collection of genres and it’s set whenever that selection changes within the select box on the registration form. The set favorite genres is appropriately being called here from within this function and it is changing the various options within the or the various collection items within the favorite genres collection. That’s what this code is doing here. And we’re calling that you can see here form select. It’s a multiple select box. And we’re calling that every time the value changes. So on change it sets it to handle genre change and that’s the function we’ve created here and it updates the favorite genres array and that function is handling that and then we need to include one more function. It’s very important function and this function handles the event when the submit button is clicked by the user. Okay here. So you can see we’ve got our various settings here. It’s creating a payload of the users’s details. So it’s creating a variable called payload. First name, last name, email, password, and then the ro default role. If a user is using the registration form, the default role is user. An admin account can only be created on the server. So if it’s created through the client interface through the website then the default role is always user. So that’s what that code is doing there. Oops, just gone a bit far there. So user we’re setting the default role there and the payload is being set appropriately. The favorite genres are being set to the favorite genres here. We’re creating the payload and we’re posting that to the register endpoint. All of these all of this data here, we’re posting that to the register endpoint and it’s being processed and then we’re navigating to the login page here. Okay, excellent. So, we’ve got our registration code pretty much set up. Might be a few little things we need to adjust here within the handle submit. um form here like navigate to login. I don’t think we need to do that right now. Oh, we can do. Yeah, we can just navigate to login. Okay, once the user is registered and I think we can potentially just test that now. Let’s see what that looks like. Anyway, I think we’ve got everything. Okay. Okay. And let’s let’s run the server code first. Let’s go to the server terminal window. Oops. And let’s run let’s firstly clear the screen and let’s go go run. Oops. Go run dot to run our code. Okay. Let’s go to the client code and let’s go npm rundev like this. And V is now running our client side React code on our local machines. And we can now we should be able to now test our code. We’ve got the server side code running. And we can go through our browsers to the client side website. Should load up our movies. Great. So, we’ve got our movies loaded here now. Okay. And let’s see if we can register a user. Let’s go to register. Look at that. So, that’s looking pretty good. And you can see here we’ve got our genres here. And we’ve got instructions on how to use this genres select box here. Okay. That doesn’t look quite right there. Genres. Oh, it is. Okay. So hold control in brackets Windows or command in brackets Mac to select multiple genres. So if we hold down the control key if we’re on a Windows machine, you can see how we can select our genre. So let’s select comedy, western, fantasy, and thriller. Why not? Okay, so this user likes all these ones here. And I’m going to register. I don’t know. Have I registered Gavin Alon yet? I don’t know if I have. Let’s go to users. Bob Jones, Sarah Smith, Ben Madison, Craig Denton. Oh, I have. Okay, so Gavin LS. Let’s select someone else. Let’s select Goth. You can always tell when a name is made up, can’t you? Goth Jenkins. Goth Jenkins. Just sounds like a madeup name. Okay. Anyway, so Goth Oops. Goth. I bet you there is a GT Jenkins out there, though. Jenkins at Hotmail. Whoops. Let us know in the comments if your name is Genkins. Genkins atotmail.com. Okay. Genkins. Password. Okay. I’m just going to select password one exclamation point. Confirm. Password one exclamation point. So, we just stick to one password. Just makes it easier to test our code. And then we’ve selected our genres here. And let’s see if we can register this. Okay. So, register. Okay. So, hopefully that’s worked. It’s registered the relevant uh user Goth Jenkins. We can we’ll check that out in Compass in just a bit. And then it’s navigated us to the login screen, which we haven’t yet developed. and let’s see if Goth Jenkins exists now within our users collection. Okay, so let’s go view reload data. Goth Jenkins. Look at that. And you can see his password has been encoded appropriately. He’s part of the user group. No tokens yet because he hasn’t logged in. And we’ve got his favorite genres here. comedy, western, fantasy, and thriller. Awesome. And now we want to create the login screen so that GTH can log into our system. Okay. So, let’s do that. That’s the next step. I’m going to cancel this here. Ctrl C to cancel when you’ve when uh it’s listening on your local machine. Just C through Visual Studio Code cancels that operation. And now we can go back and now we can develop our login screen. So let’s go to login here. And I’m going to do the same thing. I’m I’m not going to explain this line by line because it just takes too long and this course is already huge. So okay. So anyway, let’s paste in these this importation code. Oops. This importation code here. So we got use state container. We got all these react bootstrap react elements that we’re importing here. We got we’re importing axios client and the various react routed DOM elements and this use navigate hook here. Okay. And let’s let’s develop our login code. Let’s do a similar thing as we’ve done before. So we’re going to include our state variables here. Okay, we’ve got a state variable for email, password. Okay. Right. And we got a state variable for email and password. Okay. There’s not so much to fill out in this form. We’re obviously just logging on using a unique email address that we chose when registering and the password, which at the moment is just password, one exclamation point with the P capitalized. We’re just sticking to a standard password. Okay. And then we’ve got all these various things here. We haven’t written this use thing. That’s part of the next part of the course. So, I’m removing that. Okay, we’ll keep all the rest here. And the form looks something like this. I’m going to copy and paste the form which I’ve already obviously developed. And you can just paste this from you can copy and paste this from the relevant GitHub repository. The URL to that GitHub repository has been included within the description below. Pasting in that code there. Okay. And all it is really, okay, we don’t want this yet because we haven’t yet branded our website. So, I’m just going to delete that for now. Welcome back. Please log to your account. That’s the header right there. And then we’ve got these groups, these React Bootstrap groups for each of the fields. Email address here. So, value equals email state variable. when it changes, when the email address form control changes, we have to set that email state variable accordingly. And this happens before the user hits the submit button to log in to the magic stream website. We’ve got password. It’s the same pattern here. Excellent. And then we’ve got this button here for submitting to the login endpoint. Submitting the payload information, which should be the email and the password to the login endpoint here. And we’ve got a link that links the user to the register form if the user has not yet created an account using the register form. Okay, so that’s all that is happening there. Okay, we go along. We’re not yet using these. We’re not yet using this use location hook or this use navigate hook. So let’s create the actual code that interfaces with the login endpoint. So, I’m just going to copy that from the code I’ve got off screen and paste it in here. And and this is the handle submit function that interfaces using Axio’s client with the login endpoint. We’re passing in an object, a JSON object with the user’s unique email address and password so that the user can be authenticated with the system. And then it sets this or object here. Um, okay. And I’m going to create a temporary orth object here. Um, up here. This is going to be replaced in a in an another section of this course, in a later section of this course. But for now, just for testing purposes, let’s set the orth object with the with some user information that gets returned from the server once the user’s logged in. And we’ll do that now. So we’ll set that or object accordingly. So set response.data. That’s what’s returned from this post request that we are calling here. We’re calling the po the HTTP post method here and interfacing with the login endpoint via HTTP and passing this object which contains the email address and password here and then we’re handling an exception if one occurs here. Um, and here we’re if all has gone well, we’re actually populating local storage. But before that, we’re setting a state variable here. And this will be covered in in a subsequent section of this course. But for now, I think this is fairly good. And I don’t think we need to use this navigate variable. Or we can basically just once the user logs in um actually what I want to do is just write that to the screen. I’ll console log that to the screen. Oh, we’re already doing it here. So we’ll be able to see what’s returned from the Axios client. So I think we’re pretty much ready to test the login functionality now. Okay. Excellent. So, I know I’m going through this really quickly, but a lot of this is pretty self-explanatory. You can see this is of type submit. So, in the form here, in the form elements here, we’ve got an onsubmit event, which is wired up to the handler function here, handle submit. And here’s handle submit. And we’ve been through what this code does here. It interfaces with the login endpoint and passes in the payload which includes the email address of the user and the password to authenticate the user with the system. Let’s actually test that. Let’s go. Okay, we need to go back to the server. We’re at the server here. Let’s type go run dot to run our code. So, it’s listening on the server. And let’s go to let’s Whoops. Let’s actually clear the screen. npm rundev like that. Okay, let’s copy this URL to the browser. Okay, great. And our movies are loaded up. We’ve registered Genkins. Let’s log in as GT Jenkins. Look at that. That’s looking good. Okay. So, go jenkins at hotmail.com and the password I selected was pass word one exclamation point. And let’s see if we can log in. Okay. So, at the moment it’s not doing much, but we can test whether we’ve logged in by looking at the console window and seeing what’s been returned. Okay, let’s go to our developer tools. And it’s returned an object. And it’s returned a token and a refresh token. That is working perfectly. Excellent. So, we’re able to log the user in, but we will handle, you know, we can we’ll handle the the once the user authenticates bit. So, once the users authenticate, we’ll handle that in a better way. Um, so it’ll go, for example, in the header. That’s the next thing to do is in the header, it’ll go welcome goth for example or hello goth or whatever. This login and registration button will no longer appear here. One log out button will appear. So the user wants to log out once he’s logged in at some point. So it’ll have a log out button instead of the login and register button there. And of course, if the user hasn’t been authenticated, the login and register button are displayed there. This functionality, this mechanism is working. It’s returned uh the appropriate information back to the client once GT Jenkins has authenticated and it’s returned a token and a refresh token which can be used for subsequent requests to the server to protected endpoints. So now that the user has been authenticated, the user will have access for example to the recommendation the movie recommendation service and will be able to stream movies also because the user has been authenticated. So when the server side code is when the protected endpoints are subsequently called this token will be part of that request and the user will be able to be authenticated on the server which means that this user now that the user has been authenticated the client will be able to call protected endpoint. So the recommendation service for example can be consumed by the user that’s just been authenticated. Okay. Excellent. Right. So we’ve created the login functionality. So now it’s time to adjust the front end appropriately once the user has been appropriately authenticated. The user’s logged on, it’s authenticated and we want, for example, the header to change. So we want the two buttons that would be displayed when the user hasn’t logged on, the register registration button and the login button. We want those to be displayed if a user who’s accessing the magic stream website has not yet authenticated. And when the user authenticates, i.e. logs in, we want a logout button to be displayed and not the login and registration button to be displayed. So we’re going to handle things like that now. So what’s the effect on the front end when the user logs into the to our magic stream website? So if you’ll recall, let’s go to the login page here. We’re creating this or We’re setting the orth object here once the user has authenticated. We just did this as a temporary fix. We want this orth object to be available to all of the components because we want the component as it were to know whether the user has authenticated or not at any given time. So this isn’t ideal. This is just localized to the login function. We want this orth to be available to all of the components. And one way we can do that is by creating a React context. Okay, context is a way to share data between components without having to pass props down manually at every level of the component tree. A problem often called prop drilling. So here’s an example of where we’re actually using prop drilling. So you can see here we’ve got the home component. We’re fetching all the movies and then we’re drilling down these movies to the child component. So firstly the movies component we’re sending the movies object down to the uh we’re passing it down as a prop to the movies component and within the movies element within the movies component we’re looping through all of the movies that have been retrieved from the server and then we are calling the movie element here and we’re passing down the movie prop. So we’re drilling down again to the movie element or movie component here and then we’re displaying the movie. So that’s kind of like an example of prop drilling. Whereas with context, you’d just be able to retrieve the relevant objects from a centralized location. So it’s kind of like it’s a data store essentially in memory that you can just retrieve those relevant objects at any given time within a component. you can retrieve it from a context and that’s a much more efficient way of doing it especially as your application grows you want to use context rather than prop drilling to pass the relevant objects between components. Okay, so we’re going to start by creating an orth provider and this is how we are going to create our context whereby we can pass the orth object between our components without needing to use prop drilling for this purpose. So let’s create a folder. Let’s start by creating a folder within the src. So it’s going to be at the same directory level at the same level in the uh folder tree as components. And so within src we’re going to create a folder called context. And let’s name this with a small C context text like that. Press the enter key. Then within the context folder, we’re going to create a file called orth provider. And this is how we are going to create our context functionality. Or provider.jsx Okay. And let’s import the relevant components from the React package. So we want the create context function imported from the React component and the use state hook from React like this. Whoops. Okay. And then let’s go const. Let’s create a variable called orth context. This is our context. And then we create our context by calling this create context function here. So create context. And then we open round braces and pass in an empty object which is signified by opened and closed curly braces like that. Okay. And then let’s export. So we’re creating the actual context component. Export const proider. This is the name of the component equals to. And then within round braces, we pass in a prop called children. And this represents all the components in our application that will be wrapped. And you’ll see how we do this a little bit later within the orth provider element. So think of this children prop as all of our components. So this will enable all of our components to retrieve the relevant orth object from the children. That’s basically what this it’s very abstract at the moment but this is how you need to think of it for now to understand it. And let’s open curly braces like that. const. And let’s include within square braces or comm, set orth. And this is the the orth is a state variable of course that’s going to be passed between our components. Use state. Let’s use use state the use state react hook like this. And we’ve got the set or function here of course which can change the orth state variable. That’s what we can use the set all function for. Then let’s include the return keyword like this. Open round braces. And then within tags here, let’s create the tags for the orth provider dot provider element like that. And let’s close it properly here. So we’re closing the orthro provider.provider. provider element there and then within within we need to include a value attribute here and this is how we are essentially passing down the relevant or making available the orth object and the set function to all of the children within our application. So to finish the job, we include children here. This children prop here within curly brackets within the parent orth provider.provider element. Let’s save that. And then we export default like this or context. So we can utilize the or context context. Great. Okay. And we save that. And then we want to create a hook so that we can easily access this context this O object and set or function from within our component. So to make it easy we’re going to create a hook a react hook a custom react hook and hook will be let’s create a folder called hook at the same level as context and components. So within src let’s create a folder called hook like this with the small h like that. And within hook, let’s create a file called use orth. And you’ll see how we can use this use hook in just a bit. jsx. Okay, let’s create the hook. It’s pretty simple. Also, let’s import the use context hook from React. And let’s import the or context from the provider that we’ve just created. So, this has the path to it. Context forward slashorth oops or provider like that. and then const let’s create the hook const use or equals to open and close round brackets and then arrow function and then we return the use context hook and we pass in the orth context like this. This is how we can make available the orth object and the set or function to to the components of our application. Let’s export that. Export default use or like that. Semicolon. This is all to do with making the orth and set the orth object and set orth function available to the components within our application. So we need to wrap all of this code with the orth provider. So these are going to become child elements within the orth provider. And that’s how our components can be placed into their context where they’ll be able to use the use or hook to retrieve the orth object and the set or function. Okay. So within the strict mode element here, let’s include or provider. But we need to import or provider first. So import open curly braces or provider and then it’s automatically detected the path from where I want to import or provider. Excellent. Okay. And let’s move this space. Neaten up our code a bit. Save that. Crl S. And then we can wrap these elements, the roots and everything within the O provider element. So the or provider element becomes the parent element here. And this is how we can pass down the relevant context that contains the orth object and set function to the child elements the child components of our application. Okay. The next thing let’s go to the login component here within components. Let’s go to within login here. Open login there. And we need to retrieve the orth and set the orth object and set method from our use hook. So first thing we want to do is import the use or hook here. So to do that let’s type import use or like this from let’s include the appropriate path which is this hooks for slash use like that. Brilliant. And then we can use the use off hook down here. We need to comment this out. We no longer want this here. Or in fact, we could instead of doing it like this, we could simply you just use the set or because we actually only want the set or function. We don’t need the orth object here because we want to just set the orth object. So, we’re going to go set off within curly braces like this and retrieve that from our use or hook. Use like this. And then we don’t need to pass in these this empty string. So, remove that. And we’ve got the set or function available to us within our login component. And we’re using it here set to set the orth object. But now you see we’re setting it globally, not just locally. So by using the hook here, we’re hooking in to the context. We’ve abstracted a lot of the complexities of how the context works. And we’re hooking into the functionality using the use or hook here. And then we’re using the set or function here. Okay, brilliant. So we got that sorted out. Okay. And for now, I’m just going to use the navigate to navigate to the homepage. just for now. We’ll change it to something more appropriate in a bit. But so we just navigate once the user is authenticated, we’ll navigate the user to the homepage where the movies will be displayed just for now. Um but later on what we actually want to do is if they go to if the user isn’t authenticated and for example wants to utilize the recommendation service so clicks the recommendation um menu option on the header. If the user is not authenticated immediately what we want is for our front end to display the login form so that the user can authenticate and then once the user authenticates we want the user to be navigated to the recommendation page the recommended movies page rather than back say to the homepage. So that’s just for a better user experience but we’ll put in that functionality a little bit later. For now let’s just once the user logs in let’s navigate the user to the homepage. Okay, that looks good to me. And let’s go to the header now and sort out the header functionality. Okay, so the first thing we want to do here is import the use or hook. import use or Okay, and it’s detected the correct path to the use or hook here. So, we’ve got the correct importation code in place now. And then we want to include again we need to transform this here. We’re going to use the orth global object here by using the use hook use or hook. So we need to change this. So within curly braces let’s remove that or oops or from and then use or and then we don’t want to pass false in there anymore. This is not a boolean variable. This is an object containing the user’s information which is retrieved from the server once the user authenticates. So it’ll include for example the user’s token that will be passed in with subsequent calls to protected roots. So the user will have access to protected roots because the user has been authenticated. So we’ve got the orth object here retrieved from use or here. Excellent. And now this actually should just propagate through appropriately here. Hello name. But we want within curly braces here we want to include the variable. We want to include the value or dot first name. So this is one of the fields that will be retrieved from the server be first name on the orth object when the user logs in. And now we have access from the header component to the orth object which is now globally available through the context functionality that we’ve built. Okay, excellent. And then the rest of the code in fact should be good and we’ll include the actual logout functionality a little bit later. In terms of the layout we need to include a little bit more functionality. Okay. So within the components, we’re not going to include this within its own folder. We’re just going to include a file called layouts layout.jsx like this. So it’s got a capital L there to stick with convention naming conventions. Layout JSX. So let’s import the layout element. import sorry let’s import the outlet element from within react routed DOM so we can hook our layout into our centralized routting system which is made possible by the react routed DOM package here so const layout let’s create the layout component and you’ll see how we’ll use the layout component in just a bit but let’s just create the component for now go return and then open round braces and then main element. Type main whoops main tab. And then within the main element, this is just a the representation of the main semantic HTML element. And then within the main element, we include this outlet element like this. Okay. Excellent. Oh, I was wondering what all these red squiggies were about. It’s because I haven’t included this as an arrow and the red squiggies go away. That was just an equals. Whoops. That was just an equals causing all these red squiggly lines. And I’ve turned the equals into a arrow because this is of course a component denoted by this arrow function. And this component is called layout. And let’s export the layout component. So x Whoops. export default and then layout like whoops layout like that. Okay. And we need another layout related component and this one is going to be called uh require or and you’ll see the significance of this as I write the code. So required or dot j whoop jsx enter and let’s write the code for the required or component. Let’s take care of the importation code first. import then within curly braces use location navigate and outlet like this from and then within quotations react router DOM. Okay. And let’s import our use or hook from and let’s include the appropriate directory hook for slash use or like that. And let’s create the component const like this equals to open and close round brackets an arrow and then let’s open and close curly braces where we can include our logic for our required orth component or let’s retrieve or the or object from the global store if you like using the use or hook like this and then con location for navigation purposes from the use location hook which has been retrieved from the react router DOM package here. So we can hook into that functionality for navigation purposes and then let’s return. So if or if all is truly then open round braces and we just want to include the outlet element here that we’ve retrieved from react rout that there okay let’s close we’ve closed the let’s close the round braces here for this true part here and then else and let’s open round braces here. And then we want to navigate. So we’re using the navigate element here. We want to navigate the user to the login element. So, so if the user is not authenticated and tries to access a protected route, we want to navigate that user to the login screen so that the user can authenticate. Let’s include the state attribute here and the from location. So from location and then replace here. So it will replace whatever is in the whatever page is displaying within the layout. It will replace that with the login. Okay, remove that there. Okay, we’ve got all these squiggly lines here. Oops. Navigate. Let’s just figure this out. So all Okay, we need to close off this navigate element here. Okay, and that all looks good now. So basically what this is saying is if the user is not authenticated the user for example tries to access a protected endpoint. If the user is not authenticated we navigate that user to the login screen so that the user can enter the user’s credentials and authenticate with the system. Okay. Okay. And let’s export default and then the required required orth component like that. Okay. Now we want to sort out our roots. That’s the next step. And hopefully this will start to make sense to you what we’re doing here. So let’s go to the app.jsx file here. We’re importing all of these components at the moment here, but we need to sort out this code here. Path equals to forward slash homepage and element now equals to component that we just created called layout. So we pass in that layout element. Okay, so those are our nonprotected roots. Now we want to include our protected roots within the required orth element that we just created. So this is how we’re protecting those roots. So you can see now the significance of required all. So any of the roots included within the required or element if the user is not authenticated will be navigated to the login page where the user must authenticate and that’s it significance here. So let’s go back to the app.jsx JSX file here and let’s create a parent route for the protected roots element equals to and then within tags here required or like that and let’s close off that route here and let’s include the route here for recommended within the protected routes. Okay, let’s include path equals to recommended like this. And then element equals to and then this is a component that we’re going to create. We haven’t yet created it. Rec re receded like that. And this is just to give you an idea of how we are going to protect these roots. So if the user is not authenticated, the user automatically gets presented with the login screen and then the user must authenticate in order to access the protected route here. That’s how that works. I’m going to remove that for now because we haven’t yet created that component and I want to just test what happens with the header when we now authenticate. So, I’m just going to remove that there. Okay. Excellent. Um, and let’s actually run our code. Let’s see what happens when we run our code. So, let’s run our code. Let’s navigate to the server like that. CD magic stream movies server like this. Okay. And let’s go run. Sorry, let’s go go go run run dot to run our server side code. Let’s run the client side code. npm rundev. Okay, I’m just going to copy that URL to the browser. Okay, great. So, we’ve written quite a lot of code and some of it’s quite complex. So, this is what I would have expected. We’ve got a few issues to take care of. So, let’s look at the first one. Failed to resolve import. It’s not finding that path. Okay. So, let’s I’m just going to stop this. I’m going to exit the browser there and stop running the client. As you can see, we’ve also got our error defined here. So, it’s telling us where it is. It’s within the login component here. Okay. And I can already see what the what the issue actually is here. Um, anyway, I’m just going to cancel out of that. Sorl C clear the screen. Okay. And we need to just fix this. And it’s because I’ve actually named this. This should have been called hooks because you want to, you know, potentially include multiple hooks within the hooks folder, but I called it hook. So, I’m not going to rename it here because we’ve had issues in the past when we’ve tried to rename folders and files. I’m actually going to keep this as is, even though it’s not ideal. It should perhaps be hooks rather than hook. So, I’m going to just rename this here, this path to get around this issue. Right, let’s run the client again. dev. Let’s copy that. Let’s copy that URL to the browser window. Press enter. We’ve still got problems. Let’s go to our dev tools to see if anything’s been logged that can be helpful to us. Okay. Render orth provider. There’s a problem with orth provider. Let’s go to the relevant. I’m just going to close that down. Um, and let’s go. I’m going to cancel out of this actually. And I’m going to go to orth provider. And what could be wrong here? Ah, so this is incorrect. Orthrovider.provider. This should be or context. So I’m using the wrong element here. So it’s orth context here. Okay, let’s save the change. Let’s run our code again. mpm rundev. Our serverside code is already running, so we don’t have to run that. Let’s copy that URL to the browser window. Okay, let’s go to our dev tools. Layout is not defined. Okay, I think I know what that is. It’s just because we’re not importing the layout element from within the app.jsx file within the app component. So let’s close that off and let’s stop our code from running again. C clear the screen and let’s do that. So let’s go to app.jsx and let’s import the layout element with this importation code. import layout like this from then within single quotes let’s include the relevant path so component forward slash layout like that okay let’s see if we’ve got any more issues mpm rundev Okay let’s copy this URL URL our browser so that we can run the client and the server side code is already running so we don’t have to run the serverside code and we still have issues. Let’s go to our developer tools and see what the next issue is. Oh required or so we’re not importing that within the app.jsx file. So let’s go out of that and let’s control C this clear the screen and let’s go to app.jsx JSX and then we just need to import required orth the required orth element here which uh navigates the user to the login page if the user is not authenticated and tries to access a protected route. Okay. So let’s go to app.jsx here. We’ve duplicated the layout importation code here. And let’s change the relevant parts. So that required orth is also being imported correctly here. OR. Okay. And that looks good. Let’s try run our code again. dev mpm rundev. And let’s copy the URL to our clipboards. Let’s go to the browser. and run that. Ah, okay. So, that’s all working now. And I want to quickly test the effect of logging in on our front end. I’m hoping that these login buttons will disappear once the user’s authenticated and the logout button will be replaced in the header here or I mean will be displayed within the header here. The logout button rather than login and register. So, let’s log in and I’m going to log in as Genkins. atotmail.com. This is the user that we recently registered. Genkins password one exclamation point. Let’s log in. Excellent. That looks good. That’s exactly what I wanted to see. Okay. So, it’s clear that we’re now logged in and we can log out using this button, but we haven’t written the functionality for that. We will write that functionality in just a bit. Okay, brilliant. The next thing I want to do is create the functionality for the recommended component because this is actually a protected route. And then we can see how our application behaves when accessing a protected route when the user is not authenticated and then when the user is authenticated. Okay, so let’s go out of this here and let’s write the code for that. So let’s Ctrl C this clear the screen and within the components folder let’s create a recommended folder like this and within the recommended folder let’s create a file called recommended.jsx JSX to house our recommended component. Okay, great. So, let’s create the relevant code. And actually before we write the logic for this recommended component, we need to create another Axios file, another Axios configuration, but this time where we’re configuring the header with the correct authorization part to it, which will contain the the the token, the user’s token. Once the user has been authenticated, the user is given a token that the user can then the token can be included in subsequent HTTP requests so that the user can be authenticated against protected endpoints. So the user can access those protected endpoints if the user has the correct privileges. So we’re going to create to accomplish this, we’re going to create a new file, a new Axio configuration. So let’s do that. We’re going to create an actual hook so that it’s easy to retrieve this Axios, this private Axios configuration where we’re passing in the token. So within the hook folder, let’s create a new file and let’s call this file use cate. Whoops, sx. Like this. And let’s write the code for this Axios object so that we can configure this Axio object so that it includes the relevant token the authorization token within the header of subsequent HTTP requests once the user has been authenticated with the system. So firstly let’s import Axios from Axios. We’ve installed Axios. So we can write the code like this. So let’s write a hook called use Axios private. Use aios private equals to an arrow function. And then let’s include the relevant code. Okay. So let’s create const axios or equals to axios dotcreate like this. And we’re going to create our config code here. Okay. And then of course we want the base URL. So we’re going to read in the base URL from our environment variable. So in the environment variable we’ve got the base URL configured there. Um so let’s first read that in. So we go to do that we go const API url equals to import meta. NV. This is how you read in environment variable values um when your react project has been created using vit api_base url like that. Whoops. URL like that. Okay. And that should read in the appropriate environment variable value for the base URL from here. Let’s go back to our hook that we’re creating our hook code. So now we can configure that base URL by typing base URL colon then API URL like that. It’s the base of the API URL. And now what we’re going to do is create something called an interceptor. Whenever this Axios object attempts to call an endpoint, it’s going to add certain information to the header. And in this particular case, the information we want is the token, the O token, the access token, so that the user can authenticate against protected endpoints. Okay. So to do that firstly let’s retrieve the orth object and set function from our use or hook and we aren’t importing it just yet but we’ll do that in a bit. Use like this. Okay, let’s import that use orth hook with this code here. Import use orth. Okay. And it’s found use in this path. That’s excellent. Automatically it’s done that. Okay. And then what we need to do is create an interceptor as I said. So with this interceptor with this interceptor it’s going to intercept a call to a HTTP endpoint and add and we want to add relevant header information so that the user can authenticate against that protected endpoint before accessing that endpoint. So let’s go axios or dot in to seps response. This is how we add an interceptor to an axios object dot use and then open round brackets. Then we want to pass in a config variable like this config round braces and then arrow function. Okay, we want to configure the header. We don’t need that round bracket there. We want to configure the header. So, config.headers headers do authorization like this equals to within quotations bearer space curly braces. We can include the variable. So we need to precede these curly braces with a dollar. So we can include a variable within the single quotes. Okay. So now we can use the or object and dot token like that. H and sorry these need to be back to characters because we’re including the variable within the curly braces. So let’s remove those single quotes and include back to characters instead of those single quotes here like that. Okay, great. Okay, so we only want this configured if of course the author is true these. So we need to include an if statement there. So if or like that and then open curly braces like that. We want this code to reside within there. If the orth is true, the if the orth object is truly the like that and then we return config because we’ve configured it appropriately for axios like that. Excellent. That looks good now. And then outside of this here for the axios for the actual hook we want to return axios or so we include the return keyword at cos like that semicolon. And that looks good. Okay. And then let’s export this as default. Export this hook as default. So default use axios private like that. So it means the user must be authenticated to use this access component and then the or token can be included within sub within HTTP requests once the user has been authenticated. So instead of using this, we want to use the this Axios object for protected roots which will include the token within the header of the calls to the relevant protected endpoints. Okay, great. That’s all that means there. And then let’s create our recommended component, which is of course a protected route. So we’re going to want to use this hook to get the relevant Axio object to retrieve the relevant Axio objects. Let’s go back to recommended and let’s write the code. So firstly let’s import use axios private from dot dot slash dot dot slashhook. So it’s just hook not hooks forward slash use private. Oops for slash use private like that. So we’ve got the correct importation code there. Let’s import use effect and use state from react like that. Let’s import the movies component from this location from movies slash oops forward slash movies like that. Great. And let’s create our component const using our arrow function recommended. It’s the name of our component then arrow function and within our arrow function we include the logic for our for our component. So first thing let’s create a movies state variable and set movies function to change the state of that variable equals to use state that pass in an empty array. So an empty array for the movie state variable we’re defaulting to that firstly and then let’s create a loading variable loading and set loading function equals to use state and we want to initialize the loading state variable to false firstly and then const message then set message equals to use state like this. Okay. And then we want to retrieve our AIOS object from the hook that we just created. So Axios private equals to use Axios private. So it’s use Axios private hook like that. And let’s create a use effect. Let’s use the use effect hook like this. So when the component first loads, we want to call the recommended movies endpoint so that we can retrieve the recommended movies for the relevant user and it’s from a protected endpoint. Okay, let’s create the function that fetches the movies. We’re going to call this fetch reccommened movies. Okay, fetch recommended movies equals to a sync open and close brackets arrow function like that. Oops. Error function like that. Oops. Set loading to true like that. Set message to empty an empty string. And then let’s include a try catch block here. Okay. And then const response. All right. Let’s just include the catch section here. Catch error. And then let’s handle the error by just console dot error method. And we’ll just write that to the console screen. So error fetching recall mended movies like that. And then more detail about the error and the error object. We can include that in what’s written to the console screen when an error occurs. Okay. And then finally, so this code gets called whether an error occurs or not. And we’ll set loading to false. So we don’t want any loading indicators displayed once this process has resolved whether an error has occurred or not. We want to set the loading. We want the loading indicator to disappear at that point. Okay, great. So then response equal oops equals to await axios private.get and then for slashremened movies. That’s the name of our end point there. And then we need to set movies. set the movies state variable to response dot data whatever is returned. Bear in mind we’re using the Axios private Axios object here and we’ve created an interceptor. So when we call a protected endpoint the token the orth token is being sent along with that message that HTTP request to that protected endpoint. So we’re so the user is being authenticated against that protected endpoint the recommended movies endpoint. I just want to double check that that is the name that we called our endpoint. So we can go to roots protected roots and recommended movies is the appropriate end point. And we’ve tested all of these endpoints through Postman. So we should be able to test that fairly efficiently. test our client fairly efficiently at this point because we know the server side code works. Okay, there’s that our use effect hook written. We need to call the use effect, sorry, we need to call the fetch recommended movies function within the use effect hook. So we do that like this. Okay. And we need to pass an empty array. And this is to do with how the life cycle hooks work in React. So we want as this when this recommended component loads, we want it to fetch the users’s recommended movies. Okay, great. Okay, and the return jsx is pretty simple. That’s the most complex. We’ve just written the most complex part of this particular component. And so we’re checking the loading state variable loading. So if loading is truthy then while our component is loading let’s just include a very basic loading indicator. We’ll include a proper spinner loading indicator a little bit later, but for now, let’s just include text like this. Loading dot dot dot. Okay. Else then open round braces. We include the movies element. We pass in the movies state variable. We’re prop drilling here, I guess you could say. We’re passing the movies. And if there’s an error message, we’re also passing the message down to the movies component here. And we’ve got loads of red lines here. We want to include this. We did we don’t have a parent element. So, let’s make this as a fragment. which allows us to the ability to not have to include an apparent HTML element like a div for example. So we can just uh get round that by including a fragment here which are just which are denoted by empty tags. And that looks pretty good. And we mustn’t forget to export our component. It’s easily done. Export default recommended. So now we’re exporting the recommended component as default which means we can import it here within our app.jsx file. So let’s do that. So I’m just going to duplicate that there. Let’s import recommended from components recommended and then with the R capitalized recommended like that. Okay. And now we can include within the protected roots which is wrapped by this required or um element that we created as the parent element for our protected roots. And we can create a dedicated route for that recommended element. So recommended is the client path to the recommended component. So the client route is defined by for slashrecall mended and then that’s a path to the recommended component there and it’s wrapped within the required or element because it’s a protected route. So in other words, if the user is not authenticated and they try to access this route, the login screen must be presented to the user indicating that the user needs to authenticate with the system before accessing the recommended the relevant recommended movies. Okay, and that looks pretty good. I just want to double check that within the header it is indeed that link is indeed linking to this path here. So, if we go to header here, let’s have a look at what’s going on. Recommended nav link. Yep, recommended. And that’s what I want to see. So, we can now test that. I think it should work fairly well. Although, actually, I’m going to go to the login screen, the login component here, and include this code for the navigation. So it doesn’t navigate the user. When the user authenticates, say the user’s on the homepage, the movies are displayed. The user wants to check the user’s recommended movies. The user’s not yet authenticated. The user presses the recommended header menu item. The user is then navigated to the login screen. When the user logs in, we don’t want to navigate the user to the homepage again to see the movies. We want the user to go directly to the recommended screen. So let’s comment that out. And this code here will do that. It should direct the user red. Yeah, it should log the user on and then direct the user to the screen that the user was trying to access before the user was authenticated. So that just makes for a much better user experience by doing it that way. And I think there’s a piece of there’s more code that we need to write to make that work. Not sure I’ve got that in there. Yeah. Okay. So, we need to also this from variable here. We need to retrieve that appropriately from use by using this location variable. And I’ll show you how to do that now. So, and that’s retrieving the relevant uh path that the user was trying to access before the user authenticated. So, let’s do that. const from equals to location dot state dot from dotpath name like that. And if it’s null, whoops. And if that is null, so there’s no we can use we can navigate the user to the homepage. So if this is actually null, this path name which is taken from the browser’s history, if this is actually null, it will by default navigate the user once the user is authenticated to the homepage. Okay, excellent. So I think we’re in good shape there and I think we’re ready to test it. We’ve still got our server running. Okay, great. So we don’t need to rerun the server. Let’s run the client. mpm rundev. Okay, great. It’s running. Hopefully, there won’t be too many issues with this. Could be some issues. Okay, let’s go to the client. Okay, that’s looking good so far. So, now we’re not authenticated and let’s access the recommended menu option here. Okay, so the first part of it the functionality is working. We’re not authenticated. So our code has navigated us to the login screen. And let’s let’s now authenticate as go jenkins@hotmail. [Music] This is hotmail address. His email address.com. So go jenkins@hotmail.com. Password one. So we are including Genkins’s credentials within the signin form. and let’s try and log in and let’s see what happens. Okay, so we got a problem and that’s okay. And let’s just investigate this by going to the developer tools. So more tools, developer tools. Okay, so it’s responding with a 401 exception. And I suspect that’s something to do with the interceptor not adding the token into the header before making the call to the protected endpoint, the recommended movies endpoint. So it’s a problem with Axios. So we kind of got an idea of where to look. So let’s close that down. Let’s go to our code here. I’m going to controll C that cancel and clear the screen. And let’s take a look at use Axios private here. And I can already see what the problem is here. I included response here. And this should in fact be request. We’re making a request. So every time we make a request to an endpoint, a protected endpoint, we want this our orth token to be added appropriately to the header. So this should fix that problem. And let’s see what happens now if we run our code. So our server side code is still running. So npm rundev. Let’s copy this URL to the browser. Let’s launch the our browser window. run our code. Brilliant. So, our movies are being displayed to us, but this is a nonprotected endpoint for the homepage. It’s just the movies endpoint that is being called to retrieve these movies and then displaying it on the homepage, which is correct. And then we want to see our recommended movies, but of course, we haven’t authenticated with the system. So, the system doesn’t know who to recommend those movies to. And we haven’t of course authenticated whether we have the privileges to use the recommended movies facility. So we need to authenticate. So go genkins oops at hotmail.com password one exclamation point and let’s log in and let’s see what happens. Excellent. So that means our token has been passed to the server after we’ve authenticated and the system has recommended the movies based they’re all excellent movies apparently the sentiment attached to all of these movies so it’s recommended the best movies in the genres that we chose which were western comedy thriller drama I believe th those sort of genres were selected when and we registered as Goth Jenkins. So, it’s recommended the appropriate movies. But this is big because we’ve now from the client, we’re now able to access not just protected endpoints, not just sorry, not just unprotected endpoints like the movies endpoint, which displays all the movies on the client for us in the on the homepage, but we’ve also now passing in a token with requests to protected endpoints like the recommended movies protected endpoint. So when we press recommended the recommended when we select the recommended header menu option here it automatically authenticates us on the server because the token is being passed in with the request to the relevant protected endpoint. After we’ve authenticated with the system, the token is passed down to the client. The client then stores that in memory and then when we subsequently call protected endpoints, the token is used to authenticate us. So we can now just because we’re authenticated, we can now go swap between protected endpoints. We can call protected endpoints and unprotected endpoints. So we can now call unprotected endpoints and protected endpoints no problem once we have authenticated because that token can now be passed in with subsequent HTTP requests to protected endpoints and we can authenticate through the use of that token that access token. Excellent. Okay. So in this part of the course, we are now going to create the UI part associated with the AI functionality. So this is when the administrator logs into the system and will have a facility available to that administrator and the administrator can then create a movie review for a particular movie and then when the user submits when the admin user submits that movie review. That movie review is sent along with a base prompt to open AI. It is anal an analyzed and through the prompt the instructions are to extract one word describing the sentiment behind the movie review. So this is a way for us to extract quantitative information from qualitative information. The qualitative information is the movie review written in natural language and that is becomes part of a prompt to the AI. the AI analyzes that and returns a just one word and then we’re associating a a value with that word. So, uh the options the word options to describe the sentiment that we have included in the prompt are excellent, good, okay, bad and terrible. And we you can see that it’s very easy for us to associate a value with each of those words. So, excellent um has an associated value of one and terrible has an associated value of five. Okay. Is value associated value of three. Bad has an associated value of four and two is the value that represents good. So, it’s a way for us to rank the movies in a sense. So, through this ranking, you can order the movies by those values. And that’s exactly what we’re doing for the recommendation system. So, we’re recommending five of the top movies within a users’s genre, using those rankings to order the movies, those five movies. And if, for example, there’s more than five movies available for the users’s chosen genre, it will only recommend the top movies based on those sentiments, those rankings extracted from the admin’s movie review. So, let’s get started. And in fact, I’m going to keep this very simple. We’re going to get this done really fast because I’m not going to write out all of the code because most of it on the front end is just Bootstrap, uh, classes and CSS basically just to style a UI facility. Um, uh, the code is available on GitHub. So, please go to the GitHub repository if you want to copy and paste the code and then you can do your own analysis on the various Bootstrap options. By all means, please look them up on the various Bootstrap websites so that you can uh get more detail about those classes and what they’re doing. But it would just take too long for me to explain every single Bootstrap class and uh I think it’s unnecessary for this particular course. So, as I say, I’m just going to copy the and paste the code for this particular UI facility and then I’ll give you a brief explanation of what everything is doing. So, let’s um create within the components folder. Let’s create a new folder and let’s call this review with the R in lowerase. So, review like this. And then within the review folder, let’s create a file called review.jsx. And I’ve already written this code. It’s on GitHub. I’m actually going to copy and paste the code into this file. And here is the code. Just pasted it in here. And I’ll explain the code to you now. Okay. So this is what the front end is doing. We’re using the Bootstrap the relevant Bootstrap classes to basically split the screen in half. On the left hand side, we’ve got the actual movie which will it’ll display the poster and we’re reusing the movie element for this purpose. And this is just the various Bootstrap classes that are used to we’re using the Bootstrap grid system to place the movie on one half of the panel, the movie poster, and relevant details including the sentiment. And you’ll see how that works when we run our code. uh we can change the sentiment on the fly in real time by updating a text area box which appears on the right hand side. So that takes up the other half of the screen. The right hand side of the screen is taken up by this text area box. So it’s just a big text box, multi-line text box that where the administrator can add the administrator’s review like this. So it’s a text area type for this form control. We’re using React Bootstrap for the form control element here. And then we’ve got the submit review button here, which is a submit button. Type equals to submit. And we can submit the form value, the form control value for the text area, which contains the administrators movie review to the server to our review endpoint or what have we called the endpoint? The end point is update review. and we’re sending that along as with a patch request to the server and the relevant IMDb number of the movie so that the server knows which movie to update with the relevant admin review. So of course we’re using Axio’s private because it’s a protected endpoint. Remember in the last part of the course we’re behind the scenes we’re adding in the users token. In this case, it would be the administrator’s token that the that is received from the server side when the relevant user logs in. So the token is returned to the client and then we are including within this functionality here use Axios private this hook. We’re including an interceptor and we’re adding that token to the header so that requests from that particular user can be authenticated on the server through the use of this token which contains the user’s claims which is just information about the user and it includes the user’s ID for example the user’s role and that sort of thing. So the user can be authenticated on the server and therefore access protected endpoints. And we’ve taken care of that behind the scenes here within the configuration of this particular Axios object that we’re using here within the review.jsx JSX file to send a patch request to the server to the update review endpoint where the administrator is updating the administrator’s movie review and then what’s returned is the ranking name here which is the sentiment behind the movie that AI is extracting. So please by all means refresh your memory and go back to the serverside AI functionality that we created which sends this movie review along with a prompt to open AAI um and the open AI functionality extracts a sentiment from the movie review basically. So it analyzes the movie review written in natural language and extracts one particular word and we can attach a numeric value to that word. So the words are excellent, good, okay, bad and terrible for but they can be extended. You can create an entire spectrum of rankings. So this uh software is designed to be extensible in that regard. So you could add as many uh ranking values as you like or sentiments sentiment options as you like um for this functionality. So that’s basically it really. And so if we just to describe what’s happening here, we are analyzing the orth state variable here. This is the or uh variable that gets populated when the user authenticates. But we also have to check the orth ro that it’s admin in order for that particular the particular admin the particular administrator to have access to this text area control so the user can update the administrator’s review. Um or else what happens we’ve got an else part here. We’re just putting that review in readon mode. So any user can it doesn’t have to be an administrator to actually see the review but the user has to be an administrator in order to update the review. So here we’re just so here in this particular scenario the code is deemed that the user is not an administrator and is displaying the relevant review within a an alert control and this is just a bootstrap class which turns this div into an alert. So it’s basically read only. You can see the review in in readonly mode. But if you’re an administrator, you can actually update that review and send it through to the server to be processed. Great. Okay. And we haven’t yet created the spinner code here. Um I think it might be a good time to do that actually. And I’m going to do the same. This code’s not that important. It’s just for um a better you user experience. I’m actually just going to create a new folder called spinner here. And I’m going to create spinner.js. Okay. And I’m going to copy in the relevant spinner code. And it’s just basically a span and a div and utilizing various CSS properties to create the spinner effect. Okay. And we are within the review we are importing spinner up here. And then while the relevant UI screen this component is loading that spinner will display. So while this loading state variable is truthy, the spinner displays and once the data is ready, the loading indicator is is disappeared and the actual UI is displayed to the user where the user if the user is ad if the user is an administrator can update the relevant movie review. Okay. Okay. And now we need to update the app.jsx file here. So let’s go to app.tjsx. And it’s in a protected endpoint. So we need to firstly import the endpoint. So I’m just going to copy this here. Duplicate this code and then change the bits the relevant bits. So So this one’s called review. Okay. review review. So we’re importing the review here. Okay, review is declared but its value is never read. So we’re going to do that now. We’re going to utilize this review component within we’re going to add a root for that particular component. So I’m going to duplicate this here. path equals 2 and then it’s going to be review like this and we need to actually include a parameter within this path and it’s the IMDb parameter imdb ID parameter because when this route is accessed we need to pass an a unique movie ID so that the movie can be extracted. The relevant movie data is extracted uh from the MongoDB database and the relevant data is then displayed for the movie on the review screen. Okay, that’s how that works. And of course, we need to replace this recommended with re view. Okay, we’ve mapped that route now. That looks good. Okay, excellent. And then we also need to include a review button for our movie cards. But we don’t want that review button. We’re actually displaying a movie card. We’re reusing our movie element on the administrators panel where the administrator updates the relevant movie review, but we don’t want the review button displayed in that regard. And this is just uh we’re going to use prop drilling to to accomplish that. So, let’s do that. Um, update review. Okay, this is worrying me. There’s Okay, already included file name Golang magic stream view. Okay, I’ve done it again. I’ve done something stupid. Sorry. Okay, I don’t know why I do that. That’s got a small letter in it. I’m going to just cut this here. Okay. And I’m going to delete this file here. [Music] And I’m going to recreate that file. Sorry about this. It’s got to have a capital letter here. View.jsx. Let’s stick to protocol and make sure that that letter is capitalized. Can’t believe I’ve made that mistake again. Okay. Sorry about that. And let’s paste that code back in there. And that should sort out our problem on the app.jsx JSX page. Great. That has Sorry about that. We were importing, sorry, here it is. We’re importing the review here. And there’s no red squiggly lines there now. Okay, great. So, and we’ve got our root setup. And the next thing is to we’ve pasted in the update movie review method here which simply navigates to the review page and passes in the relevant IMDb ID value to this route here and then that parameter can be utilized within the review page. So if we go back here you’ll see how that parameter Here we go. So we’re using the use params hook to get the IMDb ID value here and we’re importing it from react routin this Axios private get query we’re getting the relevant data for a particular movie which will could potentially include the review for that movie and the ranking the sentiment and all the movie information for that movie the title the movie poster etc. And we’re grabbing that from this movie endpoint which must include as a parameter the IMDb ID value of the relevant movie. So this is how we’re passing that to the server the relevant IMDb movie. Then it queries our MongoDB movies collection and returns the movie data. And then we’re setting the movie data here. the state variable called movie. We’re setting with the set movie function here. Great. And then we’re reusing our movie element here. We’re reusing that element here and displaying the relevant movies information in this particular UI. Okay, let’s go back to the prop drilling code. Um, I got a little bit distracted by that error um issue we had on app.jsx. So what we want to do is drill down this value here. This method we want to drill that into the uh home element first. Okay. And this is because we only want the movie button, the review button displayed in certain context and I’ll I’ll describe those contexts in more detail in just a bit. But let’s just drill this down using prop drilling. Let’s drill it down to the home component and we’re sending our method down there. So it can be used well it can be used within the movie component ultimately but uh through a button that the button when it gets clicked will call this navigation functionality through this method call. So we’re passing that down to the home component. So, we need to include this prop within the home component. Let’s go to the home component here. Okay. Uh, we include it here. We include the prop here. Okay. We don’t need those curly braces. Okay. There. And then we actually want to drill further down to the movies component here. So, we’re going to do the same thing here. We’re going to include the prop, the relevant prop here. Paste that in there. So, we’re passing down that method reference, the reference to the update movie review method that resides actually within the code for it for which resides within the app.jsx file or the app component here, but we’re drilling it down. We’re passing a reference to that method down firstly to the home component, then to the movies component. So, this is some serious prop drilling. So we need to include it here. Okay. And then we can drill down further into the movie component. So we do that like this equals to like that. And then we must include this prop within the movie component here. Okay. And we can do that like this. And now we’re passing in that update movie review method. And now what we do within the movie component is create an if statement where if update movie review is truthy include the button. And in this respect we can control when the button is actually displayed for the relevant movies. We can control that by checking whether it’s truthy or not. So if it’s passed down as a prop to the movie component, then we include the button. If this isn’t included as a prop, the button won’t display. So we can control when that review button is displayed. And then we’re calling the update movie review method here and passing in the relevant unique identifier. When that button is clicked, this code is executed here on the app.jsx file here and we’re using this navigate utility here to navigate to the review element. And we’re passing in the relevant IMDb ID like that. So the relevant movie review is displayed, the relevant movie information and the movie review is displayed on the review page. And I think we’re in good shape. I think that’s all the code we need for now. Let’s let’s run the code and see where we are with this. Okay. So, I’m going to run the server side code. And we do that by typing go run dot like this. Press the enter key. Excellent. Okay. Okay, so the server side code is running. Let’s run the client. npm rundev. Press the enter key to run the client code. Hopefully we don’t have any issues here, but I guess there’s always a chance that we do. Anyway, let’s copy that to our browser window so that we can test out our code. Okay, we have a problem. Okay. Failed to resolve import hooks. Ah, that should be hook. Okay, no problem. It’s because we copied and pasted the code in from the prototype type code that I wrote in preparation for this course. That’s what’s causing that. So, uh, where do I go here? I go to review. So, let’s go to review and let’s change this to hook. There’s another reference to it down here. Okay. Save that. MPM rundev. There any more references I don’t know about. Let’s run that. Okay, let’s copy that to the browser window and let’s see what we got. Okay, excellent. You can see now our review our review button is being displayed under each of these movies. So, that has worked perfectly. And now we should be able to click the review button to review a movie. Okay, actual review code is not working for some reason. Just check what’s going on here. Navigate is not defined. I’m just going to clear that. Um, let’s go to app.jsx. Here we’re not uh retrieving a variable. We’re not retrieving a value from the use navigate hook yet. So to do that, we can type const navigate equals to use navigate like that. I don’t know what I was thinking. I’ i’ve included this outside of the function to include that within the app function, of course. Okay. MPM rundev. Okay, let’s copy that to our browser window. Let’s try again. Review. Great. So, it’s prompting us to authenticate. Let’s just log in as goth jenkins at hotmail.com password one exclamation point. Let’s log in. And there we go. I love the acting in this movie. It was absolutely sublime. And why isn’t the text box showing in this particular case and the submit button? Well, that’s because this user is not an administrator. It’s not part of the admin role, but part of the user role. So, all the relevant movie information is being displayed and the sentiment. Excellent. But we can’t change that sentiment through updating this movie review because we’re not an administrator. So, let’s log out. Oh, we don’t have the log out functionality yet written, do we? Okay. So, we can’t log out. Um, let’s go home. Let’s just cancel out of the whole thing. And we’ll write we’ll have to write the log out functionality soon. Let’s clear the screen and let’s run the client again. But this time we are going to log in as an administrator. And let’s check who’s an administrator. I believe it’s Bob Jones. He’s always an administrator. So let’s go into the compass utility here and check our data to see who we have designated as an administrator. I believe Bob Jones is an administrator. Let’s check and then we can log in as Bob Jones. He’s an administrator. Bob Jones. Okay, great. And you can really make any one of these an administrator because you have access to the back end. Just change the role. Just double click in there and you could change, for example, Sarah Smith to an administrator. I won’t do that now, but you can easily do that if you want to test other users as administrators. But let’s log in as Bob Jones because that’s an easy thing to remember. All Bob’s credentials are pretty easy. Um, we’re still running the code. So, let’s go to our browsers and copy that in there. And let’s see if we can log in as Bob Jones now. And let’s go to Once Upon a Time in Hollywood. And let’s review Once Upon a Time in Hollywood. Let’s click review. Let’s log in as Bob Jones. Bob Jones at hotmail.com. And then password one exclamation point. login and look at that. This movie is awful. And it’s not awful at all. It’s a Tarantino movie and it’s really quite a good movie. I really enjoyed it. But we’ve decided through this admin review that the movie is awful. So now let’s change that. Let’s uh say um another masterpiece by I don’t know how to spell terino. I think that’s right. Another masterpiece from Tarantino. Tarantino. Another masterpiece by Tarantino. The acting was also sub blime sub sub blime. Let’s see what it extracts from here as the sentiment. So, let’s submit the review. There we go. We got our spinner working. It’s come back. Excellent. So, we’ve changed the sentiment to excellent from terrible to sentiment through our admin review. And isn’t that great? You see, let’s let’s try another review. And this time, let’s do a rather middle of the road review. So, let’s say um it wasn’t great, but it wasn’t awful either. Let’s see what sentiment we get from that. Okay, middle of the road. So, that’s working pretty well. This movie wasn’t too bad. Actually, I’m hoping this will bring back the sentiment of good. Good. So, our AI is behaving itself, our AI functionality, and we’re able to add reviews because we’re an administrator. We logged on as Bob Jones and we can add our reviews to our movies now and it’s extracting the appropriate sentiment which has a bearing on our recommendation functionality here which brings back the recommended movies and the users genres the chosen users genres and it brings them back in order from one to five. So all of these are excellent. So it’s ordered Highlander 2 is apparently excellent. Okay, we’ve got to do something about that review cuz this was a horrible movie. Um, so it’s bringing back Highlander 2 as excellent, Pet Detective, excellent. And then Once Upon a Time in Hollywood, we’ve just changed that review as good. And then Hangover is good, and Harry Potter as okay. So, it’s extracting the five top movies in that particular user’s chosen favorite genres. And that is working perfectly. So it’s ordered them by ranking which is the sentiment and we’ve associated a value from one excellent to terrible five. And that means we can order by them quite easily here. So that’s our recommendation system is working really well there. But let’s do something about Highlander 2. I’m not accepting that. That was excellent. So let’s go back. Let’s go down to Highlander to excellent. No, it was an abomination. And I’m going to I’m going to reflect that in my review. So, this movie is an absolute [Music] tempted to use a some colorful language here, but I won’t. I’ll just say this movie is an absolute abomination with an exclamation point. So, that should be terrible. Now, let’s submit that through and see if our AI deems this movie is terrible. And it does. good. This is a terrible movie. And you can see now the effect it’ll have on the recommendation system. See, now Highlander is not even mentioned here in our recommended movies for Bob Jones because we’ve decided Bob Jones is an administrator and he’s decided it’s terrible. So, it’s been unceremoniously kicked off this page completely. Great. So, it’s no longer being recommended as a as a excellent movie as a movie. And you can see that these movies have been ordered by the ranking value. Excellent. Good. Good. Okay. Okay. Brilliant. Excellent. So, we can’t log out yet. That’s the next thing we need to do is build the login the logout functionality. But I’m really happy with what we’ve done so far. And I think that looks pretty good. And it’s pretty easy to use. And it’s responsive, too. You can see here if we make the screen smaller, it just pops that under there like that, which is pretty good. Okay. Excellent. And we can just use our menu like this. So if we go to recommended, it just plunks those. This is the beauty of using Bootstrap. Our screen is responsive. Our screens are responsive because we used Bootstrap. and it handles that for us automatically. So I’m really happy with that. Brilliant. So at this point we have in place all the functionality or most of the functionality for our application but at the moment we are passing our access token to access protected endpoints from the client through the header of the HTTP message. So if we look here for example, if we look at use Axios private, you can see here with this interceptor when we try to access protected endpoints, we first are passing we are first adding the O token to the header of the relevant HTTP message every time we access protected endpoints. So this is actually a vulnerability to sophisticated cyber attackers. The reason for this is at this point we are currently storing the token in memory and in many cases when including the access token within the header of the relevant HTTP messages to access protected endpoints, you may be tempted to store for example the access token in local storage on the client. This is a security vulnerability because sophisticated attackers can potentially read the access token from local storage or even from memory using malicious JavaScript code. So to overcome this security vulnerability, we are going to store the access tokens using HTTPON cookies. So why is it safer to handle the passing of the access tokens from client to server using HTTPON cookies? Well, this is because HTTPon cookies cannot be read through JavaScript code. And therefore, this is much safer. This is a much safer way to secure the access tokens. So, what are HTTPON cookies? A HTTPON cookie is a cookie that cannot be read via JavaScript in the browser. For example, through document.cookie. Document.cookie cannot be used to read the cookie. It’s set automatically by the browser with every request to the server from the domain that set it. The HTTP only flag makes it inaccessible to scripts and the secure flag ensures it’s only sent over HTTPS. Why are access tokens sensitive? Access tokens are essentially keys to the user’s account. If someone steals them, they can impersonate the user. How can they be stolen? What are the major threats? XSS, cross-sight scripting. This is where an attacker injects JavaScript and reads tokens from memory, local storage, or session storage. CSRF, cross-sight request forgery. This is where an attacker tricks the browser into making authenticated requests. So, how do HTTPON cookies help? Protection against XSS. If you store an access token in local storage or session storage, any XSS vulnerability can let an attacker read it directly. With HTTP only cookies, the token is not exposed to JavaScript. So even if malicious scripts run, they can’t read it. Automatic sending with requests. The browser automatically includes cookies with each request to the back end if same site rules allow. So your front end doesn’t have to manually attach the token to each API call. Let’s create the code that will now store the access token as HTTP cookies. So firstly we need to adjust uh what is occurring on the server side. So let’s go to user not user model user controller. Let’s go to the login code here. Okay, we want login user and there it is. And you can see currently we are passing the tokens down here and we don’t want to do that. So the first thing I’m going to do is remove this this code. So firstly I’m just going to copy uh comment these out. We still want to generate the tokens in the same way. We still want to update the database with the tokens. So we’re generating all the tokens here. We’re updating the tokens within the database and then we’re passing the tokens back to the client. This is the part of the code that we definitely want to change. And how do we do that? So we do that with this code here. Okay. So just above where we are sending the HTTP response back to the relevant client, let’s set the tokens here. So we are no longer passing that token back to the client like this. We’re passing all the other information to the client and we’ll see why this is useful in a bit when we address the client side code in this respect. So here we go. We’re setting the cookie access token here. We’re setting a timeout here. Okay. And we’ve set the secure flag to true here. HTTP only true. So it’s a HTTP only cookie that we that we are now sending back to the client and this happens automatically. So we don’t have to manually send it back like this with this code here through the response. It’s going to automatically handle that for us. And then we also want to do the same for the refresh token. So we’re setting the refresh token here to an appropriate HTTP cookie, HTTP only cookie and the access token. And we’ll address what the significance of the refresh token is in just a bit. We’ve done so we’ve now changed the server so that the access token will be passed from server to client via a HTTP only cookie. There’s a few things we need to do on the client to make this work. So let’s go to the client code. We need to adjust the code here for the the first Axios object. So it’s actually a very basic update. We need to include this with credentials flag and set it to true like this. And that will ensure that the HTTP cookies are passed between client and server. And we actually need to do it even though this particular Axios object is not used for protected endpoints. We still need to ensure that the client and server that the tokens are being passed between client and server every time we send uh a message to uh the server from the client we need to ensure that those tokens are sent via HTTP only cookies. Um so we all we do is we set this with credentials property to true and we need to actually do the same now in our hook here where we’ve got the interceptor here. We no longer need to intercept and pass in the bearer token like this. But we are going to use an interceptor a little bit later. For now, I’m actually just going to copy this. I’m going to comment this out. I’m just going to ensure that with credentials is set to true here also like that. Okay. So that is now correct. So we’ll leave this as it is for now. And you might be asking yourself, what’s the point of even using two separate Axios objects? But we’ll address this in just a bit. Why I still want to maintain this separate Axios object for accessing protected endpoints. And it’s to do with the protect. It’s to do with the refresh tokens. And we’ll look at that logic in just a bit. But for now, let’s just keep our logic as is here. Um, in fact, we don’t need this line of code here either. The with credentials property is set to true. And that will ensure that the HTTP only cookies the relevant tok that store the relevant tokens are passed between client and server. Okay, automatically. So we don’t have to add anything to the header and therefore potentially risk a security vulnerability to XSS attacks. So we’re no longer adding the bearer token here. Okay, let’s go to our login client now. Now that we’ve sorted that out, let’s go to our login client here. And I want to make sure that we’re storing it in local storage. So that’s what we want to do. We want to store the orth data. So this contains the orth data. Here you can see that we are setting the orth state variable which is now global. We made that global. We made that global through context um in the last part of the course. So we’re setting it here with response.data. So after the login occurs, we can see here on the server, if we go back to our login code, oops, we can see on the server that we’re passing this information back, but we’re no longer including the tokens. We’re just passing the user ID, first name, last name, email, RO, and favorite genres back to the client from the server once the user logs in. But this these cookies here, these HTTP only cookies are passed back automatically. So, we don’t have to manually do that through code because they’re by virtue of the fact that they’re HTTP only cookies and we’re setting the appropriate flags on the client, we’re saying, please do that on our behalf. Please just send those HTTP cookies automatically. And we’re setting with credentials equals to true here. And we’re doing the same for this Axios private hook that we created where we were using an interceptor to add the relevant token to the header when accessing protected endpoints. We’re no longer doing that. We’re just setting with credentials equals to true because we want those tokens to be passed between client and server automatically. And this is how we do it. But this is still relevant because we’re going to um adjust the code here later on so that it handles refreshing the tokens. But we’ll look at that in just a bit. We’ll look at that later on. So, okay. So, let’s go back to the login here. And we’re setting the orth object here. We’re setting the state variable. Well, let’s just first check that our code is still working. We haven’t messed anything up. Um, okay. So, I’m going to create a new terminal here. And let’s go cd server slash magic stream movies server like that. Okay. And let’s run our code. Go run dot. So, we’re running our server side code. Hopefully, we have no issues. Good. It’s listing on port 8080. Let’s go to the client terminal and navigate to the relevant directory. So client slash magic stream oops client like this. Okay. And let’s run our code. So it’s npm rundev to run our code. Okay. And let’s copy this now to our browser window. And hopefully I haven’t introduced any bugs. Error fetching movies. Oops. First bug. Okay, so interesting. Let’s see what’s going on there. Developer tools. Okay, so we’ve got to address cause. We’ve also got to change the cause policy um appropriately. Okay, so let’s do that. Let’s handle that code. Let’s go out of there. Let’s cancel out of here. Crl + C clear and we got to handle cause. Okay, so I’ve got to also cancel Ctrl C cancel the server from running. Okay, and let’s sort out cause we got to go to the main.go file to do that. So it’s this code, this configuration code here we need to also change because we’re we’re um going cross domains with our client and server which gives you a lot of flexibility. If it’s on the same domain, you basically have to always have your code on, you know, running on the same domain here. We can run our components on disparate domains, which is what we’re doing here. But the cost of that is we have to configure cores appropriately to make that work. So, let’s do that. And I’m actually just going to copy and paste the code for that. This code is available on GitHub. So, please by all means just do the same thing. Just copy and paste the code from GitHub if you don’t want to type it out manually just to save time. So, I’m going to copy this from my code off screen here and I’m going to paste it over here just above where we’re setting up the roots. Okay, great. And okay, we need to import log there. We’re using log and os of course because we’re reading the environment variable but we need to configure the environment variable because what we’re actually doing here is building up URLs that we want to permit to access the server basically. So we’re going to include our client side URL for the react code within the environment variable. uh and we can we can store multiple allowable clients in that environment variable by using a comma delimited string that stores a delimited string of the allowable client URLs the the the URLs that we are allowing access to our serverside code. So to do that we need to go to env and configure the relevant URLs. So, here are our allowed allowed origins here. So, we’ve got HTTP localhost 3000, HTTP localhost 5173, which is the one we’re currently running our client on, and then HTTP localhost 8080. So these ones are irrelevant for now, but you could run your client from these addresses running on port 8080 and port 3000. And our cause configuration would permit these clients to access our server. Um, so let’s go back to main.go. So we’re reading all of this this information in. We are creating this origins array here and we’re just logging them so that we can test the code to make sure that it’s working correctly. And then we’ve got we’re setting allow origins. We can’t use the other way. We can’t just allow all origins like we were doing before. We have to allow specific origins because we’re now using h the HTTP only cookies method and we want to when we publish our code to the cloud we want to use HTTPS so that our code is so that our data is encrypted on the wire between client and server that it’s exchanged between client and server. So this is another way to prevent our data from potentially being stolen. So we’re going to use HTTPS instead of HTTP so that our data is encrypted on the wire when exchanged between client and server. And in order to do that and use HTTP only cookies, we need to configure our cause in an appropriate way. And this is what we’re doing here. As I say, please the code’s quite involved. by all means just copy the code from uh GitHub and then go through it in your own time line by line so that you make sure that you understand exactly what’s going on here. Okay, brilliant. So we’ve now configured course correctly. Let’s run our server code again. Go run dot Okay, excellent. Let’s go to the client code. Let’s clear the screen. Clear. Whoops. clear npm rundev. Okay, let’s copy the URL. Let’s just make sure that that URL we have indeed permitted that URL that we’re running here 573. And we need to make sure that within our environment variable yeah 5173. So we are indeed permitting that origin to access our server side endpoints. Okay. So let’s copy that to the browser address and hopefully we won’t get an error now when we try to access our movies endpoint. And now that is working correctly. But let’s see if we can still authenticate the user a particular user. So let’s log in. Cray no it was go Jenkins. Let’s use go genkins. That was the last person that we registered. go genkins at oops at hotmail.com password with the pc capitalized one exclamation point. Okay, excellent. So that’s now working correctly. We’ve authenticated goth. Oh, okay. Oh, so it is saying hello. I was wondering what was happening there, but the font isn’t black, so it’s being hidden there. So we’ll have to sort out that problem. Hello go. Okay. And then we also need to do the log out, write the logout functionality. Let’s go to recommended. Okay. So, we got a problem here cuz we’re using the Axios private um object to access that endpoint and there’s a problem. So, let’s have a look at what’s going on. More tools, developer tools, 401. Okay. So, something is going wrong there. It’s not allowing us to. Interesting. Okay. So, it’s not allowing It’s not authenticating. It’s giving a 401 unauthorized error. It’s not authenticating Genkins when he tries to access his recommended movies. Okay. So, the recommended movies endpoint. So, what could be happening there? Let’s check. Let’s investigate this. Let’s I’m going to cancel out of this. Clear. Cancel out of the server code here. Clear. Okay. Okay. I mean that looks good. So this can’t be a problem. Um must be to do ah okay. H of course. Okay. Okay. So what I haven’t done is I haven’t addressed the middleware. So or middleware. So it goes through this middleware code when it before it accesses any of the protected endpoints. You can see here the protected roots routter use middleware. Middleware. So it’s running the code within here. So let’s go to definition and see we’re going get access token. Let’s go to get access token. Let’s go to definition there. And of course, we’re still reading it from the bearer token, which is not what we want to do. So, we need to change this code, get access token. Okay. And it’s pretty simple. The code for that is pretty simple. So, let’s comment out this code here. I’m just going to keep that commented out so you can still have a record of how you would do it if you were passing the token through the header of the relevant HTTP message. And I’m just going to paste this code in here. Token string here uh like this. So we’re reading the the access token from the cookie now instead of from the header. That’s what we needed to do there. And that is the crux of our problem. So let’s see if that has solved our problem by updating the get access token. utility function here. So we’re getting it from the HTTP only cookie instead of the header, okay, of the relevant HTTP message. So let’s run our server side code. Go run dot. Let’s run our client side code. I’m first going to clear the screen so we can see what’s going on and then I’m going to run it. So let npm rundev like this. Okay, I’m going to copy this to the browser and hopefully we don’t have any issues, but you never know. Okay, so getting our movies. Great. So, let’s log in. Go genkins at hotmail.com password one exclamation point. We’re logged in. Let’s see now if we can access the recommended protected endpoint. Excellent. Great. So, it was the way we were getting the the token within the middleware. The middleware protects those protected endpoints. So, we were still trying to get it from the header and we’re not we’re not storing the token in the header anymore. We’re storing it in a HTTP cookie for security reasons that we’ve just discussed. Great. So, those are Goth’s recommended movies. So we’re able to access endpoints and that is really good. So the next step is to write the logout functionality. Let’s do that now. Okay. So I’m just going to cancel out of that. Ctrl C to cancel out of that when you’re running the client. And it’s the same when you’re running it through VS Code on the server. Just control C. Let’s clear the screen. And now let’s write the logout code. So we’re going to actually write this function within the app component and we’re going to drill it down using prop drilling into um the header I believe. Yeah, we want the log out. We want obviously the logout button resides within the header. So when the button is clicked, we want to run that logout functionality and we are going to store that logout functionality. We’re going to store the method that handles the logout functionality within the app component. Okay, let’s create the function for handling the logout. We’ll call it handle logout. And I’m just going to paste it in here. As I’ve stated a few times now, you can just paste in the code from the code that’s stored on GitHub and uh you can copy it from the relevant page on GitHub and just paste it into your app.jsx code here. Okay, so I’ve pasted in handle log out here and we’re going to pass that down through prop drilling to the header. Okay, to the header component. So, and we can do that through this line of code. But obviously, we need to include the prop within the header component. So, let’s go to the header component here. Okay. We need to include that within curly braces. Handle log out. Okay. And then we need to include the logic within the button. So, the logout button here, we need to include an on click. And we can do that very simply with this line of code. So we include an on-click event and a non-click event handler that we’re passing down from the app.jsx component. We’re drilling that into if you like drilling that into the um header component so that when the user clicks the logout button, it runs the relevant logout code. So we actually haven’t created this endpoint on our server. So that’s the next step cuz at the moment that’s going to nowhere. But if that serverside code did exist, we’re posting the users ID, the relevant users unique ID to the logout endpoint. And the logout endpoint handles the logout functionality on the server. But let’s create the server side code, the relevant server side code for logging out. Okay. So I’m actually just going to just paste that in from the code that I’ve got. So let’s go to user_controller and I’m going to paste in the code from the prototype code that I’ve created in preparation for this course just to save time and we’ll just briefly look at the code here. So we’ve got a function called logout handler that hooks into jinggonic so that we can expose an end point that maps to this function. So when the user goes to that endpoint or when the client calls that endpoint, this function is called and the relevant logic is applied. So we’ve got a logout strct here, local strct that includes uh JSON data, the user ID, it maps to that. So this user ID will will store the user ID passed in through the body of the post request to this logout handler function. And then we’re binding to the strct with what’s passed in through the body, the user ID, which will be passed in as JSON data through to this function when the relevant endpoint is accessed. And it’s uh accessed via a post request, a HTTP post request. So we’re handling any exceptions here. Then we’re just logging it to the screen for testing purposes. we’re logging it and then we’re updating the relevant tokens to an empty string here because we’re logging out. We don’t need these stored anywhere on the server, the tokens. So, we’re logging out. So, that’s why we’re doing that and handling any exceptions. And then we’re setting the token to expired minus one to invalidate the token um the HTTP cookie. So it cannot be used anymore in subsequent requests. And we’re doing the same for the refresh token. And then we’re just passing back a message to the client with a status response HTTP status of status. Okay. And a relevant message logged out successfully. So now the next thing of course we need to do and we can do it in unprotected routes is we need to map an end point to the logout jinggonic function. So it’s so it’s an unprotected route and this is just this unprotected route has this and the path log out. So this is the end point log out and then we of course need to map it to the appropriate genonic function which is log out handler log out handler. So let’s go to the client and see what’s happening there. So we’re posting to the logout. We’re posting the users ID. It’s returning us HTTP status of okay hopefully. And if all has gone well we’re setting or to null. So we’re setting that global orth variable to null. and we’re removing it from local storage. Okay, so let’s test that out. Let’s see if we can log out of the system now. So let’s run the server. Go run dot and let’s run the client. Let’s first clear the screen here and let’s go mpm rundev to run the client. Let’s copy this URL to the browser window like that. Okay, great. Let’s authenticate as goth jenkins hotmail.com password one dot. Okay, log in. We still need to do something about this, but it has said hello goth, but it’s in black, so we can’t see it against the black header here. But we have logged in successfully. And we can go to recommended. That proves that we are logged in successfully. And then uh we can log out by pressing this button which is not working. Okay. So let’s just see what’s going on here. Oko logged out reference Axios client is not defined at handle logout. Okay. Oh, I see. Okay. No, it’s just an importation problem there. Okay. So, let’s fix that. I’m just going to go to the client and quickly fix that. We’re not importing Axios clearly here. Okay. Okay. So, we’re trying we’re trying to call the logout endpoint using the Axios client, but we haven’t imported the Axis client here. So, that’s what’s happening there. And it’s easy to remedy this problem with this line of code here. We’re just importing the Axios client. So we need to import the use or hook here like that and make sure that we set a variable the state variables or and set or retrieve the state variables or and set from the use hook like that. So, this should actually be hook, not hooks. Let’s change that there. MPM rundev. Okay. Copy that URL to our browsers. Login as Goth Genkins. Okay. Are we logging in here? Everything’s working. And let’s see if we can log out. Brilliant. So, our logout functionality is now working brilliantly. That’s great. Okay. So, what I want to do here is for the use private hook, I’m going to replace this code. So now you’ll see the significance of using a private hook here to get the to use this use Axios private hook where we’re creating a completely different access object to the one we’re using for protected for unprotected endpoints here. And of course we’re using this hook code here that creates a completely different Axio object. And the reason I’m doing that is so that we can refresh the token which will keep the user logged in. But this code is super involved and I’m not going to do it line by line because you’ll see it’s got there’s a lot here involved. But I will give you a brief explanation of what’s going on. By all means, please look at the code on GitHub and copy and paste it into your own applications and go through it line by line yourself to get an understanding of what’s actually going on here. Briefly, I’ll explain briefly what’s going on here. So, for example, let’s say the user has left the user’s browser open and the token has timed out. So what’s what’s actually happening here is this code is going to receive a an error a 401 error here. So what it does is it puts that request here within a failed queue. So it puts it within a queue here and then this code here traverses that queue and tries it again once a new refresh token a new token the token has been refreshed. So this is why we have a refresh token. The client, you know, once the access token times out, so it gets the appropriate error sent back to the client, the token is timed out. This client makes a further post request to this refresh endpoint, which we haven’t yet written. We’ll write that code in just a bit. And it gets given a new token. So the access token and the refresh token are both refreshed on the server and passed back to the client. And then the client then this code here that traverses the failed queue will then try again with the new refreshed token to access that protected endpoint. So the tokens are refreshed. So in other words, it doesn’t just log you out. the the client is still remains lo the client still remains logged in and our code here just automatically refreshes the token on the server so that the client remains logged in. It doesn’t just log you out automatically or present you with an error that your token has expired. It refreshes the token for you. And we’ve even got code here that if you’ve left the token to expire, in other words, you’ve just left your browser open and the token has expired and the refresh token has expired, then it logs you out and presents you with the login screen. So, you have to reauthenticate. But you can control the time on the server, the time when the tokens expire. But this is what this code is doing here. So, if your token has expired and you try to access a protected endpoint, it won’t just log you out. it will make a post request to the server to get to refresh the tokens. So you will remain logged in and on the quiet it’s refreshing the tokens and then a subsequent then it will process the queue here and a new call to that endpoint that you tried to access where the token expired will be made with the new refreshed token. So that will go through and so you won’t be disrupted on the client and this makes for an excellent user experience. So you will remain logged in until you actually manually log yourself out which will invalidate the tokens. So this is how this code here is working. But I urge you to go through the code the logic line by line and try to understand this code. So that’s what this code is doing here. It’s refreshing the token if the tok if the access token expires and keeps the client logged in so that the client has control over when the client logs out. Okay. So now you can see that we’re making a call here to this refresh endpoint. We haven’t yet created that refresh endpoint. So, I’m going to go to the server side code and create that refresh endpoint within the user controller file. Well, we’re going to create the handler function within the user controller file and map it to an appropriate refresh endpoint. So, again, I’m just going to copy this code in here to save time, but please by all means go through the code yourself to understand what is going on. Okay, here. So let’s copy this function handler code in here. And you can see what’s going on here. We’re getting the refresh token from the cookie here. We’re validating the refresh token. So the refresh token is automatically being passed in via the HTTP only cookie that we’re setting when the user logs in. So the token has what’s happened here is the token has expired on the client and instead of the user being logged out when the user tries to access a protected endpoint the code automatically makes a call to the refresh endpoint and this logic is run. This jinonic handler function is run and the refresh token value is extracted from the relevant HTTP only cookie here and we validate the refresh token here and we make sure the user ID that is also sent through with the refresh token. It will be within the refresh token encoded within the refresh token. So we can access the user ID by extracting that user ID from the token itself. So it’s being decoded. The claims are the claims that are encoded within the refresh token are being decoded and read here. So we’re able to read the user’s ID from the claim here and find the user within the database. Um and then we can generate new tokens. So we’re generating a new refresh token and a new token. And then we are saving those relevant And then we are saving those tokens to the relevant HTTPON cookies here. And the tokens have been refreshed. So the user remains logged in when the user accesses a protected token. And the access token may have timed out. Our client code automatically makes a request to this logic here via an appropriate endpoint and the tokens are refreshed and and saved to the relevant HTTP only cookies. So the user can remain logged in and can access those endpoints. So it’s up to the user to log out manually. So that makes for a much better user experience. But if the refresh token and so if the token if the access token and the refresh token have timed out, we’ve got this code here that actually logs the user out properly. In other words, it removes the local storage setting, the or setting. Bear in mind, this user local storage setting will no longer store the tokens. So it will store information about the user but not the tokens. So there’s no security risk in storing the the sort of the first you know the first name the last name and information like that. So the tokens are no longer stored here but we remove the user information and we set the orth global or state variable to null. So we’re logging the user out. If the access token and the refresh token have expired then we log the user out with our code. Okay. So, let’s go back here. User controller. Okay. Have an issue there. We need to close that off there. Save that. Getting a problem there. User dot user ID. User collection. I think we called it user collection. Sorry. Let’s change that to user collections cuz I’m copying and pasting the code. Validate refresh token. Okay. We don’t have validate refresh token. We haven’t written that code yet. Clearly undefined. Yeah. So we haven’t written that ref uh validate refresh token function yet. User do user ID saying user do user ID is undefined. So we’re mapping that user strct there. So, user do user ID doesn’t like that there. Um, just going to look at my what my model looks like here. Yeah, user ID. Okay. So, we’ve included that as a capital D. Okay. So, that’s the reason. So, I’m just going to make that a capital D to solve that problem there. Um, validate refresh token. We need to write the validate refresh token. I’ve already written that in the prototype. So I’m just going to copy that validate refresh token. It’s very similar to the validate token method, but obviously it’s meant for to validate the refresh token. So I’m just going to copy that in there. Let’s go to our token util file here and paste it in there. Okay. Um, we have we’re not reading in the key clearly there. So, we need to make sure we’re reading in Oh, secret refresh key. Okay, we are reading in the secret refresh key there, but we referencing a different name there. So, secret refresh key. Let’s update that there. And that now looks good. And all we’re doing here is writing standard Go code here to validate the refresh token in much the same way as we’ve done to validate the access token here. It’s pretty much it’s this code is very similar. Okay, great. Let’s go back to user controller. What’s the problem? Let’s go to problems. Ah, user ID. So that user ID there also needs to be changed to user capital ID like that. Okay, fine. No more problems. Now that we’ve written our refresh token handler, the next step is to map an endpoint to this refresh token handler function gingonic function. So this will also be an unprotected route. And let’s include the relevant post request to the refresh endpoint there. the relevant code that maps a post request to this refresh endpoint to the relevant gingonic handler function. Okay, I think we’re ready to test that now. Let’s just double check that I haven’t broken anything. We’re not going to do a detailed test on that refresh functionality, but you can by resetting the various timeouts. You can trigger a a uh access token. You can trigger the access token to expire by appropriately setting its time out. And then you can test the refresh functionality yourself. Okay. And I’m going to run the client dev npm rundev like this. Okay. Let’s copy that to the to the browser. Okay. Big screen. That looks okay. Let’s log Gu Jenkins in. Hotmail.com password one exclamation point. Great. Okay. So, we’re logging GTH in. Obviously, we need to do something about that. No point in having Hello Goth in black against a black background. So, we’ll do something about that. Recommended. So, it’s all working fine. And can we log out? Yep, we can. Perfect. So, we’re in good shape. Okay. Great. And then to do with the authentication on the front end and how it affects the front end and logging out, there’s one more test I’d like to do. Firstly, actually, I need to run the code again. Run dot npm rundev. I just want to show you something at the moment why the login functionality isn’t ideal just yet. So firstly I just so I want to show you why the login functionality isn’t ideal yet. So if I log in uh okay so let’s log in as G Jenkins again. Why not stick to the same user Jenkins atotmail.com password one exclamation point. So we’re logged in and we can access all the relevant endpoints. All is just fine and dandy and wonderful. Great. But now what happens if we refresh the browser window? Look at that. Logs us out. So we’ve we can no longer access our protected endpoints. So that’s all. So that’s a huge inconvenience if the client if the user just refreshes his browser and just gets automatically logged out. If for some reason the client decides to refresh the browser, look what happens. And we’re still, you know, we don’t want the user to be logged out at this point. We want the user to remain logged in even when they refresh the browser. So we can actually use the to to resolve this. We can actually use the orth information that gets sent down from the server when the user logs in and we can we are currently saving that to local storage and we can use that to check whether the user is logged in or not. So when the user if the user refreshes the browser, the user can remain on the recommended page and not be presented with the sign-in screen. So let’s write the code to resolve this issue. Okay, I’m just going to cancel out of that. Clear the screen. Okay, we can keep the server running. We’re not going to create any server side code. We’re just going to amend our code on the client to resolve that issue. So to do that you can see what’s actually happening here. First let’s go to orth provider. We’ve got the orth provider component here. We’re going to amend the code within the orth provider component. But what’s actually happening here is if we go to required or here you can see that it’s checking the orth state variable. It’s retrieving it from a global store within the context. So it’s retrieving the or state variable. It’s checking it here. If the orth is truth the it outputs the expected page that the users navigated to. So in this case it would be the recommended page. But if that or is falsy then it navigates it explicitly navigates the user to the login route here which is what we want if the user hasn’t yet authenticated. But if the user has authenticated, we want the recommended page displayed to the user. So to resolve this issue, when the user for some reason decides to refresh the browser and then it presents the user with the login screen, we don’t want that if the user has authenticated. But we do want it if the user is not authenticated because then the user can authenticate through the login screen and the login functionality. But if the user is just refreshing the page but is in fact being authenticated, we want the recommended page presented to the user, we don’t want the user to be navigated to the login screen. We’re setting this local storage value here with the response sent back from the server when the user logs on which contains various information about the user. Now what’s important to note here is that we are no longer sending the tokens back for security reasons. That’s being handled by HTTP only cookies. So but we are sending information about the user that is not so sensitive like the user’s first name, last name, email. That’s okay to store locally because it doesn’t matter if some nefarious code comes along and reads that information. It’s, you know, it’s not going to allow them to access protected endpoints like the token would because the token is like a key that can be used to unlock protected resources. But we do want to store information about the user returned from this login endpoint once the user has authenticated. And we are storing that in local storage within the browser. We’re storing it statically. So whether the user if the user refreshes the browser the context information is actually flushed out of memory but this user information that’s saved to local storage will be stored statically within the browser. So we can still read that information from the browser but we can’t read that state information after the users refresh the user’s browser. We can’t read the state information here in the orth variable and the orth state variable that will be cleared from memory. So we won’t be able to read that. So that is the reason why this code here is then navigating us to the login screen once the user refreshes the browser because that orth state has now been cleared. So it’s set to null. So this code is running correctly. But that’s not what we want uh for the user’s experience to be. So to resolve this, let’s go to orth provider here. And the first thing we want to do is create. So firstly I’m going to create a loading state variable and you’ll see how we use that in just a bit. And then we need to include this code here. And you can see what we’re doing here. We’re reading that user data from local storage which is stored statically in the browser. So this is the way we can get around um if the browser refreshes the user’s browser. If the user refreshes the user’s browser, that O information is lost pretty much even if the user’s been authenticated. So that’s currently what happening. But now we’re actually reading that information from local storage. And if that is successful, we’re resetting that state variable. So after the user refreshes the browser, we’re reading in that information from local storage and resetting that orth state variable which is accessed now globally. which can be accessed globally through the use or hook that we created. So that’s what we’re doing here. So when this loads again, we’re reading that information from local storage which is stored statically. So the information when the user logged in is not lost. So this is how the user can remain logged into the system even even if the user refreshes the user’s browser and the orth information is lost. We can read that information in through this setting in local storage. Okay. And the next thing we need to do is include this use effect. So when the orth state variable changes it runs this. So if author is truthy, we can set the item in local storage. And if it’s not truthy, if it’s falsy, we’re removing that from local storage there. And now we we’re handling this centrally within the orth provider component here. Okay. And we need to import use effect because now we’re using it twice within this component here. The or provider component. Okay. Okay, we got two use effect hooks running here. One that executes when the component loads. So, it’s reading in the users’s information from local storage and the other use effect only runs when the orth state variable changes. And that ch will change when the user logs in and logs out. So it’ll be set to null when the user logs out and it’ll be set to the users’s information when the user logs in the relevant information but it won’t include the tokens so that our application remains secure. We are handling the tokens through HTTP only cookies. Okay. Excellent. So now let’s go back to the login. So now you can see here where where we’re adding it to local storage. We’re now doing it centrally within the orth provider. um component. So we no longer need to do that here because this use effect will run when changes within the login component. So when we set off the this use effect will trigger if you like and this code will run. So if orth is truthy, which it will be when the user logs in, we’re setting we’re adding that information to local storage. And when we log out, conversely, when we log out, so let’s go to the app.jsx file here. When we log out here, we are actually removing it explicitly. But we don’t need this code because now that code is centralized centralized with an orth provider here. So we’re removing it when the orth is set to null. So it changes this use effect runs. It goes ify then set it. But if it’s falsy, if it’s been set to null and it’s changed from truthy to falsy, in other words, it’s been set to null. The user’s logged out, we remove it from local storage here. And that code now is central is centralized within the orth provider component. And we need to pass down the loading state variable. So, we’re setting loading to true here because when we’re setting it to true by default and then when the use effect hook runs, it’s going to be loading and then if and then once it runs through this code, the loading state variable is set to false. Whether an error occurs or not, it’s still set to false. So, that loading indicator will disappear. Okay. And then the next thing we need to do is go to required orth. Now we need to see exactly what required orth does first. So let’s go back to app.jsx. You can see here we’re wrapping these protected roots within the required orth element here. So this required orth protects these roots on the client. So let’s go to required or and see what’s happening. So you can see if orth so if the user is authenticated you can go to the protected route the or the user will see the protected route that it’s trying to access else if that is null the user’s explicitly navigated to the login screen. So the user needs to authenticate. So we need to include and this logic is fine by the way that’s perfect. So we actually need to include this code here. So we’re going to include a loading indicator this our spinner that we created earlier on. So we need to import the spinner there. So we’re going to show a loading indicator if that loading state variable that we are retrieving from use or the use or hook which engages with our context that we’ve created within or provider here. See, we’ve created this loading state variable here that’s being set appropriately within this functionality that reads in the users’s information and then sets the orth state variable appropriately. Um, so we go back to required or here. So if it’s loading, we want to display the spinner and we’re importing the spinner there. Great. Okay, we’re now retrieving the loading state variable which is now set globally within our context and this logic is correct. We don’t have to change anything else. So I think that will now work and we’re no longer setting that um local storage variable once the user logs in because we’re setting it within the lo login component because we’re setting it now centrally within the orth provider and remember the orth provider component or element wraps all the roots. So that’s that’s how we can pass down the context. That’s how the context is available to all of our components within our application which includes our protected roots where it’s needed for for example with the recommended component here. Okay. One other thing I want to do is within orth provider I just want to put this try to to this try code here to encapsulate all of this functionality here. Okay. So let’s just move that down outside of these this curly bracket there. That makes more sense. Okay, that looks all right. Okay, so we also we want to catch any exceptions like when it’s reading the local storage setting, we want to catch that if there’s a issue there. And of course, we want to set the loading indicator to false whether an error occurs or not when processing this code here, which reads in the user information, pauses it because it’ll be in string format when it reaches reads it from local storage and turns it into an object here and sets the orth state variable. PM rundev. Okay. Let’s go to the relevant URL here recommended. Okay, let’s log in like that. Let’s go to the recommended page here and let’s see if if what happens when we refresh this page. Brilliant. Okay, there we go. So, we’re refreshing the page by selecting the address and pressing the enter key. And now we’re remaining logged in. So, this remains in the log in state. And we’ve still got Hello G here in black. I need to sort that out. Um, but yeah, so that solved our problem. We’re now able to refresh the browser window and remain logged in. So that makes for a much better user experience. You don’t want the user to perhaps inadvertently refresh the user’s browser and be logged out of the system. That doesn’t make sense. So, but we want the user to be able to explicitly log out on their own terms, which is working now. Great. So, the user now needs to reauthenticate. Go genenkins@hotmail.com password one exclamation point and now the user just remains logged in no matter whether the user refreshes the browser or not. Okay. And so that works as expected as you as one would expect that to work. Hello go that needs to be in white I think. Um, so I’m going to just sort that out. Um, so I’m just going to add a class to. We can just leave that running there. Go here. Let’s go to the header component. And where it says hello goth, let’s just include these classes to style it better. And that should propagate right through. And there it is. Hello Goth. Excellent. So we’ve got now Hello Goth appearing appropriately in white on a black background. That looks much better. Excellent. So that’s all working for good measure. Let’s just try and refresh this a few times. And we’re good. We’re remaining logged in as Goth Genkins. Okay, great. So, but we can log out whenever we choose. So, let’s log out now. And we’re logged out. And you can see we need to reauthenticate if we want to go to the recommended page now. Okay. So, we’re good there. Brilliant. I’m really happy with that. Okay. Now, the next thing I want to do is create kind of a substitute for the streaming functionality. And in fact, all we’re going to do is play a YouTube video, a trailer for each of the videos, which just substitutes for the streaming functionality. So, we’re going to bypass creating actual streaming functionality and just substitute in playing the trailers in a React component called React Player. So, the first thing we need to do is actually install React Player. And it’s very easy to do. Okay. So now we want to be able to stream our movies and we’re not going to actually create an actual actual streaming functionality in its place. We’ll just run a trailer that exists on YouTube um within a component called react player. So we’re going to use a third party component to actually play the movie the relevant trailer per each movie. If we look here in compass, we can actually see that each movie each movie contains a field called YouTube ID that contains the ID of a movie that has been published to YouTube. And these particular videos are the trailer for the relevant movie document within the movies collection. So that’s how we are able to play a video pertaining to each of these movies. So we’re not actually streaming the movie. We’re we’re in fact playing the trailer for the movie which exists on YouTube. And we’re going to use a component called React Player to help us create that functionality. So let’s go into our code. So, I’m going to start by creating a folder within the components folder. And I’m going to call this stream with a small s here. Within stream, I’m going to create a file called stream movie like this.jsx. And I’m actually just going to copy in the code for stream movie. But firstly, we need to install a component called React Player, which will be leveraged to play the actual movies trailer within the browser. So, let’s go to the client terminal window here and let’s install React Player. npm install React Player. Now, if we were to run this, it would install the latest version of React Player. And I don’t want to do that because there was some breaking changes I found with the latest version. So, the one that I know works is version 2.6.0. So, to install a particular version, you can just include the at symbol like that and then the relevant version 2.1 6.0 zero and let’s press the enter key and it will install the appropriate version of React Player. So, I’m going to just copy in the code here for this component and all it does is play a movie, a YouTube video which is the trailer for the relevant movie. You can see a parameter gets passed through with the YouTube ID. We’re reading that parameter and we’re including that as part of the URL. And then within React Player, the component we’re importing here, it plays the relevant trailer for our movie. Okay. So then we want to include it within the protected roots here within app.jsx. So let’s copy this code. I’m just going to copy this code in here. that will point to our stream movie component. We need to of course import streamov within the app component. We can do that with this code here. So we’re importing it there. And we’re mapping our root path here to the relevant stream movie component there. And you can see that it’s got a YT ID parameter. So this is how we’re passing in the YouTube ID to the relevant trailer to the stream movie component. Okay. And then we need to go into the movie the movie component here. And you can see offscreen I’ve already created the code for this here. We’ve got a key here that points to that uh makes this div tag unique. This is of course the MongoDB unique identifier for the document that we’re using here as the key for this div element here. And then within here we’ve got with we’ve imported the link component from React Rout and we’re wrapping the card each of the movie cards within the link element and we’re pointing through this two attribute. We’re pointing to our stream component and we’re passing in, you can see here, we’re passing in the YouTube ID so that the relevant trailer can be played within React Player and that’s the component that we recently installed. And that we’re using we’re using that third party component to help us play the movie within the browser. Okay, excellent. So, you can see we’re closing the link down here. That’s wrapping the card within a link. And that should be great. We should be able to play our trailers for our movie. So, let’s make sure that the server is running. So, let’s run the server. Go run dot and let’s test to see if we can now play the trailers for each of the movies by clicking on the relevant movie cards. Okay. So, let’s run the client mpm rundev like that. and let’s copy this to our clipboards. Let’s open the browser window and let’s see what we’ve got here. We’ve got a problem straight off the bat. Okay. Right. So, streamoviecs failed to resolve. Okay. Super JSX. Okay. Ah, I didn’t include the relevant uh stylesheet. Okay. So, I just want to include a stylesheet within the stream folder here. It’s a scoped stylesheet. So, let’s create a new file called stream movie.css like that. Okay. And the CSS code is very basic. It’s just ensuring that the movie takes up the movie screen react player takes up 90% of the viewport and it’s going to be responsive on smaller screens with this particular style here. You can see here we’re setting the class name to that style. Okay. And now I think we’re ready to test out the functionality for playing a movie within React Player. Okay. and let’s go npm rundev like that. Okay, let’s copy that to our browser window and let’s test our code. Let’s log in as G Jenkins. Remember, it’s a protected route because we only want logged in users to be able to utilize the streaming functionality provided by our Magic Stream web site. So pass word one exclamation point and let’s log in as goth Jenkins and let’s try run one of our movies. Excellent. Look at that. I love that stuff. Okay. And now we’re good. We can run our movies here. He’s the best. And this is just not really ready for a relationship and no one. Okay, so clearly it’s streaming our movies. Well, it’s not the warden. I didn’t say since you ask the outside was an honest man. Okay. And we’re now able to to simulate the streaming of our movies just by playing the trailers on YouTube. blue. I am a bag. Great. I’m really happy with that. And there’s only one thing left to do really before we’ve finished, and that’s brand our web application. And I’ve prepared a a little icon that I actually generated through chat GPT um for the magic stream branding. And then we just need to put it in place. So, we want to place it here within the assets folder that I mean the the actual icon that represents Magic Stream. So, I’m going to So, I’ve got my icon here that represents Magic Stream. I’m just going to copy this from the prototype code that I created to this folder here. So I’m just going to go reveal in file explorer going to assets and paste it there and it should show up there. There it is. Magic stream. As I say I just generated this through artificial intelligence. And now we’re going to place it with appropriately within the header. So where it says brand here or somewhere here uh there brand we want to include an image. here just above where it says magic stream. Let’s include the image. Okay. And you can see we’ve got this logo variable here. So we need to import the image from our assets folder and we can do that with this line of code here. So we’re importing this magic stream logo into the header component and we’re just we’re branding our web page. So, and then we want to include this also in our registration dialogue. So, let’s go to the register component here. So, we want to include our logo here also within register. Okay, with this line of code here, so we need to make sure we’re importing the logo and we can do that with this line of code. And then we want to do it again for the login. form the login dialogue the login form the login screen. So let’s import logo there and then include the image within that login form to brand our website. Great. So let’s test what we got. MPM rundev. Let’s see if our website is now branded appropriately. Okay, let’s go there. Let’s run that. Look at that. We got Magic Stream there now. So, our website is appropriately branded. And let’s go to login. There it is. Magic stream register. Let’s register a user. And I think that looks pretty good. And now we can make use of our magic stream website and we can taste the streaming. Those are two boys. Okay, that’s looking good. So, if we log out, we shouldn’t be able to stream these movies because we’re not authenticated. So, let’s try that. So, if we click on that movie, we need to authenticate before streaming the movie. Whoops. And let’s log in. Brilliant. So that’s all working. Knives out. Let’s try that. Daniel Crane, ladies and gent. So that’s all working perfectly. I’m really happy with that. And that is basically our functionality already in place and we can see the admin reviews here. Brilliant. and we can run our movies. There’s one other thing I’d like to do and that’s just putting in a play button just in the middle of the movies just for effect. So we can do that quickly and that’ll be it. Then I think our functionality is all in place and you can see all the code on GitHub on the relevant repository which has been included below in the description. So, we want to include on the movies on each of our movies a play button. So, I’m going to include a stylesheet here, new file, movie.css. And let’s include the code for a transform effect when the user hovers the mouse pointer over the movie. And it also includes a play icon that will be situated over each of the movie cards in the middle of the movie image. Okay, let’s go back to movie.jsx and let’s implement the code for that. And in fact, we need to in order for that to work, we need to install Font Awesome. Okay. Okay. So, we want to include a play icon in the middle of each of our movie posters. So, we’re in the movie uh component here, the code for the movie component, and we’re going to use Font Awesome to include our play button icon within each of the movies. So, we’ve already created the CSS for that. And we’ve got these classes here that we’re going to leverage. One to create a hover effect over each of the movie cards and the other to place this one here, play icon overlay. We’re going to overlay the play icon within the movie card. So each of the movies will have a play icon in the center of where the poster is in the movie card. Um, so let’s go back to the movie component code. Okay. And we want to firstly install font awesome. Okay. So we want to install two font awesome packages. Let’s install the first one. npm install at Fort or some slash free dash solid dash SVG dash icons. Press the enter key. Okay, brilliant. That’s installed that. Great. So, we’ve got free-solid- SVG icons installed. Let’s clear the screen and install the second package we want. So, npm and install for oops at fort. Awesome for slash react dash font or some like that. Let’s press the enter key. Brilliant. So we’ve now got our font awesome packages installed and let’s import them up here so that we can leverage their functionality. So import And it’s font or some icon from open quotations at fort awesome free solid SVG icons like that. Okay, excellent. And then we want to I’m just going to duplicate that. And we want to import a particular icon called FA circle play. That’s the icon we want to import. And we need to import it from oops, sorry. This one should be react react dash react dash font. Awesome. Like that. And this is the one we want for free solid SVG icons. We’re importing a particular icon from free-solid- SVG icons like that. And then we want to place that within the center of our of each of our movie images. So to do that, we’re including a span element below the image element here. And then we are handling the actual placement of the FA circle play icon with within this movie- CS movie.css file here with this CSS code here. If we go back to movie, you can see that we are leveraging that class, that CSS class to place the FA circle play icon within the middle of our images. And there’s other functionality here for hovering our mouse pointer over the card. And you’ll see that we’ve created a hover effect, a custom hover effect over each of our movie cards when the user hovers the user’s mouth pointer over each of the movie cards to highlight that that particular movie is in focus. I think that looks pretty good. But one thing we haven’t done is we haven’t imported our movie.css file. So we got to do that. And we can do that with this line of code here. Okay, great. Let’s run our code. mpm rundev. Our serverside code is already running. And let’s copy this URL to our browser window. Close it down here like this. Okay, excellent. So, that’s worked perfectly. Our play icon is appearing within the middle of each of our movies. But when we hover our mouse pointers over the movie, that currently doesn’t seem to be working. And that is because we haven’t applied a particular class. So let’s leave that running. And we need to go back to our code here. And which class are we missing? We’re missing a particular class. Um, and it’s movie card that we’re missing. And we move missing movie card here. See, we got our card Bootstrap icon which turns this div tag into a card. And we need to apply our custom CSS code which is in the movie.css file to this element. So movie card and that should include the font. That should include the hover effect. now within our code. Let’s see if that works. Oops. Go here. And there we go. And we’ve got our hover effect now working. So, we’ve got our play button signifying that we can play. We can stream the actual movie. Of course, it’s just going to play the trailer of the movie as a substitute for that streaming functionality. So, let’s see if that works. So, if we play that there, it’s prompting us to authenticate because it’s a protected route. We can only stream the movies if we are authenticated. Genkins@hotmail.com [Music] password one exclamation point log in and it should take us to the movie and stream it for us. I suspect it had no suspects. Well, that’s looking pretty good. Jack Reacher. Okay. Good old Tom Cruz. He knows what I did. Nothing to lose. Okay, let’s go to the recommended section. We got Unforgiven. Fantastic movie. Maybe I ain’t bad man no more. We’re 11. Oh, they wouldn’t want to come looking for kill them cowboys. And that’s it. Basically, we’ve got all of our functionality now in place. And that is awesome. So, let’s see what a review looks like here. We can review our movies. We can Well, if we’re just a user, we can view the reviews, but we can’t update the reviews. So, let’s log out and log in as a an administrator. Bob Jones@hotmail.com. Password one exclamation point. We logged on as an administrator. This movie wasn’t too bad actually. Should be this movie. It’s not very good. Okay. And we can actually change this review. This movie isn’t too bad. Let’s just say this movie. This movie was absolutely fantastic. Exclamation mark. Submit the review. Excellent. So, it’s changed the sentiment from good to excellent. This movie was absolutely fantastic. And we can even watch our movie if we want. There we go. And that is looking really good. I’m really happy with that. I don’t think I’ve ever been this hung over. What’s on your arm? The face. Yeah, that’s brilliant. And that’s our application done. Let’s log out. So, we’ve pretty much coded our full stack web application. We’ve created our data store using MongoDB. We’ve created a web API component written in Go that runs on the Jinggonic web framework. We have created a UI client using React so that the users of our application can benefit from the high-erforming user interactive real-time UI responsiveness provided by React. But there’s no point leaving our code hanging about on our local machines. It’s great for testing and development, but we are eventually going to want to share our application with the public. So in this part of the course we are going to publish our application to various cloud platforms. The components of our application are going to reside on three disparate platforms. So this is an example of a distributed scalable application. We have also employed cyber security techniques so that our client and server components can communicate with one another across domains using HTTPS. We are firstly going to publish our MongoDB database to the Atlas platform. We’ll then seed our deployed MongoDB database on Atlas with the data we have stored on our local machines through the use of MongoDB database tools. MongoDB Atlas is MongoDB’s fully managed cloudnative database service designed to simplify the deployment, management, and scaling of MongoDB in the cloud. We are going to publish our ginonic web API component written in Go to Render. Render is a modern cloud hosting platform that provides developers with a way to easily deploy and scale web applications, APIs, static sites, databases, and background workers. It’s often described as a middle ground between traditional infrastructure services like AWS and fully abstracted platforms like Heroku. One of the reasons I chose render is because it still has a free tier that we can use. We are going to deploy our react client code to versel. Versel is a cloud platform optimized for front-end development especially for building, deploying and scaling web applications with modern frameworks like nextjs, react, view, spelt and more. It focuses on speed, simplicity and a smooth developer experience. Versell also provides a free tier that we can use. Great. So when we created the code for our application, we configured our server and clients to communicate with one another via HTTPS even if they reside on two separate domains. So HTTPS ensures that the data exchanged between client and server is encrypted on the wire which is part of our overall cyber security strategy. We are using token-based authentication and storing the tokens within HTTPON cookies so that for example malicious JavaScript code cannot be injected into the client and steal the tokens. So we have protected the client from XSS attacks, cross-sight scripting attacks. As discussed the tokens are like keys and keys can be stolen and used to gain access to protected resources. So using HTTP only cookies is another significant part of our overall cyber security strategy. So we have built a robust and secure full stack web application where our client and server can reside on two different domains on the internet and communicate with each other securely. So let’s get going. We’re going to start by creating a MongoDB database in the cloud and we’re going to use the Atlas platform for this purpose. So firstly we need to create an account on Atlas. So we need to navigate to https/mongodb.com and then products platform atlas database. Great. And I’m going to sign in. I’ve already created an account. If you haven’t yet created an account, please create an account. Um, you can just click this get started button here. I’m going to sign in. I’ve already created an account with my Google credentials. So, I’m just going to press Google here. Signing me in automatically. Excellent. And then it’s very straightforward. Um, and if you haven’t yet created a cluster, you can create a free cluster. So you can use a free tier on Atlas and create your MongoDB database. Um, so the first step to doing that is to hit this create button here to create a cluster. And I’m going to select AWS here as my provider. And then this free option here. And I’m going to name my cluster Magic Stream Movies. like that. Um going to choose America here. North Virginia and America free. Okay. Free forever. Your free cluster is ideal for experimenting in a limited sandbox. So this is really for just for testing purposes. And we can just create deployment now. So let’s click the create deployment button here. Okay. Excellent. Okay. And then access your Atlas data using MongoDB’s native drivers. For example, Noode.js, Go, etc. And we want Go to be able to uh access our uh DB database. So, I’m going to click this option here, Node.js driver. I’m going to select go from this drop-own list here. Okay. And then we have our connection string set up here. Okay. So, we need to replace DB password with the password for the Gavin London Database user. Okay. So, we can copy this to our clipboards. We’ve got our connection string. Let’s copy that to our clipboards. We’re going to be using that within our application to connect to our deployed version of our MongoDB database. And of course, we need to replace this with the actual password which we haven’t yet created. Okay, great. So, we’ve created our cluster here, but we need to include a password within our connection string. You can actually add users to your cluster so that you can include that in the connection string that you’ll use from the application to connect to the relevant database, our magic stream movies database. And you actually do this at the cluster level on Atlas. So, how to do that is you go to this database access option here and you can either add a new database user, but what I’m going to do is just edit my Gavin Lon D user here and add a password that I want here. So, I’m going to go edit password. Okay. And choose your own. I’m just going to use just for simplicity. Let’s show it. Password. I’m just going to include password one here. It doesn’t have to be uh super secure. We’re just testing it. We’re testing the deployment. So, I’ve just chosen a very simple password. Password one. Like that. Okay. And I’m going to update user. So, the next thing I want to do is actually seed the database with the data that we’ve got stored locally in our local version of the database here, Magic Stream Movies. And we can actually do that through the command line prompt through MongoDB database tools. So you firstly need to make sure that you’ve got the relevant tools installed and you can actually do that by navigating to this URL www.mongodb.com [Music] try download and then data face hyphen tools like this. Press the enter key. Okay. And then it’s detected. The site’s actually detected my platform. And it’s just a case of downloading the relevant MSI file and running the installation to make sure that you’ve got those tools installed. Um, which we’re going to use so that we can deploy or that we can seed the database that we’ve got running on Atlas. um with the data that we’ve got running locally. And you can verify whether you’ve got the tools installed by typing mongod dump mongodump- version like this. Okay. So I do I’ve got mongod dump installed. So now the first thing we want to do is find out where this is the path to this location here. So we can rightclick that there. Show connection info. Okay. And we’ve got local host 2707. So we need that connection there. I’m just going to copy that there. So we got a record of it there. So local host, this is the path. This is the connection string to our local databases, MongoDB databases. So we first want to make basically we want to create a we want to kind of export it. We want to create a copy of our database and we can do that using mongod dump. So we’re going to do that with this code here. MongoDump MongoD dump and then dash dash URI the d- URI option equals to and then within quotation marks go db colon slash slash and then we want localhost colon 27 1 27 7017 017 should I say. So localhost colon and the port numbers 27017 and then we want the name of our database which is the name of our local database which is magic stream movies. So include magic stream movies like this and then a dash dash out option equals to and then dot slash dump like that. So we’re creating a copy of this data if you like from so we’re creating a copy of this local database with this command. Press the enter key. And now we want to update uh the database we’ve just created on Atlas with the data that we’ve just dumped out here on on our local on our local machines. Okay. So to do that we go restore dash uri equals to and then we need to include this connection string here. So I’m going to copy that and paste it there. And we need to replace this password with the password that we chose through Atlas. So let’s replace this here with pass word one like that. Okay. And let’s see if that works. Okay. And that looks pretty good. I think that’s worked. 37 documents restored successfully. Zero documents failed to restore. So now we can actually go to our server side and see whether that has worked, whether we’ve got data in our database. Um so view all projects project zero. Let’s go to our project here and we should be able to browse collections now. So we can click the browse collection option here and see if our data has been restored within the database that we’ve got on running on Atlas. Look at that. That’s exactly what we wanted. So we’ve now got a copy of our database that we created locally. We’ve now got that data seeded on the database that we created remotely in the cloud on Atlas on the Atlas platform. Brilliant. So, we’ve deployed our data pretty much. Great. So, we’re going to upload our serverside code, our Go Jonic code to render, the render platform. But firstly, in order to do that, we’re going to push the code that we’ve got for the client and server to the to a GitHub repository. So if that’s the first step, we need to create a GitHub repository and then we’re going to push the code to that GitHub repository. So let’s go on to GitHub. So this is how you would do it. So once you’re logged on to your GitHub account to do that, you want to create a new repository. So we go new repository. And we’re going to call this one magic stream movie like this. Okay. And it’s available so we can use this name magic stream movies. And we’re going to I’m going to make this private for now but I’ll make it publicly available once the course has been released. Okay. So let’s do that. Let’s create the repository there. And uh the thing to note we’ve created this magic we’ve created the repository named magic stream movies is that if you go to go.mod and you’ll recall that we created when we initialized our server side code our go code we created the the module based on this name here. So you can see that this first part of this path here is is the path to the repository that we’ve just created. So this path would be similar only it would uh contain your GitHub username here within the path. Okay. And now we want to push our code to GitHub. So firstly let’s open an appropriate git bash uh terminal window like this. We’re going to use git bash to uh create our repository and push our code files to GitHub. So first thing we need to do is initialize our root folder that contains the client and server code as a repository. And to do that we can type git init like this. So that’s the first step and it’s all turned green then. Then I want to make sure that certain files like for example. Eenv files are not included in the upload. We don’t want to upload those to to GitHub. So to do that, let’s create a git ignore agit ignore file touch and then dot get ignore like this. And we’ve created a dot get ignore file here. And I’ve prepared offscreen the co the settings I want in the get ignore file here like this. Okay. So you can see it’s grayed out there and env there because we don’t want those included. And of of course all these node modules we don’t want that included within the within the within the the remote repository. So it’s already Visual Studio has already detected these settings that I’ve included here. Okay. And that looks good. And now type get add dot. So we’re adding the code files, the relevant code files to our repository. Great. And then we want to I’m just going to clear the screen. And then we want to commit that action. So get commit. And this is at this point we can include a message for this particular command associated with this uh commit. And I’m just going to type initial [Music] commit of full stack magic stream app like this. Okay, that looks good. Let’s press the enter key. Okay, that looks good. Excellent. And now we want to push those files to our new magic stream movies folder that we just created on GitHub. So to do that we can run these commands. So the first one is get remote and then add origin. So we’re adding from origin and then we can include. So this path that we’re going to include is based on this base path here which includes github.com and then my GitHub username. So obviously it’ll include your particular username um in your particular case. So https slash slash and then let’s include we need to include this path here. Okay, cuz that’s where we want to upload our code to on GitHub. And then let’s press the enter key. Brilliant. That looks good. And then let’s make sure that we’re pushing to the appropriate branch, which is the main branch. Branch dash m main. Like this. Press the enter key. Okay, all good so far. And now we’re pushing our local code to GitHub. So the remote repository. So get push dash u origin main. And this is the main branch we’re pushing to. Okay. And let’s press the enter key to do that. And hopefully all goes well. Well, that looks good. Excellent. So, now let’s go on to GitHub and see if that code has been successfully pushed. Let’s refresh this. Excellent. So, our code is there. And do we have environment thev file there? No, we don’t. And that’s what I want. We only want the code files there. And we don’t have things like the node modules pushed to GitHub. We don’t need those there. And we don’t have thev file here. That’s brilliant. Okay, so now we’re ready to publish the server side code from GitHub to render. So the first thing we need to do is go to the render platform, render.com. Excellent. And I can go to the dashboard here like this. But I need to sign in. So I’m going to sign in using my GitHub credentials. I’ve created my account using my GitHub credentials. So you can do that too. I guess it makes it easier to publish from GitHub if you do it like that. Okay. So the next step is we can click we can select this option here deploy a web service. Okay. And then let’s select our repository. It’s already detected all my repositories in GitHub. So, Magic Stream Movies. Okay. US, we want to keep it on the east of US. So, Virginia would be ideal. That’s where our MongoDB database resides. Okay. So, we want to include a root directory here. So for the root directory it is server magic stream stream server. I believe it’s movies. Let’s just double check that. So if we go to our code here. Yep. Server. Magic stream movies server. So magic stream. Oh, magic stream movie server. So that’s not right. Magic stream movies server like that. So that’s from our root from the root of the magic stream movies repository. We want the root directory where our commands are run to be run from this directory because that’s where our go code resides. And we want the free tier there. So all our settings look good now. And I think we’re ready to deploy, you know. Ah, very important step. We’re not using the environment variables. So, we need to add our environment variables here. Okay. So, we’re going to do that step by step now. So, let’s go to our code here. Let’s go to thev file here, which has been grayed out appropriately. So, let’s just start database name. Okay. So database name and that’s magic stream movies like this. Got that there. Now add environment variable MongoDB URI and this is going to be different now to the one that we’ve got here the local one. Um so let’s so let’s select let’s go back there MongoDB URI. And in fact, what we want is this that we copied from from Atlas. It generated this connection string for us. So this is the connection string we want. We don’t want a local connection string. So let’s copy that and paste it there. We got database name and we’ve got the relevant connection string. Uh and we need to change this password here to password one. That’s what we changed the password to for our MongoDB database on Atlas. So, it’s password one just to keep things simple. This is obviously just for testing purposes um for your actual production environment. You obviously want to select a more secure password. Okay. And then let’s go back here. Secret key. And we’re just going to keep it your secret key. environment variable secret key like that. Add another environment variable secret refresh key. Okay. secret refresh key. And we just keep going through it like this until we’ve copied all the settings, all the environment variable settings to the render platform like this. Okay, back there base prompt template. And of course that’s prompting open AI. Okay. And we need to copy all of this here right to the end. Okay. Copy that. go here, paste it in there. Add another environment variable. What’s the next one called? Okay. And this is our open API key that we created on the open API, the open AI platform. So, this is the open AI API key that we created on the open AI platform. Let’s include that there. Let’s go back here and let’s copy the relevant key here. I will deactivate this key once I’m finished with this course. So, you will need to obviously generate your own key if you want to try out the open AI functionality. Okay, let’s go back there. Paste that in there. Okay, let’s add another environment variable and recommended limit. Nearly there. Back there. Recommended limit. And it’s just a value of five like this. And we know we got one more environment variable to configure here. Allow origins. And I’m actually just going to paste those in because we haven’t yet published the client. We’ll have to come back and um adjust this setting once we’ve published the React client. And we’re going to publish the React client to Versell, the Versell platform, which also offers us a free tier, which is great. allow origins and let’s just I’m just going to paste these in as placeholder settings for now, but we’ll replace that once we’ve published the React code. Okay, and that’s our environment variables done. And then it’s just a case of deploying our web service. So, let’s do that. Let’s hit the deploy web service button. Hopefully, everything works. H required. Oh, we haven’t included that there. Okay, we’ve got to include we’ve got to include dot slash app like that. Okay, so that all looks right now. Okay, let’s do it. Wow. Okay, deploy web service. Hopefully everything goes smoothly, but when does it ever go smoothly? cloning from repository and we’re getting all of the status as it publishes our code. We’re getting all the relevant status and I’m really hoping this goes okay. So you can see it’s building building it uploading build now and it gives us a blowby-blow status of what it’s doing regarding the deploy deployment process. Deploying build successful. That’s really great. Okay. Excellent. Deploying. That’s exciting. And hopefully we don’t have any issues. Warning. Unable to fund file. Oh, okay. That’s just me. I wrote unable to fundv file. I meant unable to find file, which is actually expected. So that’s not a fatal error. So there’s just a whole bunch of warnings here. Okay. New primary port detected. 8080 restarting deploy to update network configuration. So you just note that you do need to to have your port set to port 8080. So it defaults to port 8080. So it’s best to include that as your port. Port 8080 available at And there we have we’ve got it. We’ve got our URL. And I’m going to copy that URL because we’re going to need to connect our client to that URL. So I’m going to copy that URL to Notepad here. So we got all our settings there. And we’re going to need to include that for our client side environment variable settings cuz it needs to connect obviously to the various endpoints. And this is the base URL of our endpoints on render. Okay, go back to render. And that’s looking good. And let’s see if we can test one of our end points through the browser just to see. It says it’s live. And let’s test the endpoint for slashmov. Like that. We got to endpoint. And it returns all the data for our movies which means we are ready to deploy our client on Versell. Excellent. Okay. So now we’re going to deploy the client to Versell. Now that we’ve got Magic Stream, the serverside part of the Magic Stream application running on render, we’re going to deploy our client code to Versel. So to do that, we first go to the Versell platform, the Versell website here. Okay. And you can see I’m already um logged in here. So you want to log in. I’ve actually logged in with my GitHub credentials. Just makes it easier to deploy from GitHub. Okay. And then once we’ve logged in to Versell, you can create your deployment. So first thing we need to do is go add new and then project like this. Okay. And we want to import from magic stream movies. So we go we import the magic stream movies repo. So we hit import like that. Project name magic stream movies. Okay. Choose where you want to create your pro project and give it a name. So project name Magic Stream Movies and you want to edit the root directory here. So we want to edit this field here and we want to set this to magic stream client there. Continue. So we’ve adjusted that route to where the our client code is within the repo the React code and it’s detected that the framework is Vit for us. Okay. And we can just call this project magic stream movies. That’s okay. And then we want to add our we I think we’ve only got one environment variable. So it’s considerably less arduous than when we were configuring the server side environment variables. So let’s go to ourv file in our code. And we just want to add this environment variable. So vit API base URL. Let’s include that there. And then the value for that now is our render URL. So let’s go there. And it’s this here now that we want to include as our environment variable because that is the root path the base URL of where our API endpoints are. Okay. So let’s just double check that. Yep. Yep. So that’s correct. So now we’ve we’re not on local host anymore. We’re on we’re on render. So we include this URL where our magic stream server side code resides. And this is the base URL for the relevant endpoints the magic stream endpoints. Okay. So the output is dist run build. So this is going to be our build command will be npm run build here directory is going to be dist. So that’s correct dist. And I think that’s it. I think we’re ready to deploy that now. Let’s try it anyway. Let’s go for it. Deploy. Deploy mint cued. Okay. And off it goes. It’s doing its thing. Wow. Okay. Gavin’s project, you just deployed a new project. Sure. That was pretty quick. Loading. Okay. Okay, let’s click on that to see if it’s error fetching movies. Okay, well that’s not great. Um, error fetching movies. Let’s check what’s going on there. Developer tools. Ah, it’s cause and that is because we need to configure this new URL within render. So let’s see what happens when we do that. So we got to go back to render here and to our allow origins here. We need to we need to adjust this setting here to allow the relevant origin. So to do that, we go edit. And I’m just going to put a comma here and add the HTTPS address for where our React code is running. Let’s take out that forward slash there and let’s try again. Let’s save. Rebuild. Okay, it’s rebuilding. So, it’s redeploying our application again. Okay, let’s try again. See if that makes it work. Okay, so that’s been deployed now. Just check um render. Let’s go forward slashmov. So, that’s working. It’s returning the expected data, all of our movie data. Let’s see what happens now when we try and run Magic Stream movies the client. There we go. Working. Look at that. So, if I run one, if I try to play a movie. Okay, let’s authenticate. Goth jenkins atotmail.com password one exclamation point. Let’s see what happens. It’s logging us on. Hopefully it plays one of our movies. And there it is. Look at that. And our application is working perfectly. What’s on your arm? 0 to a suspect, baby. Oh my god. Let’s go to recommended. These are recommended movies for GT Jenkins. Let’s play Unforgiven. They’re paying $1,000 to shoot them. Head on. Are you really going to kill them cowboys? Very cool. And that’s all working perfectly. So let’s log out and log in as Bob Jones so that we can include our own movie reviews. Bob Jones at hotmail.com password one exclamation point. And we should be an administrator now. Let’s go to recommended. So these are Bob Jones’s recommended movies. And we can actually add reviews now. So, um, let’s create a review for The Hobbit. Okay. The movie was awful. I really hated it. Terrible. What a an amazing movie. Movie. I have never seen any like this. Okay. Submit review. And let’s see what sort of sentiment. Excellent. And that’s what you would have expected. Um, so we got The Hobbit there is excellent within our recommended movies. And this is fantastic. Everything’s working. I’m really happy with this. Got another Harry Potter there with the Empire Strikes Back. Let’s see what Darth Vader’s up to. And where’s Darth Vader? [Music] A big turn in the strikes back singing. Let’s There he is. There’s Darth Vader. Okay, cool. R2D2. There we go. Loads our movies. I didn’t put the spinner for the loading. We need to I didn’t update the spinner for that, but that’s okay. We got the spinner going for the reviews here. Okay, excellent. Movie was absolutely fantastic. And everything seems to be deployed and working now. So I mean that’s amazing. We’ve got our serverside code running on render. We’ve got our client side code running on Versel. And we’ve got our MongoDB database running on Atlas. So it’s fully distributed and everything’s talking and all of our components are talking to each other and playing nicely together over the internet over HTTPS. So all the data exchanged between the client and server are encrypted and therefore protected. So nobody can steal our data and that is excellent. I’m really really happy with that. And let’s log out. Excellent. Thank you for joining me on this journey. I hope you’ve enjoyed going through this course as much as I enjoyed creating it. So now you can create and deploy a fully secured distributed web application using Go, React, and MongoDB. You’ve also learned how to integrate sophisticated AI functionality into your serverside web application code by prompting an LLM on Open AI through Langchain Go. These are excellent skills to have as a developer moving forward into the future. I hope to see you soon. Thank you and take care.