TLDW logo

The Ultimate FastAPI + React Full Stack Project (Deploy This and You’re Set)

By Tech With Tim

Summary

## Key takeaways - **AI Generates Branching Adventure Stories**: We're going to use AI, so an LLM to generate this story for us and then be able to play it interactively on the website. It creates this interactive kind of branching choose your own adventure game that we can play through directly on the site. [01:20], [01:35] - **Backend First for Heavy Lifting**: What we're going to do for this particular project is we're going to code out the backend first. We're going to test the backend and make sure that it works because the backend is doing a lot of heavy lifting for this project. [04:26], [04:31] - **UV Beats PIP for Dependencies**: UV is just a better version of PIP. UV is automatically going to handle what's referred to as a virtual environment so it will keep these dependencies kind of private to this project and isolated from the rest of our system. [05:58], [06:36] - **FastAPI Schemas Auto-Validate Data**: Schemas are going to define the data that comes into our API and that comes out of our API. This is one of the huge advantages of FastAPI is that it will automatically document the API for us as we write it based on us using the correct typing system. [09:31], [09:42] - **Background Jobs Prevent API Hanging**: The idea is frontend essentially submits a job and then the backend here returns the job so we're able to view the job. Then the frontend will say ask if job is done, the backend will report the status of the job and then if job is done backend can send story. [43:39], [43:57] - **Choreo Deploys Full Stack Free**: They are free. You do not need to pay for the platform in order to deploy this. Coro gives us an automatic staging environment as well as a production environment so that we can test what we're doing before we promote our build to production. [03:16], [03:05]

Topics Covered

  • Full Video

Full Transcript

In this video, you'll learn how to build a full stack application using fast API in Python and JavaScript and React. Now,

I'll cover databases, AI integrations, backend and front-end communication, designing the API, and various best practices. I'll even show you a free way

practices. I'll even show you a free way to deploy this project at the end so that you can share it with anyone that you want. Now, while I will explain

you want. Now, while I will explain every line that I write in this video, this tutorial is designed for intermediates and those looking to learn how to build a full stack application and connect the different components

together. It'll be a good idea to have

together. It'll be a good idea to have some experience with both Python and JavaScript and ideally some knowledge of both React and Fast API, but obviously you don't need to be an expert. Now,

lastly, all of the code that I write in this video will be available from the link in the description. Feel free to pause the video, adjust the playback speed, and reference that code from the description in case you're getting lost.

With that said, let's hop over to the computer and dive into this tutorial.

All right, so I'm on the computer now, and I want to give you a quick demo of what it is that we're going to build.

Now, what we're going to make here is an interactive choose your own adventure game. Now, choose your own adventure

game. Now, choose your own adventure game is one of my favorite games. It's

what I always played as a kid. And if

you ever remember those books where you essentially would flip to different pages based on the decisions that you make, that's kind of what we're doing here. So it'll make sense when I show

here. So it'll make sense when I show you the example, but essentially we're going to use AI, so an LLM to generate this story for us and then be able to play it interactively on the website. So

we can put some kind of theme in here.

So for example, it gives us an idea like pirates. I can press generate story. We

pirates. I can press generate story. We

just wait a second and then what will happen is it will create this interactive kind of branching choose your own adventure game that we can play through directly on the site. Okay, so

it just generated the story. You can see that we have a title and then we have some text. I won't read through

some text. I won't read through everything, but you'll notice that it gives us some options. So it says we can either sail here or we can explore for clues. So what's going to happen is

clues. So what's going to happen is you're going to go down these different paths based on the decisions that you're making. one path will lead to victory or

making. one path will lead to victory or like you're kind of ending the story successfully and a bunch of other ones will lead to you kind of failing or the ending, right? So, if I press on this,

ending, right? So, if I press on this, okay, it brings me to another path. So,

as your ship approaches the aisle, uh, you know, what do you want to do, right?

Do you want to battle through the storm or turn back? Okay, we turn back and it says our adventure has come to an end because, well, we decided to go back, right? So, we could restart. We can go

right? So, we could restart. We can go here. Maybe we battle through the storm.

here. Maybe we battle through the storm.

And then this way the uh story kind of keeps going and we have all of these different branches and options and choice and it's all generated by AI which I think is really really cool. And

also once the story is generated then it kind of has a unique ID and you can share it with someone. So like here I have you know 21 plus stories that I've already made when I was testing for the video and you can see here's another

one. So you could just take this URL

one. So you could just take this URL especially once we deploy this send this over to one of your friends and they can play through the same story and see if they can get to the end. So, that's

basically what we're going to be building. I know it seems simple, but

building. I know it seems simple, but this does require some kind of complex backend stuff, which we're going to talk about, as well as some front-end coding, and then connecting the components together. So, what I'm going to do now

together. So, what I'm going to do now is hop over into my code editor. I'm

going to walk you through all of the steps, but I do quickly want to let you know that the platform we'll use to deploy this later is Coro. Now, I've

teamed up with them for this video.

Again, they are free. You do not need to pay for the platform in order to deploy this. And I actually made another video

this. And I actually made another video about a year ago where I showed you guys how to make a Django in React full stack application where we used Coro and you guys really enjoyed that. So that's why we decided to team up again. Anyways,

let's get over to the code editor. Let's

start writing some code and building this project. All right, so I'm on the

this project. All right, so I'm on the computer now and we're going to start coding out this project. Now, you'll

notice that I'm inside of PyCharm.

That's the editor that I'm using. And

this is typically what I recommend for larger Python projects, especially when you're using the frameworks like fast API where there's actually native support built into this editor for those

types of frameworks. Now, I do have a long-term partnership with PyCharm where I'm able to give you an extended free trial of their Pro subscription. So, if

you want to try it out, you can do that by clicking the link in the description.

But obviously, you're free to use any editor that you want. And you'll see in this video why I prefer PyCharm because of the various features that it has.

Regardless, let's get into this. What

I've done here is in PyCharm, I've just opened up a new folder. So, just kind of start a new project in whatever editor you want to use. Open up a new folder and we're going to go from there. Now,

within this folder, I'm going to make a new directory and I'm just going to call this backend. What we're going to do for

this backend. What we're going to do for this particular project is we're going to code out the backend first. We're

going to test the back end and make sure that it works. And then once we have that, it's relatively simple for us to build a user interface around that using React. Now, for other projects, I

React. Now, for other projects, I sometimes go in a different order, but for this one, because the backend is doing a lot of heavy lifting for this project, we're going to code this out first. So, even if you're someone who

first. So, even if you're someone who doesn't like React, for example, you can still follow along with probably the first hour of this tutorial, get the backend spun up, and then you can do whatever you want for building your own custom front end that connects to the

API that we're going to build. Speaking

of which, I want to give you a quick definition. API stands for application

definition. API stands for application programming interface. And this is what

programming interface. And this is what we're going to build using fast API.

What we'll do is we'll expose various endpoints. So things like

endpoints. So things like slashgeneratetory slashgettory slashgettory result etc. And then our front end or really anything will be able to use this

various endpoints to either request data from our backend or to kind of send data to our backend to generate a new story.

This will make more sense as we go through, but that's what the backend's role is going to be here to be a secure place where we can handle all of the story generation and then returning the

story to our clients or to our front end. Okay. So, from our back end, what

end. Okay. So, from our back end, what we're going to do is we're going to open up our terminal here. We're going to cd into the backend directory. So, it would help if I spell that correctly, okay?

Because this is created. And we're going to use a tool called UV to initialize a Python project. Now, UV is just a better

Python project. Now, UV is just a better version of PIP. I'm going to put a video on screen right here that talks about everything you need to know about UV in case you don't know what it is, but you can follow along with me here after you

install the UV tool. So, if you don't already have it, just go to the web and just type UV install or something. Find

the installation docs. Very, very simple to install it. Install it on your system. And then once you have it, you

system. And then once you have it, you can run the exact commands that I'm going to do here. So, the first command is going to be uvnit and then dot. This

is going to create a new UV project inside of your backend folder. So you'll

see some files get created here. Then

all we need to do is just install our Python dependencies. And UV is

Python dependencies. And UV is automatically going to handle what's referred to as a virtual environment. So

it will keep these dependencies kind of private to this project and isolated from the rest of our system. So we're

going to start by typing UVAD and then fast API and then in square brackets we're going to put all. Okay.

We're then going to install lang chain and then lang chain-openai because we're going to use openai to um how do I save this here to generate the

stories for us. Okay. So we're going to use an llm call which I'll talk about later. Then we're going to install

later. Then we're going to install python.env.

python.env.

We're going to install SQL and then alchemy like that and u okay we have one more. This is going to be sigh. So psy

more. This is going to be sigh. So psy

and then comp cop g2-binary.

This is going to allow us to connect to a postgress SQL database which we'll need to do later when we deploy this application. Okay, so these are all the

application. Okay, so these are all the dependencies that we need. So let's

install all of them. Just hit enter. It

will create the virtual environment for us and then install and it happens very quickly too whereas pip usually takes a little bit longer. Okay, so now that all of these have been installed, we can close the terminal up and we can start

kind of templating our backend project.

Now, what I want to do is I want to create a skeleton of all of the files and directories that we're going to need so that it kind of gives us that project structure to begin. And then what I can do is start coding out individual files

with you and explaining what it is that we're building. So, inside of the root

we're building. So, inside of the root of our backend directory here, we're going to make a new Python file. And

this is going to be underscore_nit_.

Okay, so two underscores here at the beginning and the end. This is going to make this a Python package. Uh what is it saying here? Do you want to add the following? Okay, we can just press

following? Okay, we can just press cancel for right now. Uh so that we're able to import things using a convention that you'll see later on. Okay, so we have a nit.py. Then we're going to make another folder or sorry another file and

this is going to be called env. Now this

file is going to store some credentials.

It's going to be private. is just

something that we're going to keep on our local computer when we're doing development and it's going to allow us to store things like our API keys for interacting with OpenAI or doing LLM calls. Then we're going to make a few

calls. Then we're going to make a few folders. So we're going to go new

folders. So we're going to go new directory. The first directory we're

directory. The first directory we're going to have is core. This is for the core operations for our application like generating the story. Then we're going to have another new folder and this is

going to be DB for our database operations. Then another folder. This is

operations. Then another folder. This is

going to be models. This is for our database models. We're then going to

database models. We're then going to have new and another new folder. This is

going to be routers. This is for actually handling all of the API routes that we're going to have. So like

slashcreate story slash get job slash whatever. You'll you'll see the ones

whatever. You'll you'll see the ones we're going to have in a second. And

then we're going to have schemas. Okay.

And schemas are going to define the data that comes into our API and that comes out of our API. This is one of the huge advantages of fast API is that it will automatically document the API for us as

we write it based on us using the correct typing system which I'm going to show you here in Python. So all of the stuff while it may seem complex what I'm doing here is writing the API in the best possible practice and that's really

what I want to show you in this video so you understand how this can scale up to be some larger software. Okay, so we have the different folders. Now let's go ahead and create the files. Again, some

of them will be a little bit confusing right now because we haven't coded anything out yet. But once we have everything, we kind of have a skeleton of the project and then the whole kind of vision will come together. So, we're

going to go inside of core and we're going to make a new Python file. And

this is going to be underscore_nit_.

This file is going to make this folder something called a Python package, which makes it easier for us to import the different files and functions that we define within this folder. So, whenever

you see a nit.py, Pi, it just means that we're making this a package. Okay? And I

have entire videos that go over that in case you want to check those out on my channel, like packages and modules in Python. Then we're going to have a

Python. Then we're going to have a config.py

config.py file. Okay, so we're going to make

file. Okay, so we're going to make config. All right, we're going to make

config. All right, we're going to make another Python file, and this is going to be models. All right, I'm just going to ask this to not ask me again because we don't need to do this right now. I'm

going to go another new Python file.

This is going to be the prompts. So this

is going to have our kind of OpenAI prompts for communicating with our LLM.

And then we're going to have another one here which is going to be the story generator. Now whenever you're naming

generator. Now whenever you're naming Python files, make sure that if you want to have like a space in the name that you use an underscore rather than like having a second capital letter or something. This is the convention in

something. This is the convention in Python. It's called snake case. Okay. So

Python. It's called snake case. Okay. So

we have init config models prompts story generator. Okay. And we're going to

generator. Okay. And we're going to close all of these so that we don't get confused. All right. Now, we're going to

confused. All right. Now, we're going to go to database. We're going to do the same thing where we make our underscore_nit file to make this a Python package. And we're going to make

Python package. And we're going to make another file. And we're just going to

another file. And we're just going to call this database.

py. Okay. And I should have pressed Python file, but that's fine. This will

still make it a Python. Okay. So, those

two files inside of DB. Then for models, we're going to have a new file again.

this underscore_nit.

py.

Then inside of models, we're also going to have one for our story. So, anytime

that you have different models, I suggest that you put them inside of different files. So, we're going to have

different files. So, we're going to have essentially, okay, how do I explain this without getting into too much depth?

We're going to have these database models or a definition of the type of data that we want to store in our database. It's called a model. That's

database. It's called a model. That's

what we call it here inside of fast API.

So for our story model, we're going to have a story file. And then we're going to have another new file called job. And

this is going to represent the status of creating a story. So something will be a job. And then as soon as the job is

job. And then as soon as the job is finished, the story will have completed generation. And then we'll be able to

generation. And then we'll be able to grab the story. So that's why we have these two files here. One for the story and one for the job. Okay. Then we're

going to go to routers. We're going to make another new Python file. And inside

of here, we're going to have the same thing. We're going to have job. Okay. So

thing. We're going to have job. Okay. So

this is going to be all the routes for handling our jobs. We're going to have another Python file. This is going to be story. So all the routes for handling

story. So all the routes for handling our stories. And then we're going to

our stories. And then we're going to have an init.py file to make this a module. Okay. And it's fine to have

module. Okay. And it's fine to have duplicated file names as long as they're in different directories. So in this case like the models and the routers, they have the same file name, but because they're in different directories, we can distinguish what

they mean. Now continuing from there,

they mean. Now continuing from there, we're going to go to schemas. We're

going to make a new Python file and we're going to say underscore_nit to make it a module. And similarly to before, we're going to have one for our story schemas. Uh this needs to be a

story schemas. Uh this needs to be a Python file. So let's refactor that to

Python file. So let's refactor that to py. Okay, that's fine. Uh actually,

py. Okay, that's fine. Uh actually,

sorry, not refactor, rename. Okay, so

that is now story.py. And then we'll make another new Python file. And this

one is going to be job. Okay, as I said before, the way that the schemas work is that they're going to define the type of data that our API will be returning and the type of data that our API will

expect because really an API is just a way for us to communicate data between different services. So something like

different services. So something like the back end and the front end are communicating. In order to communicate

communicating. In order to communicate properly with best practice, we should define the types of data that we expect to come in and the types of data that we expect to come out. It makes our life a lot easier. And then if other developers

lot easier. And then if other developers were to come into this project, they would be able to view these schemas which we'll write later on and understand what our application is actually meaning to do. Okay, so now we

have the whole kind of project structure set up and we're going to start by just writing our basic fast API definition for kind of creating the fast API application. So we're going to go inside

application. So we're going to go inside of this main.py file and for now we can pretty much just delete everything that's inside of here. So, we're going

to say from fast API import fast API.

We're going to say from fast API dot middleware.cores

middleware.cores import the corores middleware. Okay, like that. Now, you're

middleware. Okay, like that. Now, you're

going to notice that it's telling me I have an invalid Python interpreter.

Depending on what editor you're using here, you may need to configure the interpreter to get your autocomplete working properly. So for me, I just want

working properly. So for me, I just want to select the correct interpreter for my project. So I'm going to press on

project. So I'm going to press on configure Python interpreter here. And

it's going to show me a few different options of where I can pick the interpreter. It's not actually picking

interpreter. It's not actually picking up the one that I have in my particular project. So what I can do is press on

project. So what I can do is press on add new interpreter and add local interpreter. Okay. Again, this is only

interpreter. Okay. Again, this is only if you're working inside of PyCharm.

Now, rather than creating a new one, I'm going to select an existing one. And for

the path, I'm just going to press on this button right here. And I'm going to locate the path for my interpreter. Now,

the path for my interpreter is going to be in the folder that I created. In this

case, I called this choose your own adventure game. You can see I have a lot

adventure game. You can see I have a lot of projects here. So, I'm going to go choose your own adventure game. I'm

going to go into the backend folder. I'm

going to go into the venv folder. This

is what will be created by UV when you do that UVIT command. And then inside of here, I'm going to go to scripts and I'm going to find the script called Python.

So, python.exe. This is the interpreter location. I'm going to press on okay and

location. I'm going to press on okay and okay. And when I do that, you can see

okay. And when I do that, you can see that now we actually get the autocomplete and that error goes away.

So you just need to select the interpreter from inside of here. Okay.

So now from there, what we're going to do is we are going to start defining our application. So we're going to say our

application. So we're going to say our app is equal to fast API. And when we define a fast API application, we can specify some details. So we can give our

API a title. So we can say the title is something like choose your own adventure game API. Okay. If we want we

can give this a description. So I don't know what we want to have as the description. Let's do a comma you know

description. Let's do a comma you know API to generate cool stories or something. And then we can specify a version for the API. So if

we did want to version this, which is typically best practice, uh we can do that. So I'm going to go with 0.1.0. And

that. So I'm going to go with 0.1.0. And

then if we are in production, we could kind of update this version every time we make a change. And then we could say docs URL is going to be equal to /doccks. And the redocock URL is going

/doccks. And the redocock URL is going to be equal to /redoc. The reason why we have this is all of our fast API APIs will automatically come with authentication or sorry not

authentication documentation which we'll be able to view from our web browser. So

I'm going to show you what that looks like in a second. Okay. So after this, we're going to add our middleware. So

we're going to say app.add_m

middleware and we're going to add the corores middleware. Okay, what this is

corores middleware. Okay, what this is going to allow us to do is have our API be used from a different origin. Now

whenever you're running these kind of web-based services, the origin refers to essentially the URL or the domain that you are running these services on. So

our front end, for example, is going to be running on like localhost port 5173 or something along those lines. But our

backend, which is this API that we're writing here, might be running on port 8000. So by default, our backend has

8000. So by default, our backend has some security built in, they won't allow it to communicate with anything that's not on the same origin. But by adding this middleware here called corores,

which stands for cross origin resource sharing, we're allowed to enable certain origins, so certain URLs to interact with our backend. So this is pretty much just a security thing. So for now, we're

going to go allow origins, and we're just going to make this star. We're

going to change this later on. We're

going to go allow credentials equal to true. We're going to go with allow

true. We're going to go with allow methods. Okay, so allow methods equal to

methods. Okay, so allow methods equal to asterisk and then allow headers.

Okay, equal to asterisk as well. Again,

feel free to pause here to catch up because I am going to use some autocomplete because it makes the video a little bit faster for me to actually get through rather than spending, you know, five hours building out the project. Anyways, point is what we're

project. Anyways, point is what we're doing here is we're saying okay, we're going to allow someone to send credentials to our back end. We're going

to allow them to use any API type method. So there's various methods like

method. So there's various methods like get, post, put, which specify different operations that you want to do on the API. For example, get is retrieving

API. For example, get is retrieving data, post is like making data, put would be updating data. So we're going to say, yeah, you can do any of those operations. And then headers is like

operations. And then headers is like additional information that you can send with the request. So, we're going to allow all of the headers. Now, typically

in production, you would want to make this a little bit more limited, but in our case, we're just going to allow everything so that we don't have any errors when we're writing this out.

Okay. Then, below here, we're going to say if_ame is equal to_ain then we are going to say import uicorn.

Okay, I'm just going to write this and then I'll explain what it is. And then

I'm going to say uicorn.run.

and we're going to run our main colon app. Okay, we're going to say host is

app. Okay, we're going to say host is equal to 0.0.0.0 and our port is equal to 8,000 and we're going to say reload is equal to true which means anytime we save the server

will reload so we don't need to keep rerunning it. Now, Uicorn is what's

rerunning it. Now, Uicorn is what's known as a web server and it essentially allows us to serve our fast API application. So out of the box, we can't

application. So out of the box, we can't run a fast API API unless we have some web server that is connected to. So we

use uicorn to just kind of run the fast API application. Now if you're wondering

API application. Now if you're wondering about this line here, this is just a standard Python practice. That purely

means only execute what's inside of this if statement. If we directly execute

if statement. If we directly execute this Python file, so if you were to import something from this file, then this stuff wouldn't run. But if you run this Python file, this stuff will run.

Okay, so that's our main app. We can run this to test it out if we want. To do

that, we can open up our terminal. We

can cd into the back end and we can type uv run and then the name of our file which is main.py. We should see that it runs. Gives us application startup

runs. Gives us application startup complete. And then it shows us where

complete. And then it shows us where this is actually hosted. So if we were to open this up in our browser, okay, it's saying it can't be reached. Uh I

guess cuz there's nothing really there.

And we go to something like /redoc. Um

it should show up. And actually, sorry.

The reason why this isn't working is because we need to change this 0000 to be localhost port 8000. And when we do that, we change this to localhost. Now,

the red do is appearing. Okay. So, we

can go to redoc or I think we called it docs. And you'll be able to see the

docs. And you'll be able to see the documentation for your API once we actually have any routes in there. Right

now, we don't have anything, so you're not seeing anything. Okay. All right.

So, we can leave this running if we want. And now, anytime we make a change,

want. And now, anytime we make a change, it should automatically update. However,

sometimes we will need to restart it. If

we do something like a database operation, um it will require a restart.

Okay, so what we're going to do now is we're going to start setting up some configuration. So I want to go into my

configuration. So I want to go into my environment variable file and I want to specify some of the variables that we're going to need to use. So in all capitals here, I'm going to specify database,

okay, underscore URL. And this is going to be equal to and we're just going to call this SQL. Okay? And then light colon and then three slashes and then

dot slash and then this is just going to be database db. Okay. What this means is we're just going to run this database locally on our own computer using something called SQLite. Later on we

will change this to use a production database or at least one that's hosted and deployed, but for now we'll use this. Then we're going to have an API

this. Then we're going to have an API prefix. Okay. Okay, so we're going to

prefix. Okay. Okay, so we're going to have slash our API prefix is equal to slash API. This is what we're going to

slash API. This is what we're going to have at the beginning of all of our API requests or routes. We'll then have debug is equal to true. This is

something we may need to use later when we start doing some deployments. And

then we're going to have two last variables. We're going to have allowed

variables. We're going to have allowed origins is equal to https colon slash localhost colon 3000. Okay. And then I'm

going to put a comma and I'm going to paste this again. And I'm going to change this to port 5173.

Now allowed origins here is important because anything that you specify in this commaepparated list here is what the API will be able to receive requests from. If for some reason your origin or

from. If for some reason your origin or your front end is not working, you're going to have to check the allowed origins to make sure that you've specified the correct URL. Again, this

is a security thing. And then after here, we're going to have open AI API_key.

And we'll specify the credentials for our OpenAI key later on in the video when we start using the LLM to generate our stories. Okay, so that's it for the

our stories. Okay, so that's it for the environment variable file, but I want to show you how we load in these environment variables into our project.

Now, the way that we do that is we're going to go to core and we're going to go to config. py. Now, again, this is standard practice. This is why I'm

standard practice. This is why I'm showing it to you. And this allows us to take all of these environment variables and map them into a Python object that we can then use and reference throughout

our code. So what we're going to do is

our code. So what we're going to do is we're going to say from typing import list we're going to say from paidantic

settings and then we're going to import the base settings and then we're going to say from piantic import the field if we can spell this correctly validator.

Okay. Now, Pyantic is a library that allows us to do some advanced Python kind of type handling and to again map data that's not a Python object into a Python object. So, we're going to make a

Python object. So, we're going to make a class here called settings and it's going to inherit from the base settings from Pantic. Now, inside of here, what

from Pantic. Now, inside of here, what we're going to do is essentially just specify all of the variables that are inside of this environment variable file and then what their corresponding type should be. And then what will happen is

should be. And then what will happen is automatically these settings will be loaded in for us um from this environment variable and we'll be able to use it inside of our code. So we're

going to say API prefix is a string and we're going to say by default this is equal to / API. You don't need to set a default option but I'm going to here.

We're going to say debug in all capitals is a boolean. Okay. And by default this is equal to false. We're then going to say the database, if we spell this

correctly, underscore URL is a string.

And for this one, we're not going to put any default options. We're then going to say allowed origins is a string. Okay? And by

default, this is equal to an empty string. And then we're going to say the

string. And then we're going to say the OpenAI API_key is also a string. Okay? And we're going to do something else in a second, which I'll show you. But you need to make sure

that whatever you specify here maps with what you have in your environment variable file. If you add something else

variable file. If you add something else here like you know new var or something the program actually won't load correctly because the config will tell you that you haven't specified that variable in the configuration. So you

need to make sure that the environment variable file and the config match.

Okay. Then what we're going to do here is we're going to add a field validator which is a little bit more complicated on the allowed origins. So we're going

to say at field validator and then this is going to be allowed origins here. Okay, let's put the

origins here. Okay, let's put the underscore and we're going to define a function. We're going to say define

function. We're going to say define parse allowed origins. We're going to take in our class. We're going to take in v which stands for value. We're going

to say that's a string and we're going to return a list of type string. Okay.

Now what I'm doing is I essentially I'm going to look at allowed origins which is going to be a string right that contains this comma and I'm going to convert it into a Python list because environment variable files do not

support lists like this. I can't do that. I need to take this string and

that. I need to take this string and convert it to a list because that's the type that we want our allowed origins to be in. So what I will do is I will

be in. So what I will do is I will return v dotsplit. I will split this at a comma

v dotsplit. I will split this at a comma if some value exists. Otherwise, I will return an empty list. Now, what this is going to do is it's just going to replace whatever we have in allowed

origins here with this field validated allowed origins, which will either be an empty list or a list containing the allowed origins. So, we're separated by

allowed origins. So, we're separated by commas. Now, lastly, we're going to have

commas. Now, lastly, we're going to have class config. Okay, this is inside of the

config. Okay, this is inside of the settings class. Make sure you put it

settings class. Make sure you put it inside. We're going to say the env file

inside. We're going to say the env file is equal to env. The env_file

encoding is equal to UTF-8.

And we're going to say case sensitive is equal to true. Okay. Now, this is just setting the configuration so that Python knows how to correctly load our environment variable file. If we called

it something else, then we would change the name here, but we called it env. All

right. And then lastly, we're just going to instantiate this by saying settings is equal to settings like that. When we

create this class, it will automatically load the environment variable file, do our field validation, and give us all the values inside of here. Now, in order to use something like this, we're going

to go back to main. Okay. From here,

we're going to import the settings. So,

we're going to say from core dotconfig import the settings in lowercase and then we're going to replace our allowed

origins here with settings dot allowed origins. Okay, so we're able to kind of

origins. Okay, so we're able to kind of grab that variable directly from our settings and use that here. Okay, very

good. So, now that core or we've done the configuration, we're going to start handling our database models. So, we're

going to go to models. And the reason why I like to do this at this stage is because it's important to understand the data that your application is going to use. Typically, all applications are

use. Typically, all applications are doing are really just flowing data through some type of system. So really,

you want to start by understanding what data it is you're actually going to have and the relationships between those pieces of data. Once you understand that, the system becomes significantly easier for you to build. So I want to

start with story. py and I want to define the information that we need to store for a story and how this is going to work. Now remember that a story is

to work. Now remember that a story is going to look something like this. We're

going to have maybe a story name. Okay,

so some name for the story. It's going

to have some kind of theme and then it's going to have essentially a first option or like a first kind of choice. So first

option and then for each option there's going to be children, right? Right? And

the children are going to be kind of like go left, you know, or go right.

Like these are the possible choices we could have. So the first option might be

could have. So the first option might be something like, you know, you come across a path. Do you want to go left or right? Now the children are if you

right? Now the children are if you choose to go left or you choose to go right. Now if you choose to go left,

right. Now if you choose to go left, then you have kind of this new node here where you have I don't know some text and then you have additional options, right? More options that you can pick

right? More options that you can pick from. So we have this kind of branching

from. So we have this kind of branching structure that gives us something similar to a binary tree if you've ever seen that before. So pretty much the way it works is you have something like X, right? And I'm going to try to kind of

right? And I'm going to try to kind of draw this out for you. You have some like option here which is Y. And then

you have some option on this side which is Z. And then from here if you go down

is Z. And then from here if you go down from Y again like you have some option here. Okay, maybe this option like leads

here. Okay, maybe this option like leads to the end of the path. and then you have some other option on the right side and you get this kind of like branching pattern. Okay, so I hope that makes

pattern. Okay, so I hope that makes sense but I'm going to show you how we represent that in this data model that we're about to define. So what we're going to do is we're going to import SQL Alchemy. So we're going to say from SQL

Alchemy. So we're going to say from SQL Alchemy and funny enough it knows literally everything that I want to import which is kind of crazy. Uh well

actually there's a few that are wrong.

So let's just type it out. We're going

to import column, integer, string, datetime,

boolean, foreign key, and JSON. Okay,

let's make this a bit smaller so that you guys can read the whole line. Don't

worry about it being gray. It's just cuz we haven't used these yet, so it's grayed out. Now, SQL Alchemy is what's

grayed out. Now, SQL Alchemy is what's known as an OM, which is an object relational mapping, similar to how we mapped the uh settings from our environment variable into this kind of

Python class. Fast API has these modules

Python class. Fast API has these modules like SQL Alchemy that allow us to kind of map data into these Python classes so that we don't need to write SQL code.

SQL is structured query language. It's

typically how you interact with the database. So rather than us having to

database. So rather than us having to write all of this SQL code and understand it, we can use standard Python operations and this library will handle all of the complexity of converting that into SQL and working

with our database. Okay, so we're going to say from SQL alchemy again. Did I

spell that correctly? I don't think I did. We're going to say SQL. We're going

did. We're going to say SQL. We're going

to import funk. Okay. And then we're going to say from SQL alchemy and we don't need that. We're going to say import the relationship. Okay, which

allows us to make a relationship. And

that should be good for now. We're then

going to say class story. And actually,

I'm just going to put a pass right now because I need to write something inside of my database file first before I can actually start writing out all these data models. So, my apologies, guys.

data models. So, my apologies, guys.

We're going to leave this. We're going

to go to DB. We're going to go to database.py. And I need to define the

database.py. And I need to define the kind of database initialization so that I'm able to then create these database models. Okay. So up here we're going to

models. Okay. So up here we're going to say from SQL alchemy import create engine. Create engine will create this

engine. Create engine will create this engine that kind of wraps around the database that we're interacting with.

Then the next line is going to be this from SQL alchemy.

import the session maker which makes a session that we can connect to to interact with our database engine. We're

then going to say from SQL Alchemy so it knows exactly what I want because it's very standard.exd.declarative

very standard.exd.declarative

import declarative base. Now this is a base class that all of the data models need to inherit from so that we know which data models we have in our database. We're then going to say from

database. We're then going to say from core settings and we're going to import these settings so we can use that here.

Okay. Uh, so we're going to say engine like this. And sorry, it's not core.

like this. And sorry, it's not core.

settings, it's core.config.

So we're going to say engine is equal to create engine. And then we're going to

create engine. And then we're going to say settings dot database URL for the uh kind of database URL that we want to create the engine around. We then are

going to make this session local which is equal to session maker. Okay, I'm

just going to do the autocomplete. So

we're going to say auto commit is equal to false. Auto flush is equal to false

to false. Auto flush is equal to false and bind this to our engine. Okay. So

we're just going to bind the session to the engine that we've made. Next, we're

going to make the declarative base. So

we're going to say base is equal to declarative base like that. Again, we

need this uh so that all of our models can inherit from this base class, which essentially gives it all of the properties to work with our kind of SQL OM here. All right. Then we're going to

OM here. All right. Then we're going to write a very simple function and we're going to call this get database. What

this is going to do is it's going to say db is equal to session local. So it's

going to instantiate this new session.

We're then going to say try and we're going to yield the database and we're going to accept db.close or sorry not accept we're going to put finally which essentially means when the program is

shutting down we're going to make sure that we close the connection to the database. Now if you're not familiar

database. Now if you're not familiar with the yield keyword don't worry too much about it. All this function does is give us access to a database session. Uh

and ensures that we don't have multiple sessions open at one time. All right.

We're then going to have another function. We're going to call this

function. We're going to call this create tables. All right. And inside of

create tables. All right. And inside of here, we're going to say base metadata.create all. And then we're

metadata.create all. And then we're going to say bind is equal to engine. Now, this is because when we first create this application, we need to create all of the tables

based on uh the data models that we've defined. So, we're going to call this

defined. So, we're going to call this function when we spin up the application uh to make sure that we create those tables in the database. Okay. And don't

worry about this. I'm not sure why uh it's kind of giving me these errors.

We'll look at that later if for some reason the import doesn't work. Okay.

So, now we can go back to story and we're going to go here and we're going to say from db database import and we're going to

import that base and use that. Okay.

Okay. Now, for some reason, this still is giving us this uh kind of unresolved import error. So, I'm just going to

import error. So, I'm just going to change this to say from backend.db.database.

backend.db.database.

And I'm hoping that's going to work.

Yes. Okay, that did fix it. And I'm just going to go back here to where we had core and I'm going to say from backend.core.config.

backend.core.config.

And I hope that's going to fix the problem for us. We might need to change these later, but for now, we'll leave it at this because it looks like PyCharm is resolving the import correctly. Okay, so

now, sorry, we want to go back to story and we're just going to inherit from this base class. Now, effectively what we're going to do is just define all of the fields that we want to have on our story. So, let's go through this. The

story. So, let's go through this. The

first thing we're going to do is specify the table name. So, we're going to have table name uh so underscore table name is equal to and then we're just going to call this

stories. This will be the name that we

stories. This will be the name that we store in SQL. Then we're going to have an ID. The ID is going to be a column.

an ID. The ID is going to be a column.

Okay, so we're going to say the type of this column is an integer. We need to have a unique ID for all of our stories.

So that's what we're representing here.

We're going to say the primary key is equal to true and we're going to say index is equal to true. Now a primary key means that this is a unique value.

So every single story has to have a unique identifier and you can use that as the primary key. Now index means that we'll be able to look up these stories very quickly. So we are just indexing

very quickly. So we are just indexing all of the stories so that we can find them pretty much instantly. Okay. Now

what we're doing is we're defining all of these columns. A column is just a piece of data that we will store for any individual story. Okay? And this aligns

individual story. Okay? And this aligns with how tables are created in SQL if you're familiar with that where you have rows which is kind of an entity and columns which is the data type for

particular entities. Okay, or values

particular entities. Okay, or values within the entity. So we're now going to have a title. So obviously for our story we want a title. So this is going to be a string column. Okay. And we're going to say again index is equal to true. So

we can look up stories by their title.

Then we're going to have the session id.

All right. And this is going to be equal to a column. We're going to say this is a string. And then index is equal to

a string. And then index is equal to true. Now the reason for this is that

true. Now the reason for this is that every user that creates a story, we're just going to track. Um so when someone creates a story, even if they're not signed in cuz we're not doing authentication in this video, we'll just

see what session ID created this story.

So later on, we could potentially get all of the stories created by a particular web browser session. We'll

talk about the session later on, but for now it's not a big deal. We're then

going to have created at is equal to column. And again, it already knows what

column. And again, it already knows what I want. So, it's going to be a datetime

I want. So, it's going to be a datetime column. And when we do datetime, we need

column. And when we do datetime, we need to specify the time zone. So, we're

going to say time zone is equal to true, which means include the time zone in the date. And then the server default is

date. And then the server default is going to be funk.now. This comes from SQL. Well, will just automatically grab

SQL. Well, will just automatically grab what the current time is and put that inside of the call. So this means it will pretty much automatically specify when this is created for us. We

don't need to do it oursel. Okay.

Lastly, we're going to say nodes is equal to a relationship. Okay. And this is going to

relationship. Okay. And this is going to be a story node and we're going to say back_populates

is equal to story. Now when we have tables and we want to have relationships between different pieces of data, we can create these various different types of relationships. Now you can have

relationships. Now you can have relationships that are one to one meaning you have like one thing connected with one other thing. You can

have one to many which in our case we have one story which is going to be connected to multiple nodes or various options kind of underneath this story.

Or you can have many to many where you have many things connected to many other things. So in our case, we're going to

things. So in our case, we're going to have this one to many relationship type, which you're going to see here as we start populating these values. Now, this

back populates thing here is essentially a field that's going to be added to all of the nodes that are related to this particular story. So they know what

particular story. So they know what story they belong to. Okay. So now we're going to say class story node. So the

idea here is that we're going to have this overarching story which contains kind of the metadata about the story as a whole. Select the title created at and

a whole. Select the title created at and then the different nodes. Then we're

going to have all these nodes which contain all of the different kind of options and paths for the story. So each

node has some different properties. Now

these properties will be things like the content of the particular node, if it's a root node, so if it's like the first node of our story, if it's an ending node, if it's a winning ending node, uh,

and then the other options that it has.

So let's just write this out. We're

going to start with the table name which we can just call story nodes. Okay. We

then want to have an ID. So we're going to have ID is equal to column integer and then same thing primary key and index equals true because we need to have a unique ID for all of our story

nodes so that we can reference them.

We're going to have the story ID which is going to be a column. Okay. and this

is going to be an integer and this is going to reference a foreign key to stories ID and this is going to be index equals true. Now what we're doing is we're just making sure that not

only does the story have a reference to the various nodes but each node in the story has a reference back to the story.

So a foreign key denotes this type of relationship where we're saying we want to have a column here called the story ID. It's an integer and it simply

ID. It's an integer and it simply references the stories ID. Okay, so

we're doing the table name do ID. All

right, and that will be um populated by this back populate kind of story thing which you're going to see in a second.

All right, then we're going to have content is equal to and this is going to be column and we're going to have string.

Okay, we then should spell content correctly. And then beneath content,

correctly. And then beneath content, we're going to have is root. Now, this

is important because we need to know if this is the first node that we have or not. So, we know where to start our

not. So, we know where to start our story from. So, we're going to have

story from. So, we're going to have column and then boolean and we're going to have default is equal to false. We're

going to have is underscore ending. Same

thing. This is a column boolean default equals false. So, we know if this is an

equals false. So, we know if this is an ending node or not. We're going to have is winning ending equal to the same thing. col column boolean boolean

thing. col column boolean boolean default equals false. And then we're going to have the options, okay, which is going to be equal to a JSON column. So we're going to have JSON

JSON column. So we're going to have JSON and then default is equal to a list. Okay, and then we're going to have story is equal to

relationship and we're going to say story and then back populates notes. So

sorry, uh, ignore the foreign key thing that I was referencing before. uh this

is just going to store the ID of the story whereas we're then going to have this whole other relationship. So we

define the relationship here in nodes as well as in story and then for each node we're going to have this story reference right and we're back populating that with nodes. Okay so these kind of

with nodes. Okay so these kind of reference each other I know it's a little bit confusing I don't want to get too into the foreign key relationships in this video but this will create that relationship for us. So that's it for

our story tables. Okay, feel free to pause the video here and kind of code this up. Now, what we're going to do is

this up. Now, what we're going to do is go over to job and this one's going to be quite a bit simpler. Now, the reason we need to have a job model is because the stories take a little bit of time to

populate or to be created by the LLM.

So, basically the flow is going to be here that when someone submits a request to create a new story, it won't be created right away. So, we're first going to create a job. Now, a job is

going to represent the intent to make a story. And this is going to have a

story. And this is going to have a particular progress. It's going to be

particular progress. It's going to be like in progress or completed for example or failed. Now we will essentially keep checking for the status of this job to be completed. And once

this status is finished then the story will be complete and we can grab it. So

we'll talk more about how we do that later on. But that's why we have this

later on. But that's why we have this job. Okay. It's very common when you

job. Okay. It's very common when you have these kind of longer operations on the back end that you don't want the back end to be waiting for this operation to finish before it returns a

request. So the idea is

request. So the idea is front end essentially submits a job.

Okay. And then the back end here returns the job. So we're able to view the job.

the job. So we're able to view the job.

Okay. Then the front end will say ask if job is done. the backend will report the

status of the job and then if job is done backend can send story. Okay, so I hope that makes a little bit of sense.

Uh it will when I start coding this out more, but the basic idea is the job will tell us the status of the story creation. So we're going to copy a few

creation. So we're going to copy a few things from here just to save us some time. In fact, let's copy all the

time. In fact, let's copy all the imports and then we can just remove the relationship because we're not going to need that here. We can remove the foreign key and uh JSON fields as well.

And actually I don't think we need the boolean field either. Okay. So let's

come down here. We're going to say class and this is going to be a story job.

This will inherit from base. We're going

to say underscore table named is equal to the story jobs. And we'll

start defining the fields. Now we're

going to have ID. Okay, this is going to be column integer. Primary key is equal to true. Index is equal to true as well.

to true. Index is equal to true as well.

We're going to have a job ID which is going to be a string. Okay, we're going to index that as true and we're going to set a unique constraint. So we're going to say unique is equal to true which means that all the job IDs need to be

unique. The reason we have a job ID is

unique. The reason we have a job ID is we want to have a longer value that's a string uh rather than just this numeric ID. Okay. Then we're going to have a

ID. Okay. Then we're going to have a session ID of the person who created this job. So we can say column string

this job. So we can say column string same thing index equals true so that we can look this up quickly. Then we're

going to have the theme of this. So this

is going to be a column which is a string.

Okay. We're going to have the status.

This can also be a column that is a string. We're going to have the story

string. We're going to have the story ID. Okay. This is going to be a column

ID. Okay. This is going to be a column that is an integer. Now we could make this a foreign key but I am just going to make this an integer to make it easier. and we're going to say nullable

easier. and we're going to say nullable is equal to true and this is going to store the story ID that was generated um by this job. Okay, we're then going to have an error and this is going to be

equal to column string and we're going to say nullable is equal to true. When you say nullable equal to true, this means that this can

have no value. It can be null or empty.

Okay, we're then going to have created at and same thing, we're going to copy what we had before. So this is a datetime column with the time zone equal to true and the server default uh function now so that it will generate

that for us automatically. And then

we're going to have completed at so we can see how long the job took. So

completed at is equal to column and we're going to do the same thing except this time we'll just have nable equal to true because it's possible this job never completes if it fails. Right? So

that's why we have that. Uh hopefully

that is making sense. I think that is pretty much all we need for the story job. So now we have our story, we have

job. So now we have our story, we have our job models. Okay. And we're going to go and we're going to start writing the schemas and then the different API routes that we need. Okay. So let's

close database. Let's close models and let's go to schemas. Now these will seem a little bit vague uh until we get into writing the routes, but just trust me they're important and once we have these

then we can write the API routes uh and start kind of testing the API a bit.

Okay, so let's go to schemas and let's start with story. Okay, now essentially what we're going to do here is we're going to define these Python classes

that specify the type of data that we want our API to accept and to return.

This is really important because it allows Fast API to automatically do some data validation for us to ensure that the data is correct that's coming into the API. So you'll see what I mean as we

the API. So you'll see what I mean as we start writing it, but just bear with me here until we start utilizing it. So I'm

going to say from typing import. Okay.

List optional and dictionary. All right.

We're then going to say from datetime import datetime.

Okay. And from piantic import the base model. All right. Now

for all of our schemas they need to inherit from the base model or at least have one parent class which is the base model. That's because paidantic will do

model. That's because paidantic will do automatic data validation. So

essentially someone can send something like you know theme is Dubai or whatever to our API but they're going to send it like this. Now in a traditional API

like this. Now in a traditional API framework we would need to check to say okay does theme exist? All right it does exist. Okay is it not empty? Okay is it

exist. Okay is it not empty? Okay is it a string? Right? We'd have to check all

a string? Right? We'd have to check all these values to make sure that the data being ingested by the API is correct before we start processing it. However,

Pyantic can automatically do this for us if we tell it what we want the shape and structure of our data to look like. So

that's what the schemas are, the structure of the data that's coming in so that Pyantic can automatically validate it. So first we're going to say

validate it. So first we're going to say class story options schema. Okay, and

this is going to inherit from the base model. Now we have a few schemas, so

model. Now we have a few schemas, so just work with me here. And some of them inherit from others. So we're going to say text is string and node id is

optional. Okay. And then int equal to

optional. Okay. And then int equal to none. Now this is going to be a class or

none. Now this is going to be a class or a type that we're going to use in 1 second. We're then going to say class

second. We're then going to say class story node base.

Okay. And this is going to inherit from base model. And here we're going to say

base model. And here we're going to say each story node is going to have some content which is a string. It's going to have is ending which is a boolean which by default can be equal to false and an

is winning ending which is also a boolean which will by default be equal to false. Okay. So we

have these two kind of subasses which we'll use here in one sec. Now we're

going to have class complete story node response. Now notice

the naming convention. Okay. So when I say base, what this means is that I'm not going to directly use this in my API. I'm going to use it as a parent

API. I'm going to use it as a parent class to make things a little bit simpler when I write more schemas later on. When I write response, this means

on. When I write response, this means that this is what the response from my API is going to be. So what gets returned to the front end when I write request, which you'll see in a second, that is the data coming from the front

end to the back end that we're accepting right into the back end. So from here this is going to inherit from the story node base. So a completed story node

node base. So a completed story node response is always going to have content is ending and is winning ending. So

we're going to have all of that plus we're going to have an ID which is an int. Then we're going to have options

int. Then we're going to have options and this is going to be a list of the story options schema which is why we wrote that and by default that's going

to be equal to an empty list. We're then

going to say class config and this is going to be from undersc_attributes equal to true. All right. So what we're saying is that a complete story node response also includes the ID and some

options and those options look like this. They have some text and they have

this. They have some text and they have a node ID. Okay. So that's why we have the story option schema here for the options from a complete node response.

Again, I know this is confusing. Just

bear with me here. And if you really need to, you can ask the LMS to kind of clarify what's going on. if you want to know line by line what's happening here.

This one's just a bit too complex for me to get into all the details without spending too much time on it. So next

we're going to move on to another schema which is the story base. Okay. And this

is going to inherit from the base model.

And what this is going to have is a title which is a string. It's then going to have a session ID which is optional

string equal to none. And then again we're going to copy in this class here.

Okay. So class config then what we're going to do is have class create story request. All right this is going to inherit from base model and

this is simply going to have a theme which is a string. So all we're saying is when you want to make a new story here you can pass uh we can use this schema where we just expect that you pass us some theme and that theme is a

string. And then lastly, we're going to

string. And then lastly, we're going to have a class which is the complete story response. Okay, this is going to inherit

response. Okay, this is going to inherit from the story base. All right. And then

in here, the complete story response is going to have an ID. It's going to have a created at time which is going to be datetime. It's going to have a root node

datetime. It's going to have a root node and that root node is going to be this complete story node response that we specified right here. Okay, for one particular root node and then we're

going to have this all underscore nodes here which is going to be a dictionary where we're going to have an int as the key and a complete story node response

as the value. Okay. And then we're going to do again same thing the class config equals from attributes which just means when we define this we can specify the attributes that we want to have here.

Okay. Again I know it's a little bit confusing with the schemas. Now that

they're written, we don't really need to come back to them at least for the story. Let's just do them for job and

story. Let's just do them for job and then once we start using them in the routes, they should make a bit more sense. But again, the way that we've

sense. But again, the way that we've defined them here is that Pyantic can automatically look at the data and validate that it's correct so that we can use these Python objects rather than

relying on just normal Python dictionaries or JSON strings or things that aren't typed correctly. This also

allows us to have really good documentation, which you're going to see later on. Okay, so we're going to go to

later on. Okay, so we're going to go to job.py and inside of job.py, this is

job.py and inside of job.py, this is going to be a little bit simpler. We're

going to write the schemas here. So

we're going to say from typing import optional from datetime

import date time from paidantic import the base model. Okay, we're then going to have our class and this is going to

be a story job base which will inherit from the base model and we're just going to say that okay, so for all of our jobs we're going to have some theme which is a string. Okay, we're then going to have

a string. Okay, we're then going to have class which is a story job response and this will inherit from not the story job base but from the base model. So let's

fix that. Now, for our response, we're going to have our job ID, which is an int. We're going to have our status,

int. We're going to have our status, which is a string. We're going to have our created at, which is date time.

We're going to have our story ID, which is an optional int because if the story is not generated yet, then we won't have this.

We're going to have our um completed at oops completed at which is also an optional datetime equal to none because if the story isn't completed yet well we

don't have one. And then we're going to have our error which is also an optional string equal to none because again if there's no error well there's no error, right? And then we're going to have

right? And then we're going to have class config and then from attributes is equal to true. Okay, that's pretty much it. Um, we'll do one last one just

it. Um, we'll do one last one just semantically to make this kind of follow our convention where we're going to have a story job create. Okay. And this is

just going to inherit from the story job base and then just pass. Now, the reason why I'm doing this is because we may use the story job base somewhere else. And

then what I want to do is I just want to have something that specifies, okay, this is going to be used for like a request. So like creating something. So,

request. So like creating something. So,

I'm just essentially renaming this so that when I use it in my code, it's a little bit easier to understand the meaning of it. Um, understand that might be a little bit confusing, but I'm just

trying to keep like the same convention.

So that now when I use this, I use it for ingesting data and rather than it being the story job base, which doesn't make sense, it's the story job create.

So, I've just given a new name essentially to this base class and then later on if I want to add anything to the job creation process, I can add it inside of here. Okay, so that's job and

story schemas. Now, we're going to go to

story schemas. Now, we're going to go to routers and we're going to start with story. Okay, so we're going to go to

story. Okay, so we're going to go to routers. Now, a router is where we

routers. Now, a router is where we actually write the end points that are going to be hit by our user. Now, this

story router, uh, what we'll do is we'll write all of the endpoints and then we'll write the implementation later.

So, let's start with our imports. We're

going to import uyu ID, which is a unique identifier that we can generate for our story um, ids. We're going to say from typing import optional. We're

going to say from datetime import date time. We're going to say from

fast API import the API. Oops. Let's get

out of here. Import the API router depends HTTP exception cookie response

and background tasks.

All right, we're going to say from SQL Alchemy import session and then we're going to say from backend

db dot what is this database import the get database and the session local we're

going to say from the models dotstory import the story and the story node and this needs to be backend.mmodels so

let's fix backend.mmodels.

backend.mmodels.

Okay, we're going to say from backend domodels job import the story job and we're going to

say from schemas dotstory and again this needs to be backend.s

schema. So let's fix that up. We're

going to import the complete story response, the complete story node response, and the create story request.

Okay. And we can fix the indentation here and continue. Okay. Next, we're

going to say from backend dots schemas.

import the story job response. And that

should be good for right now. Next, what

we're going to do is we're going to make a router. Now, these routers allow us to

a router. Now, these routers allow us to write different endpoints in different files and have different API prefixes for them. So, you'll see what I mean,

for them. So, you'll see what I mean, but we're going to say uh define the API router. We're going to say specify the

router. We're going to say specify the prefix, which is going to be equal to slash stories like that. And then we're going to say tags is equal to and then

stories. This is just for documentation.

stories. This is just for documentation.

Now what will happen now is that we have an overlying or an underlying I guess uh API prefix called slappi which means anytime we want to access any of our

routes we have to go to / ai. So we go to like backend url / ai but then for this part uh particular router we're adding another prefix called sltories.

So every time we want to access any of the routes here we're going to go to the backend URL /tories slash endpoint. Endpoint being the

slash endpoint. Endpoint being the specific URL that we want to hit. So

something like you know create story.

Okay. So that's what we're doing. So

we're organizing the story specific routes. Now what we're going to do is

routes. Now what we're going to do is we're going to write a few functions that we need. So we're going to say first define get session ID. This is

going to take in a session ID which is optional string equal to a

cookie of none. So essentially a session will identify your browser when you're interacting with a website. Now sessions

can expire after a certain amount of time but they can store information such that the website doesn't need to keep uh kind of loading new state for you or sharing new information with you. So for

example, if you ever go to a website and you don't deal with any authentication stuff, what ends up happening is it might remember like buttons that you pressed on or things that you've done on that site for a particular amount of time. And that's because it's

time. And that's because it's identifying your particular web browser and storing the ID of that session so that it can kind of repopulate the state that you had the last time you were on

that site. So we're doing a similar

that site. So we're doing a similar thing here where we're going to get the session ID which again is has nothing to do with authentication and it's not something that's like super secure or

reliable but that will identify the particular browser session that's using this website. Okay. So, if you ever see

this website. Okay. So, if you ever see things like your, you know, your session timed out, that means, well, the session expired, so now a bunch of the data you had on the site kind of got cleared and you need to, uh, what do you call it?

Refresh that again or load it in again or fill in the form again or whatever the information is. That's what that means. Uh, and anyways, let's continue.

means. Uh, and anyways, let's continue.

So, we're going to say if session ID, uh, or if not session ID. So, for some reason, no session ID already exists, then we can make a new one. So we can

say session ID is equal to the string of UU id dot uyu ID4 and then call this function and then we can simply return the session ID. So

what this is going to do is just look for your session ID and then if you don't already have a session ID, we're just going to make a new unique one for you and then return it. Okay, let's

continue. Now let's make our different endpoints. All right, so the first

endpoints. All right, so the first endpoint we're going to have we're going to say at routouter.post.

Post is what you use when you want to create something new. And we're going to type slashcreate. And then we're going

type slashcreate. And then we're going to specify the response model is equal to the story job response. Okay. Then

we're going to go here and say define create_tory.

And what we're going to specify in here is all of the values that we need. So

first we're going to say request is equal to the create story request. Now

notice I'm using the schemas that I defined. So I'm saying, hey, we're going

defined. So I'm saying, hey, we're going to return a story job response, but we are going to accept a create story request, which will contain the theme that we need, right? We're then going to

have some background tasks. So we're

going to say background tasks is equal to background tasks. We're then going to say the response is equal to response.

We're going to say the session ID is a string that depends on the get session ID function. And then we're going to say

ID function. And then we're going to say the database is a session that depends on the get DB function. Okay, it'll uh go away from being gray in a second

here. Now, what this is going to do for

here. Now, what this is going to do for us is it's going to inject these dependencies or these values into these parameters. So there's this really cool

parameters. So there's this really cool thing in fast API called depends and again what it will allow us to do is essentially call this function anytime this uh endpoint is hit and grab the

value of our session ID. Same thing with the uh get database it will just grab the database and throw that in this database value. And then we have the

database value. And then we have the request which we'll be able to get the response the background task which we'll use to create a background task which can run independently of the main thread which I'm going to talk about in a

second uh when we start setting this up.

Okay. Okay, so now we're going to say response do set_cookie and we're going to say the key is equal to session ID and the value is equal to

session ID. Now what this effectively is

session ID. Now what this effectively is going to do is just store what our session ID actually is so that we're able to use it later. It's not secure.

Someone would be able to see what their own session ID is from their browser, but it's fine. We're just using it to store what the user's session is. And

even the session stuff is not really necessary for us to do. But I'm just trying to show you a bit more complexity in case you want to identify users based on their web browser instance um rather than like some authentication thing cuz

we're not using authentication here. And

I also am just going to add this HTTP only is equal to true. Okay. Next, what

we're going to do is we're going to generate a new job ID. So effectively

what's going to happen here, right, is that as soon as the user wants to create a story, really what we're going to do is create a job. Once we make the job, that job will then trigger a background

task to run which will go to OpenAI to call an LLM to make the story for us. We

haven't yet written that component, the LLM component. But this is going to

LLM component. But this is going to handle kind of from our backend at least what we need to start setting up. So the

job right that we can return before that task runs. So, we're going to say job uh

task runs. So, we're going to say job uh id is equal to string uyuid doyuid4 like that. We're then going to say job

like that. We're then going to say job is equal to story job.

And this is going to create a new instance in our database because I imported this from my models. Right? So,

we're going to make a new story job. So,

we're going to say job ID is equal to job ID. We're going to say session ID is

job ID. We're going to say session ID is equal to the session ID to identify the person who made this. We're going to say the theme is equal to the request theme

which is from this right the create story request which we can look at and the status we're just going to hardcode this is equal to pending. Okay. So now

we have our job and we've kind of created this job but in order to add it to the database we need to do the following. We need to say database.add

following. We need to say database.add

add the job and then db.comit. Okay, so

anytime you add something, what you're doing is kind of like staging it. You're

putting it in a place where it's ready to be finalized in the database. And

then as soon as you run this commit command, it will actually commit this.

So it will save it into the database.

And that's the job of the OM. Okay, the

object relational mapping, it will automatically kind of run the proper SQL code to save this job story or this story job. Okay. Now, beneath here, I'm

story job. Okay. Now, beneath here, I'm going to put a to-do, and I'm just going to say add background tasks because in a minute, we're going to have to add a background task that will actually run

so that we can generate the story. So,

because right now, we're not actually generating the story, right? So, we're

going to say generate story. So, we know that okay, later we got to actually generate the story here. But for now, we can return this job, which will give us the job ID. And then what will happen is

once we have the job ID, we can kind of keep checking like, all right, what's the status of our job? Is it done? Did

it fail? Is it still pending? Um, and

we'll be able to get that. And then once the job is ready, we can return the entire story. Okay, so we are almost

entire story. Okay, so we are almost there. What we're going to do now is we

there. What we're going to do now is we are going to write a function called generate_tory task. Okay, and this is going to take in

task. Okay, and this is going to take in the job id, which is going to be a string. It's going to take in the theme,

string. It's going to take in the theme, which is going to be a string, and the session ID, which will be a string as well. Now, what we're going to do here

well. Now, what we're going to do here is we're actually going to create a new database session. So, we're going to say

database session. So, we're going to say DB is equal to session local. And the

reason why I need to do this is because if I use the same session that I'm using from here, so inside of all of my API routes, then I'm going to have some hanging operations where essentially

when this background task is running and it's using the database, my API will not be able to use the database at the same time. So this is a kind of a weird thing

time. So this is a kind of a weird thing and it has to do with running background tasks and something called asynchronous operations. So let me break this down a

operations. So let me break this down a little bit. What's going to happen,

little bit. What's going to happen, right, is that when they send this request, it's going to take us maybe 20 seconds to generate the story. We don't

want to wait 20 seconds before we return the story to the user because if we were to do that, then this request is just hanging. It's just sitting there and

hanging. It's just sitting there and it's not doing anything and we're waiting on something out of our control like the LLM to return us some data. So

rather than us kind of waiting this really long amount of time and essentially hanging the whole program, which means nothing else can happen during this time, what we're going to do is immediately return this job to the

user, telling them that their task is currently in progress. We're then going to create a background task, which will run in the background using a new thread, which means that anything that

the background task is waiting on is not going to interfere with our API. So

other users will be able to come in here and make new jobs. So we can have multiple of these jobs running at the exact same time and they can all be waiting for the LLM to give them the story. And then as soon as the story is

story. And then as soon as the story is generated, they can update this job and we can return the new job to the front end. Now, it gets tricky here though

end. Now, it gets tricky here though because if we were to try to use the same database instance that we had from this endpoint that we grabbed here inside of this generate story task, then

because the database is going to be waiting for certain operations to be performed, we are going to end up having this kind of weird synchronization and hanging problem going on where we're

using this object that exists in this thread inside of this thread and it's waiting on certain things to happen. Um,

I don't know how to explain this better because it gets into like some pretty complex threading and asynchronous operations. The point is this line is

operations. The point is this line is necessary to make sure that we have a new instance of our database. So, a

separate session. So there's kind of two database sessions active at the same time. So that one session can be doing

time. So that one session can be doing something while the other one's kind of waiting on something else to occur rather than if we use the same one then we're going to be clogging up our background tasks and they're all going

to be just waiting on this one session to be available when we could just create another one. I hope that makes a little bit of sense. Um, again, that's kind of the best I can explain without

spending like 20 minutes going through threading and async operations and more advanced coding. Okay, so we're going to

advanced coding. Okay, so we're going to go into a try block now. Uh, so we're going to say try and what we're going to do is we're going to look for this job.

So once this job is created, we're going to get it from the database. All right.

So, we're going to say job is equal to db dot query the story job dot filter. And we're going to say story

dot filter. And we're going to say story job dot job

id okay is equal equal to job id dot first. All right. So this is how you

first. All right. So this is how you perform a query operation in the database. What we can do is we can say

database. What we can do is we can say all right we're going to look in our database. We created a new session,

database. We created a new session, right? We're going to query the story

right? We're going to query the story job table or the story job model and we're going to apply this filter. We

want to see if the story jobs job ID is equal to the particular job ID we have here. And then we're going to grab the

here. And then we're going to grab the first entry that we find. Now again the reason why we need to do this is because if I were to pass this job here uh to this function we would get the same

problem that we had if we were to pass the database because we're passing or sharing kind of references to the same object across different threads which is problematic. So I'm going to say if not

problematic. So I'm going to say if not job then return. So if there's no job available then there's nothing to do here. So we're going to return.

here. So we're going to return.

Otherwise, we're going to have another try block and we're going to say job status is equal to processing. So, we're

actually modifying this job model. Then,

we need to save this change in the database. So, we're going to say dbom

database. So, we're going to say dbom commit. We're then going to say story is

commit. We're then going to say story is equal to and for right now I'm going to put an empty dictionary, but later we'll actually generate the story here. Just

right now we don't have a way to generate it. So, I'm going to put to-do

generate it. So, I'm going to put to-do generate story. Okay, then we're going

generate story. Okay, then we're going to say job.story

id is equal to for now we're going to put one and we're going to put you know update story id. All right, we're going to put a to-do because again we haven't generated uh

written the story generation script yet.

Then we're going to say job status is equal to completed and we're going to say job completed at is equal to datetime.now. and we're going to save

datetime.now. and we're going to save this in the database. So, dbcommit.

All right. Now, if an error occurs here, we're going to accept an exception as e and we're going to do a similar thing.

So, we're going to copy these four lines. Okay, we're going to put this in

lines. Okay, we're going to put this in here. We don't have a story ID because

here. We don't have a story ID because well, none was generated, but the status we can set to failed. We can have the job completed at still be this. And then

we can have the job.

is equal to string of e. Okay, so this is what's going to happen for this except block. Now, we also need to have

except block. Now, we also need to have another accept block. So, we're going to say exception or except exception as e. And actually, we

actually don't need the except sorry, we're going to say finally. And then

this is going to be db.close. So just no matter what after we do this operation I want to close the database instance so that I don't have this open thread or this open reference we don't need. All

right so now this function is written we actually can use this function from here. So where we have our to-do we're

here. So where we have our to-do we're going to say background tasks okay do add task and we're going to add the task which is this function. We're going to

say generate story task. And then the way this works

story task. And then the way this works is we need to pass the parameters here that we want to pass to this. So we're

going to say that the job id is equal to and then this is going to be the job ID.

We're going to say the theme is equal to request theme and the session ID is this user session ID. And then we can remove this to-do. So what will happen is we

this to-do. So what will happen is we will add this background task. It will

start running this particular function.

this function will actually generate the story. But right now, of course, we

story. But right now, of course, we don't actually have anything to generate. So, it's not doing that right

generate. So, it's not doing that right now. But at this point, this would be

now. But at this point, this would be the operation that will take a bit of time to run. So, we can just have this in the background and not interfere with the rest of our API. Okay. So, that's

almost it. Now, there is another endpoint that we need to write here. And

this is to retrieve the finished story.

So, all of this that we wrote was to kind of create the story. And we're

still not done with the creation because we need to write a few more functions.

But we also need the ability to get the story once it's finished. So we're going to say at routouter.get.

Okay. And we're going to say slash and we're going to put a dynamic uh variable here called story ID and then slashcomplete.

And we're going to say the response model is equal to the complete story response. So from here we're going to

response. So from here we're going to return the entire completed story. So

we're going to say define complete story. Okay. Or we can say

complete story. Okay. Or we can say maybe get complete story or something.

And we're just going to take in the story ID. Okay. Which is going to be

story ID. Okay. Which is going to be this. All right. So we're just getting

this. All right. So we're just getting it from the path parameter. And then

we're going to say the DB is session.

And this is going to be equal to depends on the get database function. Okay. Now

it's important that the parameter you have here called story ID matches this path parameter. This is a convention

path parameter. This is a convention directly in fast API where if you want to have a dynamic value in the URL, you wrap it in these braces and then as long as you write the same variable name and specify the type that you want it to be

in the parameter for the function definition, it will automatically populate this value for you. Okay, so we have that. Now what we're going to do is

have that. Now what we're going to do is we're just going to look up the story based on its story ID to see if it actually exists. So we're going to say

actually exists. So we're going to say story is equal to db dot query the story table. We're going to filter so that the

table. We're going to filter so that the story do id is equal to the story underscore id and then we're going to get the first value if it exists. Now if

there's no story so if not story then we're going to raise an http exception with a status code 404 and the details that we couldn't find the story.

Otherwise, we're going to return the story. But before we return the story,

story. But before we return the story, we need to actually parse it. So, I'm

going to say to-do parse story.

Okay. Now, before I do this, I want to write some other parts because it's going to make be more clear once we actually generate the story what the story looks like and why I mean we need to parse it. But essentially, before we can return the story, we need to turn it

into a format that is going to be acceptable by our front end, which does require a little bit of logic. So down

here we're going to have a function called build_complete story tree. Okay, this is going to take

story tree. Okay, this is going to take in a database session and it's going to take in the story and it's going to

return a complete story response. Okay,

so for now we're just going to say pass.

But what we're going to do is we're effectively going to generate the story here. So we're going to say complete

here. So we're going to say complete story is equal to a complete uh story tree. We're going to pass the database

tree. We're going to pass the database and the story that we found and then we're going to return the complete story. Okay, which will be generated

story. Okay, which will be generated from this function or not really generated but turned into a format in which we can return. All right, so that's all we can write right now for

the story file until we complete the story generation via LLM. So what I'm going to do now is go to job. This is

way simpler. Don't worry, we get a bit of a break with this file. Then we're

going to do the story generation and then the back end's pretty much done.

And I can show you how we test all of it as well. So, we're going to import uyu

as well. So, we're going to import uyu ID here from job. We're going to import typing uh or sorry, from typing import

optional. Okay. And in case I didn't

optional. Okay. And in case I didn't mention this, we're inside of the job.py

file for our routers. Uh did I spell something wrong here? From typing. Yes,

import optional. That's why. Okay. Then we're

optional. That's why. Okay. Then we're

going to say from fast API import import the API router. We're going to

import depend the HTTP exception and cookie. Okay. Then we're going to

and cookie. Okay. Then we're going to say from SQL import the session. We're

going to say from backend. DB. database

import the get database. We're going to say from backend domodels job import the story job and from

backend.s schemas job import the story

backend.s schemas job import the story job response. We then are going to

job response. We then are going to define our router similar to before. So

this is going to be for the jobs this time. So we're going to say define API

time. So we're going to say define API router. We can specify the prefix and

router. We can specify the prefix and the tags with slash jobs. Okay. Sorry,

quick pause here. Let's come down here.

I want to write the endpoint that we need. So we're going to say at

need. So we're going to say at router.get.

router.get.

This is going to be slash job id. The

response model, okay, is going to be equal to the story job response. And

this is going to get us the status of a current job. So we're going to say get

current job. So we're going to say get job status as the function. We're going

to say the job ID is a string. and we're

going to say the DB is a session that depends on getting the database function. Okay. And then all we're going

function. Okay. And then all we're going to do is we're going to query the job.

Uh and then we are going to return that.

So it actually is giving me all this but I don't want to use the autocomplete too much. So we're going to say db is equal

much. So we're going to say db is equal to query story job dot filter story job dot job id is equal to the job ID the

user is asking about. And then we're going to get the first value. Okay,

we're going to say if not job then we're going to raise a HTTP exception with status code 404 job not found. Otherwise

we can just return the job. Okay, which

is going to be based on this schema. Now

actually a few of the things I imported here I don't need because I was going to be writing something that I realized we don't need in this function. So this one is quite simple just getting the job ID or sorry getting the job status based on

the job ID. Okay, so now we have our job and we have our story routers. What we

need to do is we need to connect the routers to the main part of our application. So we're going to go to

application. So we're going to go to main.py here. We're going to import the

main.py here. We're going to import the routers that we wrote and we're going to include them in our application because right now they're not actually connected. So we're going to run this

connected. So we're going to run this file, but we need to connect the routers to this. We're going to say from routers

to this. We're going to say from routers and then we're going to import story and job. Okay. Then we're going to go down

job. Okay. Then we're going to go down here after the middleware and we're going to say at app or app dotincclude router and we're going to include the story.outer

story.outer and we're going to say the prefix is equal to settings prefix and then we can copy this and we can do the same thing for the job

router. Okay, so now our endpoints

router. Okay, so now our endpoints should be set up and connected. Now,

they're not all going to work right now, obviously, because we haven't actually generated the story, but we can still test this if we want. So, if we open up our terminal, we can run the backend.

So, we go cd backend uvun main. py.

Okay, let's see if we get any errors.

Uh, this is no module named backend.

Okay, so let me just have a look at why we're getting this and I'll be right back. Okay, so I was getting an error

back. Okay, so I was getting an error here that was essentially saying that this backend cannot be resolved. Uh, and

that's because it needs to just be this DB type imports like from DB.database.

We need to remove all these backend prefixes to our imports. However,

PyCharm is giving us a problem here because of the way that it's indexing the project. It thinks that we need

the project. It thinks that we need backend. So, we have this conflicting

backend. So, we have this conflicting thing where PyCharm is telling us that we need backend, but when we actually run the application, we don't. So, what

I'm going to do is I'm just going to fix this by opening up this folder directly in PyCharm. So, I'm going to go here.

in PyCharm. So, I'm going to go here.

I'm going to go file open and I'm just going to open backend directly. Okay. In

this window and then I'm going to fix some of the imports. And I believe that this should now work. Yes. So now when I open this directly, if I change this to just be core, right? So I remove all

those backend prefixes, we're good to go because now Python treats this as or PyCharm sorry treats this as the root of my project. Later we can go back and we

my project. Later we can go back and we can still write the front-end directory but for now let me reconfigure the interpreter. So I'm just going to select

interpreter. So I'm just going to select the interpreter here. Okay, so we're good to go. And let's just fix up those imports. So pretty much anywhere where

imports. So pretty much anywhere where you see backend, you can just remove the prefix there. Okay, so we'll do the same

prefix there. Okay, so we'll do the same thing here. Remove that. Go to routers.

thing here. Remove that. Go to routers.

Same thing here. Remove all of these.

And then we should be good to go. And

I'm going to remove that for right now.

Story. Okay, got a lot of backends here.

So let's get rid of all of those. Clean

this up. And this is just be part of coding, guys. Errors happen. I like to

coding, guys. Errors happen. I like to include it in the video so that u kind of everyone can see that I'm not perfect, that I do make mistakes. And

let's just kind of scroll through here and make sure that we don't have any more errors. Okay, looks okay. And then

more errors. Okay, looks okay. And then

lastly, let's go to main.py

imports. I think we should be good.

Okay, so now assuming that I fixed all of those, I think I did. I'm going to go uv run main.py directly from my backend directory. Okay. And you can see now

directory. Okay. And you can see now that the server is running. So if we go back to our browser now that this is running, let me open up the correct tab here. And we go to localhost

here. And we go to localhost 8000/doccks. You can see that we now

8000/doccks. You can see that we now have all of the API stuff showing up. So

you can see we have this post request that we can test if we want. We have

this get request and we have this get request here for the job ID. Okay. So if

we wanted to create a story, we can post something. So we can change this. So the

something. So we can change this. So the

theme, let's try it out. go try out uh session ID actually we don't need to pass anything for that but for the theme we can go with you know Dubai or something where I currently live and go

execute and then you can see here that we got an internal server error okay so let's test to see why we're getting that error uh but this is how you test the API at least one way right so you can go

here can go try it out again pass something in here and let's look at the error that we got okay so good error to run into actually something that I forgot to do it says no such table story jobs. The reason why we're getting that

jobs. The reason why we're getting that is we forgot to create the tables. So,

we need to actually just import something that will create the tables for us. So, what we're going to do is

for us. So, what we're going to do is we're going to bring in one more thing here. We're going to say from DB

here. We're going to say from DB database. Okay. Import. And we're going

database. Okay. Import. And we're going to import the create tables. And then

we're going to create the tables before the application runs to make sure that they actually get added. So now we're going to have to shut down the application and restart it. Okay. And

now if we go back here, let's refresh.

Okay. And let's try this again. So we'll

go try it out. Change this to some theme, Dubai, whatever. Press on

execute. And we got another internal server error. So let's debug this

server error. So let's debug this together. Okay, so it says we had one

together. Okay, so it says we had one validation error. I actually know

validation error. I actually know already where this is coming from, but it's saying the job ID needs to be a valid integer. Unable to parse a string.

valid integer. Unable to parse a string.

So we can kind of scroll through and see where we were getting this from, but I believe it's because we didn't actually properly handle the um generation yet of the stories. So if we go to routers

the stories. So if we go to routers job.py or sorry, story.py and in here,

job.py or sorry, story.py and in here, let's see where we're returning the job ID. So we're returning job here. It says

ID. So we're returning job here. It says

job ID is equal to job ID, which is a string. Uh but it's saying that this

string. Uh but it's saying that this needs to be an int. So that would tell me that we probably have an error here in our schema, the story job response.

So if we go to the story job response which I believe is here. Yes, you can see that we've specified the job ID as an int when actually this should be a string. So we just need to change this

string. So we just need to change this to be a string. Okay. So if we just go to schemas, this is the only fix. I'm

just debugging it live with you. If you

go to schema/job.py

and you change int to be string, then you should be good. Again guys, I apologize about the errors, but I like to just make this as realistic as possible for you so you can see that yes, you know, I make mistakes, too.

Okay, so let's go back here. Let's

refresh and let's try this now. So we'll

try it out and we can just pass this theme. That's fine. Execute. And now you

theme. That's fine. Execute. And now you can see that we actually get a job back.

So it says here's the job ID. The status

is pending. It was created at this and this is the information. Now let's take our job ID and let's go and try some of the other endpoints. So let's close this one and let's try to get the job. So I'm

going to go try it out. I'm going to paste in the job ID and hit execute. And

you can see here now that it says, okay, we have a job that was completed. It was

created at this time. Um, what is it?

Completed at null. So, that's something we got to look into because I think we should have that. And it says story ID is one. Okay. So, for some reason, we

is one. Okay. So, for some reason, we weren't getting that completed at result. So, I just went into this job.

result. So, I just went into this job.

py model file. And I noticed that we accidentally had completed job rather than completed at. So, I'm just changed this to completed at so that now we actually have a field that we're storing in our database so that we're able to

modify it and return it. So the problem is just that we had this uh this field misnamed. So that was giving us the

misnamed. So that was giving us the issue for the job right for the completed at field here. Okay. So I

think that's all good. Um that's pretty much it. Now what we need to do that

much it. Now what we need to do that we've kind of tested the APIs. I'm going

to shut this down and I need to handle the story generation. So we need to actually use an LLM to generate the story because of course this is no good unless we actually have some story to generate. So what I want to do is I'm

generate. So what I want to do is I'm going to start by going to prompts. py.

I'm going to copy in a few prompts that we need here, which you can find from the link in the description, so you can kind of see what it is that we're going to be doing and then we will start doing the LLM calls. So, I'm going to paste

this in here and let's start by looking at our system prompt. Okay, let me just make this a bit smaller so we can see this. Okay, so pretty much what we're

this. Okay, so pretty much what we're going to do is we're going to tell the LLM, you're a creative storyw writer that creates engaging choose your own adventure stories. Generate a complete

adventure stories. Generate a complete branching story with multiple paths and endings in the JSON format that I'll specify. Now, we're going to have a

specify. Now, we're going to have a compelling title, a starting situation, so root node. Each option should lead to another node with its own options, and some paths should lead to endings, both winning and losing. At least one path

should lead to a winning ending. Each

story requirement, each node should have two to three options. The story should be 3 to four levels deep, including the root node and a variety in the path length. Now, if you want the story to be

length. Now, if you want the story to be longer, you can change this to be like, you know, five to six. But the bigger that you make this, the more expensive it's going to be to run the LLM call.

So, that's why I'm going with 3 to four for now. Going to say add a variety in

for now. Going to say add a variety in the path lengths, some and earlier, some later. Make sure there's at least one

later. Make sure there's at least one winning path and output your story in this exact JSON structure, which we're going to specify in a second. Now, the

JSON structure will look like this.

Okay, so we're going to ask it to generate us that something that has this kind of data model, right? We have a root node, have all the information, have the options, we have the next nodes, etc. And then we're going to

parse in that information and store it in our database and return the story to the user. So again, you can copy this

the user. So again, you can copy this from the link in the description. You

can go to GitHub, you can find this prompts. py file and you can bring it

prompts. py file and you can bring it into your program. Okay, so now we're going to go to story generator. py. And

from story generator, we're going to start writing a very very simple AI model or not AI model, but we're going to call an AI model that will allow us

to generate this. So let's begin. We're

going to say from SQL alchemy import session. We're then going to say

import session. We're then going to say from and we can actually just go with I think core dot uh config import

settings. Okay. And we're going to say

settings. Okay. And we're going to say from langchain openai import chat open aai. We're going

to say from langchain_core.prompts

import the prompt template. And from

langchain_core output parsers import the no not that the paidantic output parser.

Essentially, what we're going to be able to do is we're going to take a uh string response coming from our LLM and kind of pipe it into a Python class uh which is going to be very useful for us to use um

what is it in order to get all of the data that the LLM gives us. We're then

going to say from core.prompts

import the story prompt and then we're going to say from models.tstory

models.tstory import story. So we can save the story

import story. So we can save the story and then we're going to go quickly over here to models. py within core. So in

core we have this new file models. py

and we're going to define a few pyantic models that are very simple that we need for loading in our LLM data. Okay. So

bear with me here. We're going to say from typing okay import

list dictionary any and optional. We're

going to say from paidantic import the base model and the field. And then we're going to start writing our classes. So

we're going to say class story option llm is base model. Okay. and we're going to say text

string is equal to a field and we're going to say the description is equal to the text of the option

shown to the user. Now the reason why I'm writing this is that we're essentially going to create this very detailed class that we want the LLM to give us the data in. So, we're going to

specify all of the fields that we want to be included in the data that's comes back from the LLM by writing all of these descriptions. And then the LLM

these descriptions. And then the LLM will know how to populate this so that we get the correct data. Okay, we're

then going to say next node is a dictionary that is a string key and any value and this is going to be a field

which is a description. and we're going to say the next node content and its options. Okay, so that's the first part.

options. Okay, so that's the first part.

We're going to use that within our next classes in a second. We're then going to say class story node llm inherit from the base model and we're going to say content and then this is going to be a

string and this is going to be field and this is going to say description is equal to and this is going to be the

main content of the story node. We're

then going to say is ending this will be a boolean which is equal to a field that we will describe as the following. We're

going to say description and this is going to be whether this node is an ending node. We're then going to say is

ending node. We're then going to say is winning ending boolean is equal to field

description. Okay. And this is going to

description. Okay. And this is going to be equal to whether this node is a winning ending node. Okay. And then

we're going to have options. And this is going to be optional because some nodes don't have any options if they're ending nodes. And this is going to be a list of

nodes. And this is going to be a list of the story option LLM, which is equal to a field with a default equal to none and

a description that's going to say the options for this node. Okay, let's make this smaller so we can read all of it.

Lastly, we're almost done. We're going

to have a class which is the story llm response. Okay, this is going to be a

response. Okay, this is going to be a base model and we're going to say the title is a string equal to a field with the description which is going to be

equal to the title of the story. Okay. Okay. And then we're

the story. Okay. Okay. And then we're going to have root node. And this is going to be a story node llm. And this is going to be a

node llm. And this is going to be a field with a description. Okay. That is going to say the root

node of the story. Okay. So essentially

what we're doing is we're mapping out exactly what we want a story to look like within this Python class structure so that we can pass it to the LLM. Okay.

Okay, so let's go back to our story generator and inside of here we can start writing the class that's going to generate the story. So we're going to say class story generator and we're

going to define a class method. So we're

going to say class method. Okay, this is going to be define_get llm cls. Now the reason why I'm writing

llm cls. Now the reason why I'm writing a class method is this is not going to be specific to the instance. In fact,

nothing here is going to be specific to the instance. We're just writing a class

the instance. We're just writing a class so that um we can kind of organize some of the functions that we have for our story generator. Now, when you write

story generator. Now, when you write something with an underscore, this means it is a private method. So, it should only be called internally from the class. Um it's just a convention in

class. Um it's just a convention in Python. Don't worry too much about it if

Python. Don't worry too much about it if you haven't seen it before. Now, what

we're going to do is we're going to return chat open AI. Okay? And the model we're going to

AI. Okay? And the model we're going to use is going to be GPT for Turbo. Of

course, you can use any model that you want. Now, in order for this to work, we

want. Now, in order for this to work, we are going to need our OpenAI LLM key, but I'm going to get that in a second.

And if you already know to get it, you can just put it inside of your environment variable file. And if you have it inside of here, then you are good to go. Okay, so that's our first class method. The next class method is

class method. The next class method is going to be one that actually generates a story. So, we're going to say define

a story. So, we're going to say define generate story. This time it will not be

generate story. This time it will not be private. We'll take in the class. We'll

private. We'll take in the class. We'll

take in a database which is a session.

The session ID which is a string. The

theme which is a string. And by default we'll just make it equal to fantasy or something. And then we are going to

something. And then we are going to return a story.

Okay. So from here we're going to generate the story. So we're going to get our LLM. So we're going to say llm is equal to cls.get

llm. We need to use the underscore.

Okay. Okay, so we get our llm. Uh, what

is the problem here? It's we have two colons. Okay, let's get rid of that.

colons. Okay, let's get rid of that.

We're then going to say our story parser is equal to the pi dantic output parser and the pi dantic object is going to be

the story llm response. Okay, now we need to import this. So we're going to say from models. Okay, so actually from

this is going to be core dot models import the story llm response. Okay. And we're

also going to bring in the story node llm.

Great. So that's going to make an output parser which we're going to use with our llm in a second. And we're now going to specify our prompt. So we're going to say our prompt is equal to let's put

this on a new line. A prompt template.

Now actually this needs to be a chat prompt template. So let's change the

prompt template. So let's change the import up here. And let's change the value down here to chat prompt template.

And from here we're going to say from messages and we're going to pass in here a list of messages. Now when you pass a message you need to specify the role. So

we're going to say this is a system message and then we're going to pass our story prompt. Okay. So this needs to be

story prompt. Okay. So this needs to be all capitals story prompt. So the first message is a system message that says this right large system prompt. Okay.

Now the second message is going to be the user message. All right. So, we're

going to say here uh actually human and we're going to put an fstring and we're going to say create the story with this theme. Okay.

And then we're going to specify the theme. Okay. So, that's it for the

theme. Okay. So, that's it for the prompt. And then here we're going to say

prompt. And then here we're going to say partial and we're going to say the format instructions are equal to the story parser dot format

instructions. And this is actually sorry

instructions. And this is actually sorry get format instructions. So when we make this pyantic output parser we pass a pyantic model to it. So story lm response we then can use this fancy

function called get format instructions which will give us a string that will specify exactly what it needs to look like when the LLM generates the response. So what we're saying here is

response. So what we're saying here is okay we're going to embed the format instructions which are these. The format

instructions we specified in this variable right here in the prompt. So we

will get the format instructions from our model pass it here inside of the prompt and then we also pass the theme and say hey create a story with you know this given theme and then it will do that for us when we invoke the LLM. So

this is our prompt but we need to send this to the LLM. So to do that we say raw response is equal to llm and actually we're going to say llm

invoke and we're going to invoke the prompt.invoke invoke and when we prompt

prompt.invoke invoke and when we prompt uh when we invoke the prompt sorry we're just going to pass an empty dictionary okay this is because you could pass additional variables to the prompt template if you want to in this case we

don't need to so what we're doing is we're saying we're going to invoke the llm with this particular prompt and when we just do invoke it just generates the full prompt by actually passing in the format instructions then we're going to

say the following we're going to say response text is equal to raw response okay And we're going to say that if has

adder which means has attribute and we're going to say the raw response and then content then we're going to say the response text is equal to the raw response content. Okay, so we're going

response content. Okay, so we're going to strip out the text content. If that's

the case then we're going to say the story structure is equal to and this is going to be the story parser.parse

parse and then the response [Applause] text. Now, this is just going to confirm

text. Now, this is just going to confirm that this actually is in the correct format and parse it into the Python object that we want to use. So, it's

going to give us a uh kind of returned version of what do you call it? This the

story LLM response that we'll be able to actually use to access the values. Then,

we're going to say story database is equal to story. We're going to say title is the story structure.title

and the session ID is the session ID.

We're then going to say db.add

the story db and we're going to say db.flush. Now what flush is going to do

db.flush. Now what flush is going to do is it's just going to update this story database object with all of the automatic populated fields like the ID of the story for example the created at time. So we can continue to use the

time. So we can continue to use the story later down here as we start adding some more values to it. So now we're going to say the root node data is equal

to the story structure dot uh root node like that. Okay. Now let's go back here.

like that. Okay. Now let's go back here.

We need the root node and we're going to have to actually process some data here to put it into the correct format which you're going to see in one second. So

we're going to say if is instance and actually we don't need parenthesis here. We're going to say if

parenthesis here. We're going to say if is instance the root node data and this is going to

be dictionary. Then what we need to do

be dictionary. Then what we need to do is say the root node data and is equal to the story node. Okay, llm. And then

what we're going to do is say dot model_v validate the root node data. All

right. Now, I know this a lot of pyantic code, but essentially what we're doing is we're saying, all right, let's get the root node because remember, if we look at our models, we're going to return a root node, which is the start of our story, which will have all of the

options here. We're going to just say,

options here. We're going to just say, all right, if this is a dictionary, we want to validate that it is actually in the correct format of our root node. So

if we go back here, we can see that a node should look like this, right? Story

node LLM should look like this. So we're

going to say, all right, let's make sure it's in that correct format. And if it's not, then this will raise an error.

Okay? Otherwise, we'll be able to continue moving forward. So from here, we're going to say process data, which we're going to do in one second. So

we'll say to do this, and then we're going to say dbomit, and we're going to return our story database object. Now what I'm about to

database object. Now what I'm about to write is one last function here which is going to go through all of the data returned by our LLM and convert it into the correct Python data tape data type

sorry that we can store in our database because by default it's going to return us JSON. So the model's going to return

us JSON. So the model's going to return JSON. So what we're doing is we're

JSON. So what we're doing is we're essentially enforcing that the JSON returned by the model is correct. We're

then putting it into the correct Python object so we can store it in our database. Okay. So, we're going to put

database. Okay. So, we're going to put another function down here. This is

going to be a class method. And we're

going to say define, and this is going to be underscore process_tory node. Okay, we're going to take in the

node. Okay, we're going to take in the class. We're going to take in the

class. We're going to take in the database, which is a session, and we're going to take in the story ID, which is an int. We're going to take in the node

an int. We're going to take in the node data, which is story node llm, and we're going to take in is root. Okay, which is

a boolean which by default is equal to false. Again, let me close this so we

false. Again, let me close this so we can read it. All right, this is what our function looks like right now. And what

is this going to return? Well, this is going to return a story node. Okay, so

we're taking in the class database session story ID, the node data, and if this is a root node or not because we're actually going to recursively call this function in a second, which you will see. Now, story node is something that

see. Now, story node is something that we need to import because we don't have it imported. So, where we have

it imported. So, where we have models.story story. We'll also import

models.story story. We'll also import the story node up here. Okay, let's come back down. What we're going to do now is

back down. What we're going to do now is we're going to start processing our nodes. So, we're going to say node

nodes. So, we're going to say node is equal to story node. All right. And

what we're doing is we're taking the story node llm response which is different from the story node that we have in our database. So, we're now going to say story ID is equal to story

ID. We're going to say content is equal

ID. We're going to say content is equal to node data.content. content. Sorry. If

the has adder again setting for has attribute of node data content otherwise it's going to be node data content. The reason why we're doing this

content. The reason why we're doing this is we're not sure if we're going to be able to access the content attribute or if we're going to have to access content from the JSON dictionary that's returned. So we're just doing kind of a

returned. So we're just doing kind of a safety check here uh to make sure that it is uh loaded in correctly. Okay.

Okay, so we have content, we have is root. So is root is equal to is root

root. So is root is equal to is root which is passed here. We then are going to have is ending.

Okay, this is going to be the same as this. So it's going to be node data is

this. So it's going to be node data is ending if has adder node data is ending otherwise node data is ending. Okay,

same thing. And then we're going to have is_winning ending. And this is going to be equal to

ending. And this is going to be equal to the same thing. No data is winning ending. If has adder, no data is winning

ending. If has adder, no data is winning ending. Else no data is winning ending.

ending. Else no data is winning ending.

Okay, lot of this similar code, but that's almost all. And then we're going to have options is equal to an empty list which we are going to populate. So

again, because we have this JSON structure that's being returned to us, we need to convert it into the correct Python objects, which requires a little bit of processing, which is what we're doing here. So now we're going to add

doing here. So now we're going to add this node to the database. So uh db.add

node and db.flush.

So what will happen is here we're going to take the root node. We're going to pass it to this function. We're going to process the root node. We're going to add the root nodes to the database. And

then we're going to look at the children of this root node. We're going to add those to the database. We're going to look at the children of those root nodes. We're going to add those to the

nodes. We're going to add those to the database. And we're going to continue

database. And we're going to continue that recursively. So we can actually go

that recursively. So we can actually go ahead and call the function here. We can

say cls.c_process

story node. We can pass the database the story dbid the root node data and is root is equal to true only this time for this first node. Okay. Then what we're going to do

node. Okay. Then what we're going to do is the following. We're going to go down here and we're going to say if not and

this is at node is ending and we're going to go in parenthesis has attribute. So has adder node data

attribute. So has adder node data options. So if the node is not an adding

options. So if the node is not an adding node and it does have options and the node data.options

node data.options are not empty. Okay, that's what we're checking. We're saying, are you not an

checking. We're saying, are you not an ending node? If you're not an ending

ending node? If you're not an ending node and you have some options that are not empty, then we need to process those nodes. So, what we'll do is we'll say

nodes. So, what we'll do is we'll say options list is equal to a list. We're

going to look at all the options that were returned from the LLM and we're going to convert them into a node that we can store in our database. So, we're

going to say for option data in node data dot options. Okay. Then we're going to

dot options. Okay. Then we're going to say next node is equal to option data dot next node. So for each option we

have this next node uh property which is telling us okay this is the next node that's coming up. So we're going to look at that and we're going to say if is instance all right and this is going to

be next node dictionary. So if it's a dictionary then what we're going to do is validate that it's correct. So we're

going to say next node is equal to story node llmodel validate. So same thing that we did

validate. So same thing that we did before of the next node. This needs to be is instance not in instance. Okay. So

we validate that it's correct. And then

we're going to say the child node is equal to cls.process

node. We're going to pass our database our story ID our next node and false.

Okay. For isroot.

So what's happening here is we're looking at our node, right? So we're

getting the root node to start. The root

node will have some options. Again, the

options are not already in the correct format. So what we're doing is we're

format. So what we're doing is we're saying, all right, let's look at all these options if there are any. We're

going to say, let's loop through all the options that were present on the node data that we passed in, not this node object that we created in the database.

Then we're checking, all right, is this next node actually valid for us to process? If it is, then we're going to

process? If it is, then we're going to process it the exact same way that we process the root node. So we'll come back and we'll repeat this function.

This is called doing this recursively.

This time it will not be the root node.

Again, we'll check to make sure it has options. If it has options, we'll

options. If it has options, we'll process all of those. And as soon as there's no options, right? So we've

reached those ending paths. Then we'll

stop processing. Okay, so we have the child node. We're then going to say the

child node. We're then going to say the options_list.append.

options_list.append.

And we're going to append the following.

We're going to have text, which is the We need to do this in a string, sorry, because it's Python. We're going to have text, which is the option data.ext,

and we're going to have the node ID, which is the child node ID. All right,

we're then going to come out of this for loop, and we're going to say node.options

node.options is equal to the options list. Okay. So,

what this is going to do is it's going to kind of create this mapping structure where for every single node, rather than storing all of the node objects in there, we're going to store a simplified

version where we have the text of what that option is and we have the ID of that next node so that we can always look up what the next node is, but we don't need to store all of the node data

in this kind of binary tree structure that would be created otherwise. I know

this is a little bit confusing, but what's happening is we have this root node. We then are essentially just

node. We then are essentially just populating the options of the root node.

For that, we're going to have the options looking like this, where each option includes the text of the next option and the node ID for that particular option. In our game, if we

particular option. In our game, if we decide to press on the next option, for example, we can load that next node and then we can do the same thing from there. we can look at the options of

there. we can look at the options of that node, see the text and see the node ID. Okay. And this creates a simplified

ID. Okay. And this creates a simplified way for us storing this in the database without having to store too much data at once. This is probably the most complex

once. This is probably the most complex part of this application is this kind of binary tree structure. But that's the whole point of me doing this is I wanted to make this more complex than something just like a super simple node app,

right? Okay. So, we're going to go

right? Okay. So, we're going to go dbflush down here. Notice this is outside of the

down here. Notice this is outside of the if statement. So after we've populated

if statement. So after we've populated all of the options, we're going to flush the database and we're going to return this particular node. Okay, so that's how this works. And this kind of

recursive call stack will uh go through and make sure that all of the children nodes are processed correctly. So that

is pretty much it. That's all we need for this function here. And now we just need to make sure that we get our OpenAI API key. So let's populate that in our

API key. So let's populate that in our environment variable file. So to do that, we're going to open up our browser. Let me open the correct one

browser. Let me open the correct one here. And we're going to go to

here. And we're going to go to platform.openai.

platform.openai.

Okay.

Uh API keys. All right. I'll leave this link in the description. Make sure that you're signed into OpenAI. I'm going to generate a new secret key here. I'm

going to call this adventure game. Okay. Let's copy that.

adventure game. Okay. Let's copy that.

Obviously, don't leak this with anyone.

And we're going to paste it here. Now,

in order to use this OpenAI key, of course, you need an account on the OpenAI platform and you are going to need a credit card on there because this will cost you a little bit of money.

It'll be like a few cents, especially if you hardly use it. It might not even cost you a scent, but if you want to use these LLMs, you know, they do cost money unless you run them locally on your own computer. Okay, so we have the story

computer. Okay, so we have the story generator. Uh, we now have everything

generator. Uh, we now have everything hooked up and we pretty much just need to make sure that we now call this function from our router. So if we go to our router story. py, what we're going

to do here inside of story. py is we're going to replace this kind of empty story that we had. So right here with the call to that story generator. So

we're going to go to the top of our program. We're going to say from

program. We're going to say from core.story generator import the story

core.story generator import the story generator like that. We're going to come down here and we're going to say story is equal to story generator

dot generate story. and we're just going to pass the theme. Okay. Then for the story ID, we're going to say this is story do ID. So we're actually using this value now. And then we will know

the story ID for this particular job.

And then we can then look at that story from our um what do you call it? API. So

that should actually be it. Uh that

should finish our backend unless I miss something which to be honest I probably did. And yes, there we go. We missed

did. And yes, there we go. We missed

this function. Build the complete story tree. However, before we do that, we're

tree. However, before we do that, we're quickly going to test the current state of the API. Then we'll write this. Then

the back end will be finished. Then we

can move on to the front end which is significantly simpler. Then we can do

significantly simpler. Then we can do the deployment and then we are good to go. All right. So let's run the API.

go. All right. So let's run the API.

Let's go to the back end and let's go uvun main.py.

uvun main.py.

Let's see if we got any errors. Okay.

Looks like it's loaded up here. Let's go

back to the browser to our documentation and refresh. And let's try to create a

and refresh. And let's try to create a new story. Okay. So from here we're

new story. Okay. So from here we're going to go try it out. Let's put a theme. Let's go like I don't know water

theme. Let's go like I don't know water park or something.

Okay. Actually, is water park one word?

I don't know. Two word. Water park.

Let's go with that. Let's go execute. Uh

and internal server error. Okay. So,

let's see what happened here. Uh we got a problem saying the table story jobs has no column completed at. Okay. Uh

interesting. Let's look at why we are missing that. Okay. So, the reason why

missing that. Okay. So, the reason why we're getting this is because we had changed this but we didn't delete the old database. So, what we're going to do

old database. So, what we're going to do is we're just going to go to our database.db here. Uh, we're going to

database.db here. Uh, we're going to just first turn off the server and we're going to just delete it. When we delete this, so just go ahead and get rid of that. Uh, now when we rerun the code,

that. Uh, now when we rerun the code, it'll make a new database and then it should have that column and then we should be good to go. So, let's go uvun main.py. It should now create that new

main.py. It should now create that new table for us. Okay, looks like it did.

We can now go here, refresh, and try it again. So, we're going to go try it out.

again. So, we're going to go try it out.

Let's go water park and execute. And

there we go. We get our job status.

Okay, so it says job ID is this. So now

we can actually uh test this out. So

let's go to job and let's check the job status.

Okay, so execute and it says completed at this time and it gave us an error.

Story generator missing one required positional argument. Uh can I open this

positional argument. Uh can I open this up? Let's make it a bit bigger. Session

up? Let's make it a bit bigger. Session

ID. Okay, so let's go fix that. We need

to go to story generator. So where do we call the story generator? We called that from story.py.

from story.py.

And right here, generate story. This

requires the database, the session ID, and the theme. So we're going to pass DB, session ID, and theme. And DB is right

here. Okay. So that should be good to go

here. Okay. So that should be good to go now. So, let's go back and let's try

now. So, let's go back and let's try this again. So, we're going to have to

this again. So, we're going to have to go to make a new story. So, refresh. All

right. We're going to go create try water park.

Okay. Execute. And we get our job ID.

And let's now go to look up the job status. Okay. So, try this. Paste this

status. Okay. So, try this. Paste this

in here. and the API key client option must be set either by passing the API key to the client or setting open AI API key environment variable. Okay, so it just looks like we're missing the open AI key. So let me look at why that's not

AI key. So let me look at why that's not loading. I think I already have an idea.

loading. I think I already have an idea.

We're going to close all of this. So

close all tabs. Going to go to core story generator. And I mean I know we

story generator. And I mean I know we have it from settings. So we might be able to just pass the API key directly here. But I think another way that we

here. But I think another way that we can do this is the following. We can

just go from enenv import load.env and

then we can just call the load.env

function and we can remove this line up here where we're importing settings.

This should load the environment variable file for this particular Python script and then give it uh give us access to that environment variable. So

let's see if that works now. So let's

shut down the program and restart. You

are going to need to shut it down in order to test this. We'll go back and we'll try again. Again, just debugging slowly with you guys. Okay, so let's go

here to create. Let's go to try it out.

Let's fix this to say watermark and execute. Okay, we've got our job ID.

and execute. Okay, we've got our job ID.

So, let's copy that and let's go here to job ID. Try it out. Paste it in.

job ID. Try it out. Paste it in.

Execute. And it is processing. Okay, so

this is good. So, it's still processing.

That's what we actually want to see.

It's going to take a second to process.

So what we can do is we can just keep executing here and we can just wait until eventually it says it's not processing. Now this is what's referred

processing. Now this is what's referred to as polling where you essentially pull or you keep hitting this endpoint until it gives you a particular response that you're looking for like now where it

says okay it was created at it's completed the story ID is one and this is the time that it finished at and you can see if you look at the time delta that it took like 20 seconds to finish

this. So now I can get the story ID

this. So now I can get the story ID because now it's done. And I can go just there's so many things open. I want to close these so it's a little bit easier

to see. Let's close that. We can go to

to see. Let's close that. We can go to stories. We can put in the story ID of

stories. We can put in the story ID of one and execute. And we get an error now because of the way that we're returning the story, which I'm going to fix in one second. But in a minute, this will

second. But in a minute, this will actually return us the entire story and we'll be able to view it. So now let's go back here. And you can see it says input should be a valid dictionary or object to extract. Okay, I know why

we're getting that. Again, we're going to fix that right now. Okay, so let's go now to that function I promised we would write inside of routers story.py.

So we have this build complete story tree, right? For right now, it doesn't

tree, right? For right now, it doesn't return anything, which is why we're getting that error. So now we're going to need to actually write the function out which will return the correct tree.

So what we're going to do is we're going to say nodes are equal to db doquery story node dot filter.

Okay. And we're going to say the story node dot story id is equal to the story id. And then we're going to get all the

id. And then we're going to get all the nodes. So rather than first we're going

nodes. So rather than first we're going to get all and sorry this should be story do ID. Okay. So we reference this.

Now this is going to look at all of the nodes that we created in our database from the story generator. It's going to get all the ones that reference this particular story. And then we're going

particular story. And then we're going to build this kind of tree that we can return to the front end. So we're going to say node dictionary is equal to an

empty dict. We're going to say for

empty dict. We're going to say for node in nodes and we're going to say the node I don't know why that's popping up.

The node response is equal to the complete story node response. Okay.

Inside of here we're going to populate the values. So ID is equal to node do

the values. So ID is equal to node do ID. We're going to say content is equal

ID. We're going to say content is equal to node dot content. We're going to say is ending is equal to node is ending.

We're going to say is underscore.

Ending is equal to node isisc_inning ending. We're going to say options is

ending. We're going to say options is equal to node dot options. Okay, so

we're just kind of wrapping this node information in this complete story node response that we want. So we're omitting anything that we don't need and we're just including the few values that we do actually want to return. We're then

going to say the node dictionary at node ID is equal to this node response. Okay,

so we're just again wrapping it in the correct response format. We're then

going to say the root node is equal to next and this is going to be node for

node in nodes if node is_root.

Okay, otherwise none. Now what this is going to do and sorry we just need to put parenthesis around this. Okay, so

essentially what we're doing here is we're looking at all of the nodes and we're just searching for a node that is the root node. So this is kind of a fancy way of doing it, but we're saying, okay, we want to get every node that's

in our nodes, but only if that node is a root node. So we're only going to get

root node. So we're only going to get one node ideally, and then that's going to give us a list. And then we just call next. When we call next, that's going to

next. When we call next, that's going to give us the only value that's inside of here, which is the root node. And then

we're going to return that as the root node. If for some reason there is no

node. If for some reason there is no node that's the root node, then we just are going to get none. Okay, that's what the next function does. Just grabs the next value from an iterable object. And

I know it's kind of fancy syntax, but but that's what we're going to use. Now,

we're going to say if not root node, then we're going to raise an HTTP exception and say 404 and then uh story has no root node or story root node not

found. And actually, we'll make it 500

found. And actually, we'll make it 500 because that means there's some error going on. Okay. Now, we're going to

going on. Okay. Now, we're going to return the complete story response, not node, but story response where we say

the ID is the story. ID. The title is the story.title. This needs to be an

the story.title. This needs to be an equal sign.

Okay, we have the session ID equal to the session ID. And this is the story session

ID. We have created

ID. We have created equal to story dot created at. And then

we have the root node which is equal to the node dictionary at the root root node id. And then all underscore nodes

node id. And then all underscore nodes equal to the node dictionary. So what

we're able to do here is return this completed story response that contains all the information we need in order to access the data about a story. Notice

that we include all of the story nodes.

So that even though we just start at the root node, we have all of the node information. And then based on the

information. And then based on the options that the root node has, we're going to be able to navigate to those other nodes from our front end. Okay. So

that should allow us to return the whole story. So let's save. Let's make sure

story. So let's save. Let's make sure our uh API is still running. Looks like

it is. Okay. Let's go here and just refresh this. And then let's try this

refresh this. And then let's try this again. So let's go to uh actually try to

again. So let's go to uh actually try to get a story. Let's try to get story ID 1 and execute. And you can see now that it

and execute. And you can see now that it gives us the entire story. So sorry if I go here to scroll through notice that we

have the title session ID ID created at root node and then it shows us the options. Okay, in this case there's

options. Okay, in this case there's three options. So we have node ID 2,

three options. So we have node ID 2, node ID 7, node ID 10, and then we have all the nodes. So we can navigate through all of the nodes in our story uh and kind of go between them. Cool. So

hopefully that is clear and the backend now is completely finished. The next

step is to write the front end uh that will integrate with the back end and then to deploy this. So let's go ahead and set that up. All right. So let's

move on to the front end. I'm just going to close this uh here. Okay. Let's close

the back end. And actually what we're going to have to do now is I'm just going to have to open up the project again. So from the root directory. So

again. So from the root directory. So

let's do that in this window. And now we can open up the back end. So let's go new. Actually we don't need to make

new. Actually we don't need to make anything new because we're going to use a command. Okay. Sorry. So, let's open

a command. Okay. Sorry. So, let's open up the terminal. We're inside of our root directory and we're going to use npm to create a new React project. Now,

in order for this command to work, you do need Node.js installed on your system. So, make sure that you have that

system. So, make sure that you have that installed. So, we're going to type npm

installed. So, we're going to type npm create vit.

We're going to type front end dash- template react. Okay, this is the

template react. Okay, this is the command that is going to create the front-end directory for us. We're going

to cd into the front end. We're going to type npm install and then we are going to install one more library uh which is called Axios and then the front end will kind of be set up and we can start

writing some code. Now I promise you the front end is significantly simpler than the back end. It's just basic user interface stuff. So, a little bit of

interface stuff. So, a little bit of React code and a little bit of JavaScript code and it's just going to be five kind of components or simple files that we write. Okay, so now that that's installed, we're going to type

npm install Axios.

We're going to use this to simplify sending requests. So, let's install the

sending requests. So, let's install the Axios library there. Give this a second to load up. And actually, while that loads up, we can open up the front-end directory. So, in the front end

directory. So, in the front end directory here, you'll see that we have a src folder. Okay, that's done. So,

let's close this here. We're going to go into src and we'll just start cleaning a few things up. So from app.jsx, we can just remove everything that is being used inside of here because we really

don't need this. Okay, so let's remove that. And let's remove the logos because

that. And let's remove the logos because we're not going to use those. And let's

close the fragment here. Okay, we can also get rid of this use state which we don't need. Okay, so that's the app. Uh

don't need. Okay, so that's the app. Uh

we're going to go to assets. We can just delete the assets folder right here. So,

let's go. Where's the delete button?

Like that. Okay. And we're just going to make a new folder called components.

Okay. So, inside of src, we're going to go new folder components.

Okay. We now have the components folder.

And inside of the components folder, I'm just going to make five simple components. So, I'm going to go new

components. So, I'm going to go new file. I'm going to do one which is

file. I'm going to do one which is loading status.jsx.

loading status.jsx.

I'm going to do another one which is story generator.jsx.

story generator.jsx.

I'm going to do another one which is the story loader. Okay. JSX.

story loader. Okay. JSX.

Let's make this a bit bigger so we can see the names of the files. We're going

to do another one which is the story game.jsx.

game.jsx.

And then the last one is going to be so we have loader generator game status and we're going to have the theme input.js.

jsx. Okay, so those are our five components. Now, I also just remember

components. Now, I also just remember there's one more library we need to install and that is React Router DOM.

So, we're going to go back to our front end and we're going to type npm install react router DOM. Okay, now we need that because we're going to have a kind of

URL route that you can go to to view the different stories. All right, so we're

different stories. All right, so we're going to start writing some of the simpler components first. So, we can go to loading status, which will be quite a simple one. We're going to write a

simple one. We're going to write a function here. We're going to say

function here. We're going to say function loading status. We're going to take in here the theme. Okay. And we're

just going to return some very simple uh divs. So we're going to say div class name is equal to the class name can be the loading container. And by the way, for the styling, I'm just going to

give you all of the styles, which I'll show you how to get in one second. We're

going to say H2 and we're going to say generating your theme story like that. And let's spell generating correctly. And we can do a

generating correctly. And we can do a capital on your. Okay. So, we have an H2. We're going to have another div. For

H2. We're going to have another div. For

the div, we're going to have class name is equal to, and this is going to be the loading animation. And then we're going to have

animation. And then we're going to have another div inside of here with class name equal to the spinner. And then

we're going to have a simple paragraph tag. And we're going to say that the

tag. And we're going to say that the class name is equal to the loading info. Okay. And here we're just going to

info. Okay. And here we're just going to say please wait while we generate your story dot dot dot. Okay. Okay. Last

thing is we're just going to export this component. So, we're going to say export

component. So, we're going to say export default loading status and we should be good to go. Okay, so that's it for this component. Now, I just want to bring in

component. Now, I just want to bring in the CSS before I forget. So, rather than me writing all of the CSS in this video, I'm just going to get you guys to copy it from the code link that's in the description. So, the GitHub repository

description. So, the GitHub repository there, because there are quite a few styles, and it just really doesn't make sense to write hundreds of lines of CSS.

So, I always do this in my tutorials, but what I'm going to get us to do first is go to app.tcss CSS and just replace the content of that with what you find from the GitHub repository. So if you go to GitHub, right, which will be linked

in the description for all of the code, you'll find this app.css file in this exact same directory structure. Simply

copy app.tcss and just paste it in here.

Now, we're going to do the same thing for index.css. Okay, so we're going to

for index.css. Okay, so we're going to take what's in the index CSS on the GitHub and we're going to paste that in the index CSS here in our local code.

Okay, you can just copy it right from there. So now we have all of the styles.

there. So now we have all of the styles.

We finished the loading status component. So let's close that one and

component. So let's close that one and let's start writing a few other ones that we can use. So let's go to the theme input. Another relatively simple

theme input. Another relatively simple one. This is going to be the component

one. This is going to be the component to get the input uh for our theme.

Right? So we're going to say import use state from React. And we're going to go and make a functional component. So

we're going to say function theme input.

Okay. We're going to take in an onsubmit function that we can call. Uh, and let's do the function. We're going to say

const theme set theme is equal to use state. And we'll make this an empty

state. And we'll make this an empty string. And then we're going to have

string. And then we're going to have const and we're going to say error set error is equal to use state. And then

again make that an empty string. We're

then going to say const. And this is going to be handle submit. Okay. For

this, we're going to take in E. We're

going to say E.prevent

default, which is going to prevent the default form submission behavior. And

we're going to say if the themetrim.

Okay, so if sorry, not theme.trim,

which means if you didn't actually give us anything other than blank text, then we're going to set an error and say please enter a theme name. And then

we're simply going to return, okay, in lower cases. Otherwise, we're going to

lower cases. Otherwise, we're going to say set theme or sorry, not set theme.

We're going to say onsubmit and then we're going to pass to this function the theme. Okay, so this is our handle

theme. Okay, so this is our handle submit function that we'll use from inside of the form. And then we're just going to write the form and that's it for this component. So we're going to say return. We're going to have a simple

say return. We're going to have a simple div. We're going to say the class name

div. We're going to say the class name is equal to and this is going to be the theme input container. We're going to put an H2 tag

container. We're going to put an H2 tag and we're going to say generate your adventure.

We're going to put a paragraph tag and we're going to say enter a theme for your interactive story.

Okay. Did we spell interactive? There we

go. Then we're going to have a form.

We're going to say onsubmit is equal to our on uh not onsubmit our handle submit function. So handle submit like that.

function. So handle submit like that.

Then we're going to have a div inside of this form. So we're going to say div

this form. So we're going to say div class name is equal to and this is going to be the input dash group.

Then we're going to have a simple input tag. So we're going to say input and

tag. So we're going to say input and this is going to be type equals to text value is equal to the current theme.

We're going to say onchange is equal to and this is going to be e so it's a function and then we're going to say set theme e.target dov valueue. Sorry it's

theme e.target dov valueue. Sorry it's

kind of populating this so it's a little bit difficult to read. Let's go down.

We're going to say the placeholder is enter a theme and we can give some examples. So eg like you know pirates

examples. So eg like you know pirates space I don't know medieval or something. So

medieval like that dot dot dot. Okay. Okay. And then we're going to have class name equal to error question mark

error otherwise none. So essentially if there's an error then we'll kind of like highlight the box showing that there's an error otherwise we won't. So this is our input field. Pretty straightforward.

We're then just going to have a simple error message. So we're going to say if

error message. So we're going to say if there's an error then we're going to have a p tag that has the class name error not message but error text. And

we'll display what the error is. And

then lastly, underneath the div here, we're just going to have a button. Okay.

And we're going to say the type is equal to submit, we're going to say the class name is equal to the generate and then btn. So button and then we'll just do

btn. So button and then we'll just do generate story.

Okay. So that's going to complete our uh theme input. And then we just need to

theme input. And then we just need to export this. So from here we're going to

export this. So from here we're going to say export default theme input. All

right. So that's the theme input class.

We are getting close to we finished the front end. So now what we're going to do

front end. So now what we're going to do is we're going to write the story loader component. Okay. So we have loading

component. Okay. So we have loading status theme input. This right here is actually going to load a story for us and then display it on the page. Okay.

So we're going to say import and this is going to be use state from react and we need to make sure that we have this inside of braces. We're also

going to import use effect.

Okay. From down here, we're going to say import use params as well as use navigate from React.

We're going to import Axios. So, we're

going to say import Axios from Axios and we're going to import the loading status.

import loading status from the loading status component. We're then going to

status component. We're then going to define the API base URL. So we're going to say const API_base URL is going to be equal to slash API.

Okay, we're then going to say function and we're going to say story loader.

And what we're going to do here is we're going to attempt to load in a story. So

to load the story, we're going to get the ID from the URL. So what's going to happen is to access this page, we'll go to like our, you know, front end link, whatever slash one or slashstory one or

something. Uh so what we'll do is we'll

something. Uh so what we'll do is we'll grab the ID and we'll use that ID from the URL in the browser to load the correct story from the back end. So

we're going to say const id is equal to use params. Okay, we're then going to say const navigate. This will allow us to navigate

navigate. This will allow us to navigate to another page because we're going to set up react router in 1 second. We're

then going to say const story set story is equal to use state null because we need to know what story we actually have here. We're going to have const loading

here. We're going to have const loading and then say set loading equal to use state and by default this will be true because when we navigate to this page

we're going to be loading the story. And

then we're going to have const error and then set error equal to use state and then null by default. Okay. Now what

we're going to do is we're just going to write a function that will load the story. So we're going to say const load

story. So we're going to say const load story is equal to an async function that's going to take in the story ID and then attempt to load the story for us.

Okay. So to load the story, we're going to say set loading to true.

We're going to say uh what is it here?

Set error to null. So we'll refresh the error if there was any one. And then

we're going to try sending a request to our back end. So we're going to say try const response is equal to await axios.

Okay.

We are going to use back ticks. We're

going to embed the API base URL. So

we're going to say API_base URL. We're going to say slashstories

URL. We're going to say slashstories slash we're going to embed the story ID.

So story ID like that and then slashcomplete.

Okay, so let's make sure the slashcomplete is outside of that embed.

Again, we'll just close this so we can see this better. Okay, so this is what the response is going to be. We're

sending a get request to this URL to get the story. We're then going to say set

the story. We're then going to say set story and this is going to be response data and we're going to say set loading equal to false. We're then going to say

catch any error. So catch err. We're

going to say if the error dot response question mark status is equal equal equal to 404. Then we're going to say

set error and we're going to say story uh you know is not found.

Okay. otherwise. So, else we're going to say set error, let's spell that correctly. And we're

going to say failed to load story. And

then finally, we're going to say set loading to false. Okay, so that's how we're

to false. Okay, so that's how we're going to load the story. Now, we're

going to just have a simple use effect hook, which just means as soon as this page is rendered on screen. So, let's go use effect. We're going to have a

use effect. We're going to have a function and our dependency array. If

the ID changes, okay, so the ID in our browser, then we will run this use effect. And what we're going to do is

effect. And what we're going to do is we're simply going to call the load story function with the ID. So that's

it. We have a use effect. We load the story. And then we just need to render

story. And then we just need to render the story on screen, which we're going to do in one second. So we're actually going to say first of all const uh create new story.

And this is just going to be a simple function. And what this function is

function. And what this function is going to do is it's just going to call the navigate function and just navigate us to the homepage of our browser. We're

going to say if loading. So let's put this in brackets here. So if loading then we're going to return

the loading status.

Okay. And the theme is going to be equal to uh what is it?

Story. Okay. For the loading status.

Okay. So if it's loading, we show the loading indicator. Then we want to have

loading indicator. Then we want to have kind of like an error page. So we're

going to say if there is an error, then we're going to return a really simple form, a really simple page. So we're

going to say div class name is equal to story loader. We're going to have

story loader. We're going to have another div, okay, with class name equal to the

error message. Inside this div, we're

error message. Inside this div, we're going to have an H2. We're gonna say story not found. We're then going to show whatever the error is on screen. So

we're gonna have a p tag with the error.

We then are going to have a button. The

button is going to be on click equal to create new story. And we're going to say go to story generator. So if the story fails,

story generator. So if the story fails, then we'll let them go back to the story generator page. Okay, that should be it

generator page. Okay, that should be it for the error. And then lastly, of course, we need to show the successful story. So we're going to say, you know,

story. So we're going to say, you know, if story. So keep forgetting we need the

if story. So keep forgetting we need the parenthesis because we're not in Python.

So if there's a story, then we're going to return a not dove, but a div. The

class name will be equal to the story loader.

And then inside of here, we're actually going to render the story, but I don't yet have the story component component written, sorry. So I can't actually show

written, sorry. So I can't actually show it. So we'll do that in a second. But

it. So we'll do that in a second. But

then from now we're going to say export default and this is going to be the story loader. Okay. So this component will

loader. Okay. So this component will allow us to kind of go to slash whatever the story ID is and then load that up.

So for now we just need to write the story generator and the story game component and then we should be good to go. So let's go with the story game to

go. So let's go with the story game to start and then we can have the generator. So we're going to import

generator. So we're going to import use state and use effect from React. And

this is going to just render a story for us, right? So we're going to say story

us, right? So we're going to say story game and we're going to take in the story and the on new story. So

essentially what happens when you press um you know you want to create a new story, where do we go? So from this component, we're going to say const and we're going to be storing all the information about the story. So pretty

much for the story, it's actually pretty simple. We need to have the entire story

simple. We need to have the entire story including all of the nodes. But if we know what node we're currently on, then we can display that. And then when you press another option, we'll just update the node that you go to. And then we

just have a few edge cases for if the game ends, for example, or if you win.

So you're going to see it's actually not that complex. So we're going to say

that complex. So we're going to say const um current node ID and then set current node ID. And we're going to say this is equal to use state. And for now,

this is going to be null. Okay. Then

we're going to have const current node set current node is equal to

use state and then null. We're going to have const options set options. So these

are the options we're going to be displaying for the current node, right?

It's going to be set state or not set state use state and by default it's going to be an empty array. And then

we're going to have const is ending set is ending is equal to use state false.

Then we're going to have const and this is going to be is winning ending set is winning ending is equal to use state false. And now we can go and write the

false. And now we can go and write the rest of the component. So we're going to have a simple use effect. So we're going to say use effect. Okay. For here,

what we're going to do is we're going to check if the story is changing. So,

we're going to say if story and story.root

story.root node. So, if we do actually have a story

node. So, if we do actually have a story that you pass to us here, then we are able to render it. And we can say const root node id is equal to story.root node

ID. And then we can set the current node ID equal to the root node ID. Okay. Then

we can do the dependency array of story.

So essentially anytime the story changes that we're showing here, we can change the root node ID and we can start rendering that entire story. Now we also need to have another use effect. So

we're going to say use effect.

Okay, the dependency array for this one is going to be the current node ID and the story. So essentially if the current

the story. So essentially if the current node ID changes, then we need to run this effect which is going to render new options onto the screen for us. So we're

going to say if current node ID and story and story doall nodes. So if

we have access to all of the nodes and we have a current node ID and the story exists then we can say con node is equal to story.all

to story.all nodes. Okay. And then we can set the

nodes. Okay. And then we can set the current node ID or we can get the current node ID. Sorry. We can say set current node is equal to the node that

we just loaded from all nodes. Then we

can say set is ending equal to node.is

ending. Right? So is underscore ending.

We can say set is winning ending to node.is

node.is winning ending. All right. And then we

winning ending. All right. And then we can continue. Now, why is this not

can continue. Now, why is this not highlighting? Actually, no. We're all

highlighting? Actually, no. We're all

good. We're going to say if now not node do is ending.

Okay. And node dot options and node dot options.length

options.length is greater than zero. So if we do have any options to display then we're going

to say set the options to be node dot okay node.options options otherwise

okay node.options options otherwise set the options to be an empty array.

Okay, so we're just setting up all the values that we need. All right, so now we're going to go down here and we're just going to write two very simple functions. First is going to be choose

functions. First is going to be choose option. Okay, we're going to take in an

option. Okay, we're going to take in an option ID and all we're going to do is say set current node ID to option ID. So

when we set the current node ID, what's going to happen is this is going to change. So it's going to then going to

change. So it's going to then going to update all of these fields for us. Okay.

Then we have a restart story. So const

restart story is equal to a function.

Here we're going to say if story and story.root

story.root node exists, then we're going to say set current node ID to the story.root node

ID. And that will reset the story for us and bring us back to the original state.

That's all we need in terms of logic.

Now we just need to write the kind of UI. So we're going to say a return.

UI. So we're going to say a return.

Okay, we're going to return a div. We're

going to have class name which is going to be equal to the story-game.

We'll have kind of like a header component here. So we'll say header and

component here. So we'll say header and class name is equal to the story header.

We then are going to just have an H2 which is going to put the title. So it's

going to say story.title. Okay. So we'll

display the title of the page. Then we

need the actual content. So we're going to have a div with class name equal to story dash content. Okay. Inside of here we're going to check if we have a

current node. So we're going to say

current node. So we're going to say current node and then we're going to render a div.

Okay. Let's close the div. For the div, we're going to have a class name equal to the story- node. All right. Then

we're going to show the current node content. So we're going to have a

content. So we're going to have a paragraph tag and say current node.content.

node.content.

So we can show kind of what it reads as.

Let me get rid of this brace by the way.

Okay. Then we're going to have all right are we ending? So if we are ending then we need to show them that we're ending.

So we're going to say is ending question mark. And then we're going to have a

mark. And then we're going to have a div. Let's close the div. The class name

div. Let's close the div. The class name will be equal to the story ending. Okay.

Then inside of here, we're going to have an H3. We're going to say is winning

an H3. We're going to say is winning ending question mark. We're going to say congratulations if it is. Otherwise,

we're going to say the end. Okay. So, we

either tell them, you know, if they're ending, we're going to check are they winning? If they are winning, we're

winning? If they are winning, we're going to tell them, okay, good, you know, congratulations. Otherwise, the

know, congratulations. Otherwise, the end. All right. Then underneath here,

end. All right. Then underneath here, we're gonna have is winning ending again. And we're gonna have a question

again. And we're gonna have a question mark and we're going to say you reached a

winning ending otherwise your adventure has ended. Okay, then we are almost

has ended. Okay, then we are almost done. We need to now put the next step.

done. We need to now put the next step.

So essentially, all right, if this doesn't happen, right, if we're not ending, then we need to display the options. So for not ending, we need our

options. So for not ending, we need our div and we need to close our div. Please

close the div. Okay, let's try to format this a little bit better. Okay, so

almost there. We have this div. For the

div, we're going to say class name is equal to the story dash options.

Okay, inside of the div, we are going to say h3. What will you do? Question mark.

say h3. What will you do? Question mark.

All right. Then we're going to have another div. For this div, we're going

another div. For this div, we're going to have class name equal to and this is going to be the options dash list. Then

inside of this div, we're going to render all the options. So we're going to say options do map. We're going to take in the option

and the index and then we are going to render the correct thing. So from here we're going

correct thing. So from here we're going to return. Let's make sure this is

to return. Let's make sure this is correct. So, we're going to say return.

correct. So, we're going to say return.

And this is going to be a button. The

button is going to have key equal to the index. And let's fix our formatting

index. And let's fix our formatting here.

Okay. Go down. Indent that one in. Okay.

Let me just fix this up here. That's not

what I want to do. Button looks good here. Okay. So, we have key index. So we

here. Okay. So, we have key index. So we

have on click which is going to be equal to choose option.

Okay. And we're going to call the option node id. Then down here we're going to

node id. Then down here we're going to have class name is equal to the option btn. And then inside of the button we're

btn. And then inside of the button we're going to display the option.ext.

Okay. And let's format that a bit better. So there we go. That is what

better. So there we go. That is what we're doing for the options. Uh that

should pretty much wrap up this. So if

they're not ending or if they are ending. Okay. Okay. And then outside of

ending. Okay. Okay. And then outside of right here. Okay. So this bracket that

right here. Okay. So this bracket that you see, I know it's a little bit confusing, but there's going to be kind of two divs at the end here. We're going

to do a new div and we're going to say class name is equal to the story controls. Okay. We're then going to have

controls. Okay. We're then going to have a button. And for the button, we're

a button. And for the button, we're going to have an on click which is equal to the restart.

Okay, story like that. Then we're going to have the class name equal to and this is going to be the reset btn. And then

here we're going to say restart story.

Okay. Then we're going to have another button, but only if we have a new story.

So we're going to go down here. We're

going to say on new story. So if they pass us that function, we're going to have an and and we're going to have a button. Okay, we're going to say on

button. Okay, we're going to say on click is going to be equal to the on new story. Then we're going to have the

story. Then we're going to have the class name which is the new story button. That is correct. Okay. Then

button. That is correct. Okay. Then

we're going to make the actual button here. So button and this is going to say

here. So button and this is going to say new story. Okay. Then lastly, we're

new story. Okay. Then lastly, we're going to say export default story game. All right, so at this point,

story game. All right, so at this point, actually, after all of this code is written, again, you can find it all from the link in the description, we're almost ready to test the front end. What

we need to do is just go to our story loader, and we're just going to import that component we just wrote. So, we're

going to say import the story game from the story game.jsx. And then what we can do is we can render the story game down here. So we can just say like this the

here. So we can just say like this the component story game we can pass our story and we can say on new story create new story where it will navigate us to that page. So for right now we're not

that page. So for right now we're not going to have the ability to generate a new story but we should be able to view a new story once we set up React Router DOM. Okay. So everything's done other

DOM. Okay. So everything's done other than the story generator which we'll do in a second. For now we're going to go set up uh React Router DOM. So to do that we're going to go to app.jsx JSX

and we're going to bring in the DOM components that we need. Uh and we're going to start setting up our routes. So

from here we're going to say import okay the browser router as router.

Our route or roots whatever you want to say our route from and this is going to be react router DOM. Okay. We then are

going to import the storyloader from dot /component/story loader. Inside of here, we're just going

loader. Inside of here, we're just going to create the react router DOM application which allows us to navigate between pages. So we're going to say

between pages. So we're going to say router.

Okay, so let's create the router. Inside

of the router, we're going to have a simple div. We're going to have class

simple div. We're going to have class name is equal to app dash container.

We're going to have a header.

Okay, for the header, we're just going to have an H1 and we're going to say interactive interactive story

generator. Okay, let's fix that.

generator. Okay, let's fix that.

Interactive story generator. We're then

going to have main. Okay, so this actually needs to be outside of the header. So, let's go down here. All

header. So, let's go down here. All

right, for main, we are going to have our routes. So, all of them need to go

our routes. So, all of them need to go inside of here. And then we're going to do our individual routes. So we're going to have a route and we're going to say the path is equal to /story slash colon

id which just means that this is a dynamic value. So you can pass the ID

dynamic value. So you can pass the ID the element. So element that we want to

the element. So element that we want to render is going to be the story loader like that. And for our route we're just

like that. And for our route we're just going to make this a self-encclosed tag.

So we're going to end it like that. All

right. That's all that we need for right now. Uh, last thing we need to do is

now. Uh, last thing we need to do is connect our API backend to the front end. In order to do that, we need to go

end. In order to do that, we need to go to this vit.config.js

file and we just need to set up what's known as a proxy so that anytime we send a request to the / ai route, it automatically gets forwarded to our backend. So to do that, we just add a

backend. So to do that, we just add a server option here. So we're just going to say server. Okay, we're going to say proxy.

And what we're going to do is we're going to do slash API.

And then we're going to have the following options. We're going to say

following options. We're going to say the target is equal to our backend. So

it's going to be localhostport 8000. And

what this is saying is anytime we go to / API, essentially just take whatever we're sending and send it to the backend instead. We're going to say change

instead. We're going to say change origin is true because we're changing the origin of this request. And we're

going to say secure is equal to false.

Okay? So that we don't get any errors.

So now whenever we send a request to slash API, it will automatically get forwarded to our backend server assuming that that server is running. So I want to run the front end now. So I'm going

to just go to the front end, type npm rundev. When I do that, it will start

rundev. When I do that, it will start the front end. Okay. Then I need to make sure my back end is running as well. So

I'm going to cd into the back end and I'm going to go with uh uvun main.py. Uh that should run for us.

main.py. Uh that should run for us.

Okay. And now what I'm going to do is open up my front end. So I'm going to go here. So what I'm going to do now is

here. So what I'm going to do now is open up this port. And you can see we have interactive story generator. And if

we go to slash one for example, uh or maybe slash2 or slash3. Uh it should be loading our stories, but it looks like it's not. And maybe that's because I put

it's not. And maybe that's because I put the wrong URL. I think it is. Yeah, we

need to go to slstory/ ID. So let's try this. /story slash 1 or not 12. Sorry.

this. /story slash 1 or not 12. Sorry.

Let's go to slash one. And if we open up the console, looks like we are getting an error here. Something about uncut error. Use params is not a function.

error. Use params is not a function.

Okay, so let's fix that up really quickly. So if we go to story loader

quickly. So if we go to story loader says use params. We may have spelled use params incorrectly or something. So let

me see. Ah, okay. So the issue is we're importing this from React, not React Router DOM. So we're getting a problem

Router DOM. So we're getting a problem there. So we just need to change this to

there. So we just need to change this to be from React Router DOM. the correct

import. Go back here and you can see now that it's actually loaded the story for us. So, like I said, we should be able

us. So, like I said, we should be able to load the stories, which we are. So,

we can kind of press on some of the options here. You can restart the story

options here. You can restart the story or we can go through this. Restart go

through the options. Okay, so it's loading, right? Stories are working. And

loading, right? Stories are working. And

I think we only generated one story, I believe. So, this is the only one that

believe. So, this is the only one that we can view. Uh, and based on kind of the options we pick, and you can see there's a lot of them. I can't quite get to the correct option here. uh we should eventually be able to see like yes we

got to the correct option that if we press new story it brings us back to the homepage. Okay so now all we need to do

homepage. Okay so now all we need to do is write the story generator uh which will allow us to generate the story and wait for it to load and then we'll be done with the whole application and

we'll be able to move on to deployment.

So let's tackle that. So now we're moving on to the story generator. Now

this component will be used for us to actually submit the form to the back end so we can tell it what the theme of the story is that we want to create. Now,

this is also going to be responsible for pulling the endpoint. You'll see how that works. But essentially what that

that works. But essentially what that means is we're going to keep calling the endpoint to see if the job is finished.

And as soon as it is, then we'll load the story and redirect them to that page. So, let's start with our imports.

page. So, let's start with our imports.

We're going to import use state and use effect from React. We're then going to import the use navigate, okay, from React Router DOM. We're then going to

import Axios. Okay, so we can send a

import Axios. Okay, so we can send a request from Axios. We're going to import the theme input component. So the

one that we wrote already here from theme input.jsx.

theme input.jsx.

And we're going to import the loading status. So we're actually going to use

status. So we're actually going to use the loading status now so that we can see if we're loading the um story or not. Okay. Now, similarly to before, we

not. Okay. Now, similarly to before, we need to have the API_base URL which is going to be equal to / API.

Now later or actually now I'm thinking we might want to just put this in a separate file uh because we're using it in multiple places so we probably shouldn't be repeating it. So what we can do is just make a new file here and

we can just call this something like you know util.js.

know util.js.

Okay. And let's just copy this variable and just export it here. So we're just going to say export const API base URL.

Uh so this way we can just import it from where we're using it. So let's now remove this and let's go import and then this is going to be the API base URL

from utils.js and let's copy that and go back to where we load the story. So here

where we have our API base URL we'll delete that and we'll just bring in this import. So now we're using it um and not

import. So now we're using it um and not duplicating the code. Okay. So now that we have that we're going to make a function. So we're going to say function

function. So we're going to say function story generator. Okay. In this function

story generator. Okay. In this function we're going to have some state. So first

we need the ability to navigate. So

we're going to say const navigate is equal to use navigate. We're then going to say const theme and set theme is

equal to use state like that. We're then

going to say const and let's fix this here. This is going to be job id and

here. This is going to be job id and then set job id is going to be equal to use state and then null. Okay, we're

going to say const and this is going to be job status and then set job status is going to be equal to use state null.

We're then going to have the error as well. So let's go here const and we're

well. So let's go here const and we're going to have error and set error is equal to use state null. And then

lastly, we'll have the loading. So we're

going to say const loading set loading is equal to use state. And then we will start again with false. Okay. So now

we're going to write a few of the different functions and use effect hooks that we need. So the first use effect hook here is going to be responsible for pulling the job. Uh and actually before we do that, let's write the individual functions and this will make a little

bit more sense. So let's start with const and we're just going to call this reset. Okay. Now this is just going to

reset. Okay. Now this is just going to reset uh kind of this form here and the job status that we're keeping track of in case there's an error and we need to retry. So we're going to say set job ID

retry. So we're going to say set job ID to null. We're going to say set job

to null. We're going to say set job status to null as well. We're going to say set error. Let's type this correctly. So error to null. We're going

correctly. So error to null. We're going

to say set theme to an empty string. And

we're going to say set loading to false.

So just in case there is any error, we'll just reset all of the state in this function. Okay. Then we're going to

this function. Okay. Then we're going to have a function that we'll call as soon as the job is finished and the story is ready to load that will just redirect us to that page. So we're going to say

const fetch story is equal to async.

Okay, we're going to take the story ID that we want to fetch and then we're going to try the following. So we're

going to say try and we're going to say set loading to false.

Okay, we're going to say set job status to completed and we're going to call navigate. Okay.

And we are going to navigate to uh let's go back ticks here slashstory slash and then we're going to put in braces here ID with a dollar sign. Okay.

So we're just going to navigate to this story ID page as soon as we try to fetch the story. Then we'll just have an

the story. Then we'll just have an accept or a catch block because we're working in JavaScript of course. So

we're going to say catch e and then we're going to say set error. And what

we're going to do is say in backtick failed to load story. So in case there was an error loading it for some reason here. And then we're going to go e

here. And then we're going to go e dossage. Now this should never happen

dossage. Now this should never happen but we're just going to handle the case in case. And then we're going to say set

in case. And then we're going to say set loading is equal to false. Okay. So

that's what we need for fetching the story and for kind of resetting the form. Now we need a function though

form. Now we need a function though that's going to allow us to pull the job status. So we're going to say const pull

status. So we're going to say const pull job status. This is going to be equal to

job status. This is going to be equal to async. We're going to take in the ID of

async. We're going to take in the ID of the job that we want to pull. And then

we're going to do the following. So

we're going to have a try. Okay. And

let's just write the catch block now so we don't get any errors. Okay. So we're

going to catch some error there. And

we're going to say try const response is equal to await axios.get.

axios.get.

Okay. Same thing. We're going to use back ticks and then we're going to use a dollar sign and I don't know why it's doing that. One second. Let's go here.

doing that. One second. Let's go here.

We're going to use the API base URL and then we're going to say slash jobs slash and then dollar sign and then in braces here ID. Okay, so what we're doing is

here ID. Okay, so what we're doing is we're trying to get this particular job ID and we just want to see what the status of this is. Okay, we're then going to say const status

story id error and we're just going to remap this to be called job error because we already have state called error is equal to response dod data.

Okay, what we're doing is we're just kind of dissecting this object here and we're getting out the different values that we care about. So we care about the status from the data, the story ID from the data, and then the job error, which

is going to be called error, but we're just going to remap it to a variable called job error so that it doesn't conflict with the error state that we have up here. Okay. Then we're going to say set the job status to whatever the

status is there. Then what we can do is we can check the status. So essentially

if the status is completed, it means that we can fetch the story and go to that page. Otherwise, we're just going

that page. Otherwise, we're just going to keep pulling the endpoint, right? So

we're going to say if the status is equal to completed then inside of here we can say fetch story and then story ID. Now we also will just check here. We'll say and

story ID just to make sure that the story ID exists because obviously if it's completed but we have no story ID then that's a problem. Okay. Now we're

going to say else if the status is equal to failed or we have some kind of error.

Okay. Okay, then what we're going to do is we're going to say set error and we're going to set the error to be the job error or if the job error doesn't exist, we're going to say failed to

generate story. Okay, then we're going

generate story. Okay, then we're going to say set loading to false so that at this stage here we'll stop loading and allow the user to try again. Okay, so

that's all we're going to do inside of here. So either it was completed to

here. So either it was completed to fetch the story or there was an error and we show the error and then the other state will be kind of like pending or processing. If that's the case, then

processing. If that's the case, then we'll just keep calling this function and we'll call this function from a different place which I'm going to show you in a second. Now, if there is an error, what I want to do is I just want

to say if e.response response question mark status does not equal 404. Then

what I'm going to do is set an error and I'm going to say in backtick failed to check story status colon and then we're

going to do a variable and we're going to embed e dossage.

Okay. And then same thing we're going to say set loading false. Now we hope that we're never going to run into these errors here but we're just making sure we handle all of the cases. So it's only if we didn't get some 404 error here that we're going to show the message.

Okay. And I just added another equal sign so that we got rid of that warning message. Okay. So that is pulling the

message. Okay. So that is pulling the job status. Uh now what we're going to

job status. Uh now what we're going to do is we're going to write a function that we can call to actually generate the story. And then we're going to have

the story. And then we're going to have a use effect hook where after we try to generate the story, we'll keep pulling for the new job status to see when it's created. So we're going to say const

created. So we're going to say const generate story. Okay. This is going to

generate story. Okay. This is going to be equal to async. All right. And this

is going to take in the theme. Okay.

Then from here, what we're going to do is the following. We're going to say set loading equal to true. We're going to clear any errors that did exist before.

So we're going to say set error is equal to null. And we're going to set the

to null. And we're going to set the current theme equal to whatever theme was passed here so that we can show it kind of while we're loading the story.

Then similarly to before, we're going to have a try catch. So we're going to say try const response is equal to await axios.post.

axios.post.

Okay, we're going to use the API base URL again. So API base URL and then this

URL again. So API base URL and then this is not going to be the SL jobs. This is

going to be slash stories slashcreate and then we will just pass the theme data like this. Okay, so that's going to send the post request which will start generating the story. Then we're going

to say const job ID and status is equal to the response data. And we're going to set the state. So we're going to say set

job ID to be the job ID. And we're going to set the job status, okay, to be the status. Perfect. All

right. Then after this, we're going to start pulling the endpoint. So we're

going to say pull job status. And we're

going to pass the job ID. So the idea is we send the request to start creating the story. We get the information back.

the story. We get the information back.

So we get the job ID that was created and the status of that job and then we start pulling immediately which means we're going to call this function which will then go to check okay is the job ready. Then we're going to set up a use

ready. Then we're going to set up a use effect hook in a second that will just keep calling this constantly until the job is done. Okay. Then we're going to have a catch. Same thing we'll catch on

E and we can say set loading is false and we can say set error and then same thing we're going to say failed to generate story and then we're going to put the e dossage. Okay, so whatever

that is. All right, so that is pretty

that is. All right, so that is pretty much it for the functions that we need.

Now we're going to write that one use effect hook and then we'll write the actual rendered component which is fairly simple. So we're going to say use

fairly simple. So we're going to say use effect here. This is going to be a

effect here. This is going to be a function. We're going to have a

function. We're going to have a dependency array. And for the dependency

dependency array. And for the dependency array, we're going to have the job ID and the job status. Now, this is important because if either the job ID or the job status changes, then that

means that we need to run this effect to see if either we need to call the poll job status function again or if we need to stop polling because we already have the um what do you call the result. So,

we're going to say let the poll interval equal or actually we don't even need to equal. We can just say let pull

equal. We can just say let pull interval. We can then say if the job ID

interval. We can then say if the job ID and and the job status is equal to processing which is what we called it.

So if we're still processing the job then what we're going to do is we're going to set an interval and keep calling pole job status. So we're going to say the pole interval is equal to set

interval. Now, setting an interval just

interval. Now, setting an interval just means that we're going to call a function um every few seconds or on some interval. In our case, it's going to be

interval. In our case, it's going to be every 5 seconds. So, we'll have a function inside of here. And what we're going to do is we're just going to call the poll job status. Okay? Like this.

And we're going to call it with the job ID. And then we're going to go here and

ID. And then we're going to go here and we're going to say 5,000, which means we're going to wait 5 seconds before we do this again. So, an interval, very basic, just means we're going to keep calling this every 5 seconds. That's it.

Okay. Now, what we need to do though is we need to return from this effect something that will essentially close the interval. So, we're going to return

the interval. So, we're going to return this function and we're going to say if pull interval. So, if this exists, if we

pull interval. So, if this exists, if we created this, then what we need to do is say clear interval and we're going to clear interval pull interval. So what

will happen now is when we leave this page, so when this component is kind of rerendered or deattached from the screen or the DOM, this function will be called and it will see if we had this polling

going on. If we did, then it will just

going on. If we did, then it will just clear it. So we'll stop polling. The

clear it. So we'll stop polling. The

reason why this will work is because as soon as the job is loaded and it's finished and completed, then we have the story. And if we look down here, we

story. And if we look down here, we redirect them to another page. So when

we redirect them to another page, then this page kind of closes, right? this

component is no longer on the DOM. So

that function will get called and then it will clear the interval. So we'll

stop hitting that endpoint. Okay, so

we're almost done. We're just going to go now and obviously write the um code that we want to show on screen. So we're

going to say return and we're going to have a div. The class name is going to be equal to the story generator. We're then going to show an

generator. We're then going to show an error message first. So we're going to say error and end and then we'll have a div. Okay, let's end the div. We're

div. Okay, let's end the div. We're

going to say the class name is equal to and this is going to be error dash message. Okay, and then inside of here,

message. Okay, and then inside of here, we're going to put a paragraph tag.

We're going to display the error and we'll just have a button that allows them to try again. So, we're going to say button try again. Okay, like that.

Let's remove this brace that we don't need. And for the button, we'll just

need. And for the button, we'll just have an on click. So we'll say on click is equal to and then this is going to be the reset like that. Okay, the reset function. So that's the error handling.

function. So that's the error handling.

Now down here we're going to show the input for the theme. So we're going to say if not job ID and not error and not

loading. So if we're not loading, if we

loading. So if we're not loading, if we don't have a job ID and if there's no error, then we're going to show our theme input. So we're going to say and

theme input. So we're going to say and and theme input like that. And then we're going to have an onsubmit. So we're going to say onsubmit is equal to generate story.

Okay. So we're going to call the generate story function as soon as we submit the kind of theme that we want to make the story for. Then lastly down here we're going to say if we are

loading so loading and and we're going to show the loading status and we can say the theme is equal to the theme and then we can close that like that. Okay

cool. So that should be good for now.

Um, again, what's going to happen here is we'll just display any errors, we will allow them to enter a theme if they haven't already done so. And if it's not loading, and then we should be good to

go. Now, lastly, let's export

go. Now, lastly, let's export default, the story generator, and let's make sure that we actually show it on the page by adding it to one of our routes. So, let's go to app.jsx. From

routes. So, let's go to app.jsx. From

here, we just need to add another route.

So, we're going to say route. Okay,

we're going to say path, and this will just be the default path. And then we're going to say the element is equal to and then we need to import this. So we're

going to say import okay the story generator from dot/component/story generator and then we're going to show

the story generator like that. Okay. So

we have the story loader, we have the story generator. Our entire front end is

story generator. Our entire front end is actually finished minus the deployment.

So let's quickly run this or let's make sure it's still running. Okay. So we

looks like it is still running actually.

So we have the front end and the back end. Okay, good. So, let's open this up

end. Okay, good. So, let's open this up in our browser. All right, let's refresh and let's go to the main page and see if this works. Okay, so we're going to go

this works. Okay, so we're going to go to slash. You can see it says generate

to slash. You can see it says generate your adventure. Enter a theme for your

your adventure. Enter a theme for your interactive story. So, let's go with

interactive story. So, let's go with something like pirates and let's press on generate. It says that it is loading.

on generate. It says that it is loading.

So, we're going to wait while it generates this story. And then we can check and we got an error message says you exceeded your current quota. Okay,

so this is actually an error that we're getting here from OpenAI. So, I guess I have been using it too much. So, I have this kind of quota error from OpenAI.

So, I am going to attempt to fix that.

But for you guys, this should have worked. Uh, and it should have loaded

worked. Uh, and it should have loaded the story. Anyways, I'll be back in 1

the story. Anyways, I'll be back in 1 second. Okay. So, unfortunately, I think

second. Okay. So, unfortunately, I think I'm just going to have to wait for this rate limit to wear off because I guess I was using this a lot for the tutorial.

So, for now, let's just move on to the deployment. I'm very confident that this

deployment. I'm very confident that this component is actually going to work and everything else is functioning and you can see all the code from the description. So, that pretty much wraps

description. So, that pretty much wraps up the coding component. Now, we're

going to go over to Coro and we're going to start setting up our deployment.

Okay. Now, as a reminder here, I partnered up with Coro for this video.

They're a really great platform for deploying your application. And what we need to do here is just make a new account. So, I'm going to leave a link

account. So, I'm going to leave a link in the description where you can get started for free. So, you can just press that button or just kind of like sign up for a new account with Coro pretty much.

And then once you do that, it should bring you to kind of a Coro platform, which I'm going to get to here in 1 second. Now, once you've created a new

second. Now, once you've created a new account on Coro, the next step here is going to be to make a new project. Now,

there's multiple ways to do this. And

first of all, you will need an organization. So, just make sure that

organization. So, just make sure that you make one. Again, it's free. You can

just set one up. And to make a new project, you can find this create button right here. You can also click on this

right here. You can also click on this arrow and click on create project. You

can go to overview and then find the button. Okay. So, we just got to make a

button. Okay. So, we just got to make a new project. So, let's go create

new project. So, let's go create project. What we're going to do for the

project. What we're going to do for the name is I am just going to call this my kind of choose your own adventure game.

Choose your own adventure like that. Okay, for the name it's going to go with choose your own adventure. And then we are going to

adventure. And then we are going to connect this to a GitHub repository. So

let's go back to the project and set that up. Okay, so we're going to open up

that up. Okay, so we're going to open up PyCharm. I'm just going to shut down the

PyCharm. I'm just going to shut down the services that I have running here. So

just shut all of this down. We're going

to CD into the root directory and we're going to create this git repo. All

right, so I just cleaned up the screen a little bit. What I'm going to do first

little bit. What I'm going to do first of all is I'm just going to make sure that I delete any.Git folders that exist in my project. So I'm just going to open this up in my file explorer. So I'm

going to go to choose your own adventure and I'm just going to make sure that I view any hidden files here on Windows.

On Mac it might be a little bit different. I'm going to click into both

different. I'm going to click into both backend and front end and if I have any.get git folders. I'm going to delete

any.get git folders. I'm going to delete them just because sometimes these projects automatically make git folders for you, but I want the git folder to be in this root directory, not in these

individual directories. Okay. Now, I'm

individual directories. Okay. Now, I'm

also going to go to my backend. I'm then

going to go to myv file in the back end and I'm just going to remove my openi API key here so that I don't accidentally commit this to source control so I don't accidentally put it

on git. So, we'll just remove that. But

on git. So, we'll just remove that. But

we will actually just upload this whole um env file to GitHub because it doesn't contain anything sensitive now that we've removed the OpenAI API key. Okay,

later we will add the OpenAI API key in kind of production when we're deploying this. Okay, so now everything should be

this. Okay, so now everything should be kind of set up here. Uh we're also going to go into front end and sorry, we're going to make one more folder here.

We're going to make a new file. We're

going to call this enenv and we're going to say debug is equal to true inside of this file. Okay. And actually, it needs

this file. Okay. And actually, it needs to be vitug.

The reason for this is we want to know if we're running the front end in production or not, so we can determine how we're going to call our backend because we're going to have a kind of different deployed backend when we're in

production or when we're deployed versus when we're running this locally on our own computer. So, I'm going to need this

own computer. So, I'm going to need this variable. And you know what? While we're

variable. And you know what? While we're

at it, what I'm going to do is I'm going to go to this v.config.js

js file and I'm going to modify this slightly so that we only use this proxy to our backend when we are running in kind of the local environment rather than when we're running sorry in the

deployed environment. Okay, so what

deployed environment. Okay, so what we're going to do here is we're just going to bring in something called load env. This is a function from vit and

env. This is a function from vit and what we're going to do is we're simply going to load our environment variable file here inside of this configuration.

check this value that we just defined and if it's true then we'll use this proxy otherwise we don't need to use it.

So in order to do this we're going to say export default define config but we need to actually change this to a function. Okay so this is going to be a

function. Okay so this is going to be a function and we're going to accept command and mode. Okay then what we're going to do is we are going to return this here. Okay, so we're going to

this here. Okay, so we're going to actually have a set of parenthesis or a set of braces, sorry, another set of braces, and inside of here, we're going to return this as kind of like a

configuration object. Okay, so I know

configuration object. Okay, so I know this is a little bit weird, but we're just returning this. We've turned this into a function. And now what we're going to do is start using some of the stuff here. So we're going to say const

stuff here. So we're going to say const env is equal to load env. And we're

going to load env Okay, so let's spell process correctly.

CWD, which stands for current working directory. Just means load the

directory. Just means load the environment variable file from this directory. We're then going to put an

directory. We're then going to put an empty string as the prefix. Okay, so

don't worry about that. Then what we're going to do is we're just going to log out the environment variable to make sure that it's working. So we're going to say console.log and we can just log envug.

Okay. And what we're going to do here is where it says server, we're going to just put the following. We're going to put a set of parenthesis. We're going to put dot dot dot before this. And we're

going to put env.vit_debug.

Okay. Is equal equal equal to true.

Okay. And then we're going to have and and then we're going to have this proxy config that you see here. So we're going to put proxy like this and close the

parenthesis. And sorry, let me just fix

parenthesis. And sorry, let me just fix the parenthesis like that. So I think this should be good. So essentially

we're going to do all of this here.

Okay. this proxy only if v2 debug is equal to true. All right, so just kind of copy this if you need to. Again, all

of this code will be available from the link in the description. So in case you're getting lost on kind of the parenthesis or what I'm typing here, but we just want to make sure we only include this if the um what do you call it? If we're running this in development

it? If we're running this in development mode. Okay, so that's it for the

mode. Okay, so that's it for the frontend changes, at least for right now. We will need to change something

now. We will need to change something later. And we're going to go to our back

later. And we're going to go to our back end and actually we're going to make one more change before we make the git repo.

So, we're going to make a new file here called requirements.

Okay requirements.txt.

Now, this is because Coro doesn't know how to use UV. So, we need to manually specify the requirements that we need for our Python project. So, we can go to the pi project.toml file and we can just

copy the dependencies that are listed here and we can just paste them inside of the requirements.txt file and we can just remove all of the uh what do you

call it here? quotations and the commas.

Okay, so you want your requirements.txt file to look something like this. So if

it looks like this, we're good to go.

Just make sure that you have requirements.txt inside of the back end.

Okay, so continuing from there, what we're going to do is make our git repo.

So we're going to close all of this.

Okay, we're going to open up the terminal and we're going to type getit dot. Okay, and sorry, lastly, we're

dot. Okay, and sorry, lastly, we're going to go to front end. We're going to go to getit ignore and we're going to ignore the enenv file that's here so that we don't accidentally commit it to

source control because we just want this when we're running the code locally.

Okay, so now we've initialized the new git repo and we're going to type get add dot. We're going to type get commit-m

dot. We're going to type get commit-m first commit. So we're just going to

first commit. So we're just going to save everything that we've done and then we are going to type get branch dash m with a capital M and then main. What

this is going to do is change the master branch to be called main. And then we're just going to set up a git repo uh like a public git repo access. Okay. So, what

I'm going to do is I'm just going to go over to GitHub. So, you guys are going to have to do the same thing for yourself. We're going to open up GitHub

yourself. We're going to open up GitHub and we're just going to make a new repo.

So, I'm going to go new new repository.

Okay, this is going to be super simple.

You're just going to put your name as the owner. For this, we can go, you

the owner. For this, we can go, you know, choose your own adventure AI or something and spell adventure correctly. We can make it public. You

correctly. We can make it public. You

can make it private if you want, but I'm just going to make it public cuz I want you guys to be able to view it. We don't

need to add anything else. And then we can just go with create repository.

Okay. From here, we just care about these two lines. So, this get remote add origin and this get push command. So,

we're just going to copy this get remote add origin command. Okay. which is going to allow us to add the remote origin here. Then we're going to type git

here. Then we're going to type git push-u with a capital and then origin main or actually let's just check to see if that's correct. No, it's a lowercase.

So get push u with a lowercase origin main and that's going to push our code up to the origin. So now if we go back to GitHub, we should see that all of our

code is up here. Okay, so we have it backend, front end, etc. We can add a readme file if we want and now we should be good to go. Okay, so now that we have the Git repo, we're going to open Coro back up here. We're going to go back into that create project flow in case

we're not already in it. I'm just going to type the name again because for me it kind of like sign me out of this. So,

choose your own adventure. Okay. Then we're going to

adventure. Okay. Then we're going to authorize this with Git uh or connect to a public repo. So, if you want to authorize with Git um that's just going to connect to your GitHub account.

You're going to have to go through that flow. In my case, it already connected.

flow. In my case, it already connected.

So, I'm going to choose my organization which is Tech with Tim. I'm going to choose the repository which I called choose your own adventure game uh with AI. So let's pick that. And then for the

AI. So let's pick that. And then for the project directory that can just be slash. Okay. And then we're going to go

slash. Okay. And then we're going to go ahead and press on create. So we've now kind of synced our GitHub repo up here to our project. Now what we need to do from here is we need to deploy two separate components on Coro. The first

is the front end, the second is the back end. And then we just need to connect

end. And then we just need to connect them together. So from here we're going

them together. So from here we're going to go to web application and we're going to press on continue with Git. We're

going to start by deploying the front end because that's the simplest and then we will set up the back end. Now we see it's going to automatically connect to your GitHub. So we have Tech with Tim.

your GitHub. So we have Tech with Tim.

I'm going to scroll down. I'm going to find the repo again. So let's go choose your own adventure AI. For the component directory, we're going to press on edit here and we're going to select the directory which is the frontend

directory. Okay. So just select front

directory. Okay. So just select front end and press on continue. Then we can change the name if we want. I'm just

going to call it front end. For the

build preset, we're going to select on React. Okay, for the build command,

React. Okay, for the build command, we're going to type npm run build. For

the build path, we're going to type /dis, which is where the distrib distributable file story will go. And

for the node version, we can just figure out what node version we're using on our own computer and use that. So to do that, you can just type node-v in your terminal. It's going to give you a

terminal. It's going to give you a version. So you can just copy that

version. So you can just copy that particular version, come back here, and just paste that in. So 20.17.0

and then press on create and deploy.

Okay. Okay, so what this is going to do is deploy the web application for us.

And I'm going to show you kind of how these this gets set up and how we can view the web application. Now,

obviously, it's not really going to work right now because we haven't connected it to the back end, but this is the first step. Just get the kind of front

first step. Just get the kind of front end up and running. Then we can deploy the back end. Then we can connect the back end. And you're going to see how

back end. And you're going to see how simple it is to do that here with Coro.

Okay. So, right now, what's happening is it's being created and it's being built.

Now the pipeline here that you're going to see in choreo is that you need to create build and then deploy. Now

fortunately coro can connect directly to your GitHub repository. So anytime you make a new commit, it can actually automatically build this and then you can come up here and deploy it whenever you want. So if you go to the side here,

you want. So if you go to the side here, you'll see that we're in this component.

Sorry. So we're in this component front end. Okay, we can have multiple

end. Okay, we can have multiple components which we're going to add in a second. And if we go to build, you can

second. And if we go to build, you can see that this build is currently in progress. Now, if you were to toggle

progress. Now, if you were to toggle auto build on commit, what will happen is that uh it's automatically going to build this anytime a new commit is made.

So, I'm going to do that. We're just

going to have to wait a second.

Sometimes it takes like 2 or 3 minutes to build. And then if we go to deploy,

to build. And then if we go to deploy, once this build is finished and one of them is created here, we'll be able to deploy it and then we can push that into production. So Coro gives us an

production. So Coro gives us an automatic staging environment as well as a production environment so that we can test what we're doing before we kind of promote our build to production. It's

very cool. We also have a lot of other features like insights, usage, etc. We can't see them right now because of how things are set up. Uh observability

where we'll be able to view logs and stuff. Again, it depends on what kind of

stuff. Again, it depends on what kind of project we're deploying here, but you get the idea. And then we have connections. So in a second we're going

connections. So in a second we're going to connect the um back end here to the front end using this connections tab.

Okay. So the front end has actually been deployed here and what we can do is press on this URL called web app URL and it should bring us here to the application. Now you can see it's

application. Now you can see it's actually working. It's hosted on this

actually working. It's hosted on this domain which you could share with someone else. But if we try to use this

someone else. But if we try to use this now it's just not going to work because we haven't connected it to the back end.

So you can see it says failed to generate story. Right. So what we want

generate story. Right. So what we want to do now is start deploying the back end cuz the front end's actually up. And

if we go to build for example, you can see the build was successful. We can

view the details of the build and kind of view the logs and stuff as well. Just

takes a second to load here. So

initialization, build etc. And if we go here to deploy, we can see the current build, we can see the deployment here.

And we can enable autodeploy on build if we want as well, which I'll do for right now. So it will automatically deploy for

now. So it will automatically deploy for us. There's all kinds of other stuff

us. There's all kinds of other stuff here. Shows us the web app URL, bunch of

here. Shows us the web app URL, bunch of settings. We don't need to go through

settings. We don't need to go through all of it for right now, but you get the idea. Okay, so that's the front end.

idea. Okay, so that's the front end.

It's up. Now, we're going to go to create new component here. So, from this component tab, and we're going to make a new one for the back end. However,

before we do that, there is one small thing that I need to add to the backend code. So, we're going to go back into

code. So, we're going to go back into PyCharm. I'm going to go to backend and

PyCharm. I'm going to go to backend and I'm going to make a new folder in here called Coro. Okay, actually Cororeo.

called Coro. Okay, actually Cororeo.

Now, this is a folder that we need. So,

Coro like that because it's going to be a special folder that Cororeo will look for to check the endpoint configuration for our backend. So, when we deploy on Coro, we need to specify what endpoints

we want to be available. So, what I'm going to do is I'm going to make a new file inside of here called component.yaml.

component.yaml.

Okay yl.

We're going to hit enter. And what I'm going to do is just copy in the configuration. And you guys can just

configuration. And you guys can just paste it from the description or you can simply write it out. So we're going to specify a schema version and then we're going to specify the endpoints that we need. Now we can give this a name. Okay,

need. Now we can give this a name. Okay,

we have a display name. We have a service and a port. This is the most important part. We specify this is a

important part. We specify this is a REST API and that this is a project public. Okay, so this just means that

public. Okay, so this just means that anyone will be able to call this API.

That's how we're going to set it up for right now. So we're going to save this

right now. So we're going to save this file. We're going to go to our terminal

file. We're going to go to our terminal and we're going to commit this to git.

So we're going to type git add dot git commit-m say add choreo folder and then git push origin main so that this actually goes

to the github repo. Okay. So now we have this cororo folder. We have our endpoint configuration. Again you can read more

configuration. Again you can read more about this from the choreo documentation but this is like the most basic version that's going to expose our different endpoints from our backend. And now

we're going to go here and we're going to go to service. Okay. So from service we're going to go continue with GitHub and same thing. We're going to find the same repository. So let's go and find

same repository. So let's go and find our choose your own adventure repository. So here you go. Choose your

repository. So here you go. Choose your

own adventure AI. We're going to select the component directory which is going to be backend this time, not front end.

Okay. We're going to choose Python as our build preset. For the language version, we're going to go with the newest, so 3.11. And for the command, we're going to go with Python 3 and then

main.py. Okay, so not UV, we're going to

main.py. Okay, so not UV, we're going to use Python 3. Then we're going to press on create and deploy. And we're going to wait for this to be deployed. So what

I'm going to do now is I'm going to close this and I'm going to start showing you how we interact with OpenAI and how we call that endpoint to actually generate the AI story. Now, we

could just add an environment variable as a part of our build. For example, we could go here, click on configure and deploy, and then we could add an OpenAI API key here. But I'm going to show you a more secure way to do this that

involves creating a connection within Coral. So what I'm going to do is I'm

Coral. So what I'm going to do is I'm going to exit out of this backend component so that I'm just in the root of my project. Choose your own adventure. I'm then going to go to

adventure. I'm then going to go to resources and where it says Genai services here. I'm going to make a new

services here. I'm going to make a new service. So Coro allows us to actually

service. So Coro allows us to actually make a Genai service to connect directly to OpenAI. So we can proxy all of our

to OpenAI. So we can proxy all of our requests through Coro and we can keep everything nice and secure. So I'll show you how this works. What I'm going to do is I'm going to click on register. I'm

going to go to OpenAI because that's the AI service that we're using for this video, but we could use Mistral, Anthropic, etc. So, let's go next. For

registering the service, we just need to give this a name. So, I'm just going to call this OpenAI. Okay, we can just give it version v1. And then for the service URL, just leave it empty. And then same thing with the summary. Okay, we're

going to go on next. And then what we need to do here is create an endpoint.

So, we're going to go new endpoint. And

we're just going to call this whatever we want. So, I'm just going to call this

we want. So, I'm just going to call this OpenAI. Okay. And then what we need to

OpenAI. Okay. And then what we need to pass here is an OpenAI API key. So, let

me grab one of those keys and I'll paste it here. So, I just copied one of the

it here. So, I just copied one of the keys here from the OpenAI website. You

can go to platform.opai.com/api

to get that. And then I've placed that here. Then, we're going to make sure the

here. Then, we're going to make sure the endpoint URL is this. And again, just name it OpenAI. And what we can do is press on okay. So, it adds the endpoint.

And then press on register. Okay. So,

this will take a second to register. And

once this is registered, we should actually be able to use this. So from

here, if you're curious, you can look at the service definition and you can see all of the different endpoints that you could call and how you would call them.

Now, in our case, we don't really need to use these. I'll show you how they work in 1 second. But for now, we can go back to services. And you'll see that it's not available. So what I'm going to do is I'm just going to click into this here, and I'm going to press the button

that says add to marketplace. When we

add this to marketplace, the service should become available. So if we go back to services, you can now see that this says available. And now we'll be able to connect to this as a connection.

Okay. So again, what this is effectively doing for us is it's creating some form of proxy where we can actually send a request to this the service created by Coro and then that request essentially

will be forwarded to OpenAI so that we have everything happening securely inside of our application. All right. So

what I'm going to do now is I'm going to go back to my backend component. And

what I'm going to do is I'm going to create a connection between my backend and between OpenAI. Okay. So to do that I am going to go to connections and I'm going to create a new connection to a

service. So from here we should see the

service. So from here we should see the OpenAI service. So I'm going to press on

OpenAI service. So I'm going to press on that here. For the connection we can

that here. For the connection we can just call this OpenAI connection. Okay.

And then where it says environment to endpoint mapping just leave this as it is. We don't need to change anything and

is. We don't need to change anything and we can go ahead and press on create.

Okay. So that's going to create the AI service for us. And you'll notice here that it gives us a few instructions on the right hand side on changes we need to make to ingest this service. So first

of all, we need to add this dependencies um I don't know kind of config to our configuration file. So what I'm going to

configuration file. So what I'm going to do is I'm going to copy this here. Make

sure you copy what you have on your I'm going to go back into PyCharm. I'm going

to go to my components.yaml and I'm just going to paste this underneath. Okay. So

your components.yaml file should now look like this inside of the cororo folder. So I'm going to save that. I'm

folder. So I'm going to save that. I'm

going to go back and then we can continue. Okay. Now, what's going to end

continue. Okay. Now, what's going to end up happening here is that we're going to have two environment variables which will be passed to our backend by default when the backend is deployed. Now, those

variables are going to be the following.

The Coro OpenAI connection OpenAI API key, then the Coro OpenAI connection service URL. Now, what we're going to do

service URL. Now, what we're going to do is we're just going to load in these environment variables from our Python script and we're going to use them when they're available to connect to OpenAI.

So, let me show you how this works. But

for now, I'm just going to copy these two lines right here where we're loading in these variables. And let's go back into PyCharm. Okay. So, what I need to

into PyCharm. Okay. So, what I need to do now is I need to go to core. I need

to go to my story generator.py. And

inside of here, let me just close my terminal. And then from get LLM, I need

terminal. And then from get LLM, I need to make a few changes so that I load this LLM differently based on if I'm in the development environment or the production environment or essentially the deployed environment. Now, if we're

just running this locally on our own computer, we'll just load the environment variable from that variable, which is OpenAI API key, which is contained inside of our env file.

However, if we're doing this in the deployed environment, then we want to use the environment variables that are passed to us from Coro, which will happen automatically when we deploy this. And I'll show you what that looks

this. And I'll show you what that looks like in one second. So, for now, what I'm going to do is I'm going to paste in these two values. Notice that since they're using OS, I need to import OS.

So, I'm going to go up here and I'm going to import the OS module. Okay?

Okay. And then we can close that up. And

you can see now that we have these two values. Now, essentially, I want to use

values. Now, essentially, I want to use these values when I initialize this chat openai, but only if they exist. So, what

I'm going to do is I'm just going to put a simple if statement. And I'm going to say if the OpenAI API key and the service URL, then what I'm going to do is I'm going to return chat OpenAI. Same

thing. The model is going to be equal to GPT-40- sorry, this is 40- mini. And then what I'm going to do is pass two additional parameters. The first is going to be the

parameters. The first is going to be the API key which is going to be equal to the OpenAI API key. And the next is going to be the base URL. Okay, which is

going to be equal to the service URL. So

all I'm saying is all right if I have these two variables that are passed to my backend. So if they exist then what

my backend. So if they exist then what I'm going to do is return a new instance of my LLM but this time I'm going to pass a different API key the one provided from Coro and a different base URL. When you pass a different base URL

URL. When you pass a different base URL this means we're going to route all of our requests through Coro so we can handle everything through that platform rather than directly calling OpenAI. And

this again is much more secure. Okay.

So, what I'm going to do now is I'm going to save this. And that should pretty much be all of our backend code.

So, from here, what I need to do is just commit these changes. So, I'm just going to say get add dot and then get commit-m I'm going to say update backend for

OpenAI service. Okay. And then I'm going

OpenAI service. Okay. And then I'm going to push these changes. So, get push origin main. And now we can actually

origin main. And now we can actually redeploy this. So, we can test

redeploy this. So, we can test everything out and make sure it's working now that we've connected to this service. Okay. Okay. So, what I'm going

service. Okay. Okay. So, what I'm going to do now is I'm going to go back here and I'm inside of my back end, right?

So, from my back end, I'm going to go to build and I'm just going to build latest. Now, I could also check this.

latest. Now, I could also check this.

So, auto build on commit. So, anytime a new commit is made, it will automatically build. But for now, we'll

automatically build. But for now, we'll just manually build the latest kind of update to our codebase. And then once that's built, we can deploy this. Okay.

So, you can see now it's cued. It's

going to take a second to build. So,

once that's done, I'll be right back.

All right. All right. So, now that this build is finished, I'm going to go over to deploy and I'm going to deploy this build. So, from this page, I'm going to

build. So, from this page, I'm going to press on this arrow and I'm going to go on configure and deploy. I'm going to press on configure and deploy. And you

should see now that there's two environment variables that have been created which are managed by Coro and handle our connection to OpenAI. So,

let's press next, next again. And then

we're just going to make sure that we uncheck the OOTH box here. So, we

shouldn't have any of these checked because we're not using the authentication. And then we can go ahead

authentication. And then we can go ahead and press on deploy. Okay, so that's going to take a second to deploy. You

can see actually it's deployed right now. That was pretty fast. And then from

now. That was pretty fast. And then from here, of course, we have the option to promote this to production cuz right now it's in the development environment. We

can manage our secrets. We can handle things like the rate limiting for example, cores, resiliency, etc. Right from this panel. Okay. Now, I do want to actually test this application quickly.

So, what I'm going to do now is I'm going to go to this testing section and I'll just go to console. And from here it should give me a link. So you can see that this is the link for my API. So

from here I can just copy this URL and you can just put it in your browser and you should see that you get some output.

So in our case we get detail not found.

That's fine. That's actually what we're expecting. And if we want to test our

expecting. And if we want to test our real API endpoint, we could go to something like / API slash uh what did we have? I don't know stories or

we have? I don't know stories or something slash one and we could see if that story exists. Now it's telling us not found. It's fine. Okay. We could go

not found. It's fine. Okay. We could go to slash job slash one. Same thing we'll get not found just because this doesn't exist. But we can just test it quickly

exist. But we can just test it quickly from the browser like this. But what I want to do now is I want to allow our front end to interact with this back end. So we are getting some result which

end. So we are getting some result which means the back end is up and running.

And now we just need to connect it to here. Okay. So how are we going to do

here. Okay. So how are we going to do that? Well, we need to find something

that? Well, we need to find something here from choreo. So if we go to our front end. So we got to change

front end. So we got to change components to front end. Now we're going to create a connection. So if you go to the connections tab, you can see connections. We're going to press on

connections. We're going to press on service and we're going to select our backend as the service. Okay. Now you'll

see that I I have multiple here just cuz I made one as a test beforehand. So just

click on probably the only one that you see assuming you're not already using Coro. And we can just give this a name.

Coro. And we can just give this a name.

So we can just call this backend. Okay.

We're going to do public and we're going to go no authentication. Okay. So we're

going to go with create here. And now

the connection has been created. Okay.

So from here, if you were using Coro's managed authentication, which we're not doing, then you would be able to see the steps for setting up the O config. In

our case, the only thing that we actually care about here is this service URL. So what we want to do is copy this

URL. So what we want to do is copy this service URL because what's really interesting about Coro is it's going to be able to deploy this backend on the same origin as our front end. That means

that we don't actually need to have a proxy or send a request to a different origin. we can just send it to the same

origin. we can just send it to the same URL and then include this slash path and then whatever the slash URL is that we want to go to from our backend and cororeal will automatically route the

request for us. So let me show you what I mean by that. Again, just copy this service URL. Okay, when we copy the

service URL. Okay, when we copy the service URL, what we're going to do now is we're just going to go to our front-end directory. So in our actual

front-end directory. So in our actual code this time, we're going to go to src and then util.js. Now, where we have the API base URL here, what we're going to

do now is we're just going to simply append this prefix to it. So, it says /coro API/ choose your own adventure/backend/v1.

Okay, we can simply add this URL here and then slap ai. Now, when we um kind of make the commit again, we'll be sending the request to this URL, which

will automatically forward it to our backend uh and it's going to work really well. Okay, so that's all we need to do.

well. Okay, so that's all we need to do.

Now you could add an environment variable here and do this a bit more dynamically because when you do make this change it's going to now stop working locally and just work in our deployed version but in the name of time

I'm just going to do this right now and you guys can adjust that code later on.

So I'm going to go get add dot I'm going to go get commit-m say changed URL and I'm going to go get push origin main. Okay, we're going to

push this up to the git repo. Then we're

going to go back here to coro. We need

to go to our front-end component again and we can get out of this now because the connection is made and we're going to go to build. Okay, now you can see the build is automatically triggered because we had auto build on commit.

Once the build is finished, I'll be right back and then we'll ensure that this is actually working and that it's sending the request correctly. Okay, so

this is deployed. So we're going to go to deploy now. Uh it says it's active.

Okay, 21 seconds ago. So let's open up the new URL and let's try this out now.

So really what we can do is we can just try by typing Dubai or something here.

And now it says it's generating the story. It's actually loading which is a

story. It's actually loading which is a good sign. So let's wait a second here

good sign. So let's wait a second here and see if this works and generates the story for us. Okay. So the story was generated. So let's go through it. Okay.

generated. So let's go through it. Okay.

And you can see we get to the end. Okay.

Restart the story. The end. I'm just

going I'm just kind of messing with it to make sure that it actually does work.

And we're good to go. And then if we want we can try to go to story slash2 or something. And you can see here we go.

something. And you can see here we go.

We have now the story that we generated previously. Story slash one. And this is

previously. Story slash one. And this is working. Okay. So there we go. We

working. Okay. So there we go. We

deployed the application. It is fully functioning. It is on choreo. Now you

functioning. It is on choreo. Now you

could share this URL with anyone you want and they'll actually be able to use this application. So that is pretty much

this application. So that is pretty much it when it comes to the deployment.

Obviously you can promote these both to production if you want to do that. if

you're going to use this in a more serious setting. And I just quickly want

serious setting. And I just quickly want to let you know that Coro does actually have this platform engineer view as well, which is a little bit more complex that we're not going to get into in this video. But if you wanted to set up

video. But if you wanted to set up continuous integration and deployment pipelines if you want to do things that are again just a little bit more complex, you can do that with Coro and you can have kind of different views for

different people. So like a developer

different people. So like a developer view, which is what we're using right here because we're developing the app, and then a platform engineer view where you get kind of this more granular control. Okay, so let's go back to the

control. Okay, so let's go back to the developer view. Now, of course, if

developer view. Now, of course, if you're having any issues, you can press on this runtime logs button inside of observability. But it's also a little

observability. But it's also a little bit easier if you actually get out of the component view and you go to the root project view. And from here, you go to runtime logs and then you're able to see the logs for both the front end and the back end. And if you want to sort by

just one, you can go back, front end, etc. So you can view all of your components here. All right. So that's it

components here. All right. So that's it for that. But the next step here is

for that. But the next step here is actually to deploy a database because currently the database we're using is just a file that's contained kind of on the back end when really we should have a separate production ready database

which we're going to deploy with Coro.

So in order to do the database deployment, what I'm going to do is get out of my project here. So I'm just in my organization view and I'm going to go to resources and then databases. Okay,

again you need to be in the organization view to do this. From here, I'm going to press on create to make a new database.

I'm going to use Postgress SQL. And for

the service name, I'm just going to go with database. Okay, you can obviously

with database. Okay, you can obviously call this something better if you want.

Now, you can go through and you can choose the options. You should get one free database with your Coro subscription, so you don't need a credit card to do this. I do have an upgraded Coro account. That's why you're seeing

Coro account. That's why you're seeing that I have the various pricing options.

But for you, you should be able to just create one for free. So, I'm just going to go with the cheapest option right here. I'll put this on Amazon Web

here. I'll put this on Amazon Web Services. and I'll put this located in

Services. and I'll put this located in the United States. Okay. Now, it's going to take a second to create this database, but what we're able to do is we're able to copy some of these values now and just change a little bit of code

on our back end in order to connect to this remote database. So, let's go ahead and do that. All right. So, let's go to PyCharm. Let's go to config. py because

PyCharm. Let's go to config. py because

this is actually where we're going to make a few changes. And what we're going to do is start importing a few things that we need and then making some changes inside of here to load the new variables uh that we're going to take in

as environment variables for our database. So what I'm going to do is I'm

database. So what I'm going to do is I'm going to say import OS and for my database URL I'm just going to make this equal to none by default so that it's an optional value. Now what I'm going to do

optional value. Now what I'm going to do is I'm actually going to add an init method on line 16 here. And essentially

what I'm going to do is the following.

I'm going to look and I'm going to check to see if I'm in debug mode. Now, if I'm in debug mode, that means that I'm running this locally. If I'm running it locally, then there's no change that we need to make because we'll only connect

to this database if we're running this in a production environment. So, if we are running it or not production, but in the deployed environment, then we'll connect to the remote database. So, what

I'm going to do for a net is I'm going to take in self and then asterisk asterisk values. Okay. Then down here

asterisk values. Okay. Then down here I'm going to say super_nit and then again I'm going to take in my asterisk asterisk values like that.

Okay, this is just to call the default constructor to make sure that I don't mess anything up. Then I'm going to say the following. I'm going to say if not

the following. I'm going to say if not self.debug.

self.debug.

So if we're not in debug mode, then what I'm going to do is I'm going to start setting a bunch of variables here that are related to connecting to our database. So I'm going to say the db

database. So I'm going to say the db user is equal to os.get env. And this is going to be db

user. Okay. Then I'm going to say the db

user. Okay. Then I'm going to say the db password is equal to os.db password. And

then it actually is autocompleting what I want. So let's just use that. All

I want. So let's just use that. All

right. So you can see that we're loading the database password, the host, the port, the name, and then we're creating a database string. So we're saying the self.database database URL which is the

self.database database URL which is the same as the one that we have up here is equal to and then we're saying that for this we're using Postgress and then we're having the user the password and then the host port name etc. And that's

actually the only change that we need to make because if we go and we look at our database config here our database setup you can see that we end up using this database URL to create this database

engine. So when we change the database

engine. So when we change the database URL string here, when we're not in debug mode and we load in these other variables, then we should just automatically connect to that database.

Okay, so we should be good to go there.

Let's just make sure that everything is set up. And let's also just make sure

set up. And let's also just make sure that we're calling create tables. So I'm

just going to go here and go create tables just to make sure that we are calling that. And it looks like we are

calling that. And it looks like we are calling that function. So we should be good to go. Okay. So now that we have that, what we're going to do is we're going to make a new commit and we are going to add this to coro. So we're

going to say get add dot and then we're going to say get commit-m and we're going to say setup databases. You can

call this whatever you want and then I'm going to go get push origin main. Okay,

so we've made the change to our back end and now we can push this up. All right.

So, what I'm going to do now is I'm going to get out of this and I am going to go and I'm going to take these values and I'm going to add them as environment variables for my backend deployment. So,

I'm just going to copy these down into a file. So, let me do that and I'll be

file. So, let me do that and I'll be right back. Okay, so I've got all these

right back. Okay, so I've got all these values copied down and now my build is actually finished because it was automatically building when I made that change. So, make sure that you build the

change. So, make sure that you build the latest version of your backend that you've updated to GitHub first. Then,

we're going to go over to deploy. Now

from deploy we'll see this build. Now

it's automatically deployed but it's not going to work yet because we haven't yet added the variables for our deployment or for our database. So what I'm going to do is go to manage configs and secrets here. Okay. So you can see that

secrets here. Okay. So you can see that kind of let me just click it again so you guys can see it. Manage configs and secrets. From here I'm going to go

secrets. From here I'm going to go environment variables and I'm going to start adding the different environment variables that we need. So the first one that we're going to have is going to be the DB host. Okay, it's going to be

this. I'll mark that as a secret. Same

this. I'll mark that as a secret. Same

thing for the port. So we'll go db_port.

Put that in. Mark it as a secret. We

then need the DB user. So we're going to go DB user. Put that in. Mark it as a secret. Then we're going to add another

secret. Then we're going to add another one. This is going to be the DB name.

one. This is going to be the DB name.

Okay, this is the default DB. Mark it as secret. And then lastly, we have the

secret. And then lastly, we have the password. So this is going to be the DB

password. So this is going to be the DB password. Put that in. and then again

password. Put that in. and then again mark it as secret. Okay, so I've added the five values now that we need related to our database. Again, these are just the same values that you had when you created the database. And now I'm going

to go save and deploy and deploy this again with those new values. So now once we're deployed, we should be connecting to the remote database versus connecting to the local database. And we'll go

ahead and test that out and see if it's working. And by the way, just make sure

working. And by the way, just make sure that the values that you have match the ones that you put in the config here.

So name port host password user.

Make sure that again, all those environment variables kind of match what we've put inside of here so that they're loaded in properly. And actually, I just realized that we need to make one more change here. We need to set debug equal

change here. We need to set debug equal to false. So, I'm going to add another

to false. So, I'm going to add another environment variable here. I'm going to call this debug. And we're going to make sure that this is false. And we don't need to make that a secret. We can just make it false because otherwise it's going to be equal to true. And that

means we're not going to connect to the new database. So, I'm going to go name

new database. So, I'm going to go name debug value false. Let's save and deploy that and try it again. All right, so it looks like that is deployed. And now you can see if I go to something like story/2, which was a valid story that we

have previously, it's no longer loading because it's not connected to the same database. It's connected to a new

database. It's connected to a new database. So that indicates to me that

database. So that indicates to me that this is working. Of course, we could test this further by making a new story like let's generate a hello world story.

This will take a second to run, but of course, we can test that and make sure that it's functioning. But I'm fairly certain that we are connected to the correct database. If we wanted to check

correct database. If we wanted to check that, we could look at the logs. And

also, if we go back here, we can see now that this story has been generated.

Okay, sweet. So, that is pretty much going to wrap up this video. I've showed

you how to deploy the front end, how to deploy the back end, how to connect to a database, and how to connect to the OpenAI service. Obviously, Coro is

OpenAI service. Obviously, Coro is capable of doing a ton of stuff and much more advanced things than I showed you in this video. But, I hope this scratched the surface and showed you first of all how to create a really

awesome application that is a great base and template and then how to actually deploy it. so real users can interact

deploy it. so real users can interact with it. Anyways, if you enjoyed the

with it. Anyways, if you enjoyed the video, make sure to leave a like, subscribe to the channel, and I will see you in the next one.

[Music]

Loading...

Loading video analysis...