Transcripts
1. Apollo Graphql ad: Are you tired of traditional
rest APIs and want to learn the latest and greatest
and RPA development look no further than
our GraphQL course. In this course, you will
learn everything you need to know to get started
with Graff girls, starting with the basics of building a Graciela
BI with Express. But we don't stop there. We'll dive deeper into using a border server with
TypeScript and express, working with type RM and using Postgres database with docker. Our course isn't meant to
be a full graph care costs, but it will teach you
all the essentials. You will learn
about Graff girls, halo types, queries,
mutations, schemas, and how to merge schemas and resolvers with a
photograph girl will also learn TypeScript and cover socket IO and a database
migration with diaper. And by the end of the course, you'll be able to confidently build your own GraphQL API and work with some of the latest and most powerful tools
in the industry. So what are you waiting
for then row now for our golf Gail course and take your API development
skills to the next level.
2. Introduction to Graphql: Hello and welcome to this
first lesson on graph gear. This lesson we'll be
introducing Dr. Gale, discussing queries and mutations and explaining what a schema is. So what is GraphQL? Graphql is a query language and runtime for APIs
developed by Facebook. It allows clients to ask for exactly what they need
and nothing more. Make it a powerful alternative
to traditional rest APIs. One of the key features of graph Gil is it's Query Language. The query language
allows clients to specify exactly
what data they need, including nested data structures and retrieve it all
in a single request. This makes it very efficient for clients to retrieve
data from the server. As they don't need to make multiple requests to get
all the data they need. Let's talk a little bit
more about queries. In golf. Gayo, a query
requests for data. It specifies the field that
the client wants to retrieve, as well as any arguments
that are required. Here's an example of a
simple query in GraphQL. In this query, we're asking for the name and email fields for
the user with an ID of 123, the response from the server may look something like this. Now let's move on to mutations. Mutations in GraphQL are used to modify data
on the server. They allow clients to send
data to the server to create, update or delete resources. Here's an example of a
simple mutation in GraphQL. In this mutation we're creating
a new user with the name Jane Doe and email Jane
Doe at example.com. The response from the server may look something like this. Finally, let's talk about
schemas in GraphQL. Schema is a contract between
the client and the server. It defines the types of data that can be queried and mutated, as well as the relationships
between those types. The schema is usually defined using the GraphQL schema
definition language, or SDL for short. Here's an example of
a simple schema in GraphQL schema
definition language. In this schema, we have a query type with
a user field that takes an argument and
returns a user object. We also haven't mutation type with the create user field that takes a create user input object and return a user object. Binary. We have a
user type with ID, name, and email fields. That's it for this
introduction to graph Gail, we've covered what Garcia is, our query is and mutations
work in what the schema is. In the next lesson, we're going to dive into
building GraphQL APIs.
3. working with Graphql Queries: Hello and welcome back
to our GraphQL course. In our previous video, we covered the
basics of GraphQL, including queries,
mutations, and schemas. In this video, we
will be building a simple graph gala
PI will express. Specifically, we will
be using Express to handle our GraphQL
requests and responses. And we will be
leveraging the power of Graff girl to retrieve
data from a data source. By the end of this video, you will have a solid
understanding of how to create the GraphQL
API with Express. And you will be able to apply these concepts to
your own projects. So without further ado, let's dive in and start
building our graph. Gala PI with Express. I have prepared the
project for you to follow along within this video, you can download it from
the resources section. I have included an index.js
file in the project. This file contains
the code necessary to start listening for
connections with Express. In addition to that, you'll find a package.json
file with three dependencies. Express, express, Dutch, Graff, girl, and graph. Here. These are the main
tools we will be using to build our GraphQL
API I would express. Additionally, node money is also installed and configured
in the project. This tool automatically restart our server when changes
are made to the code, which will save us
a lot of time and make the development
process smoother. Now, before we start
working on the project, it's important to make sure that all the necessary
dependencies are installed. To install these dependencies
simply navigate to the project directory in your terminal and run
the command npm install. This will install all
the necessary packages for our project. Once the installation
is complete, we can move on to
building our graph, get RPI with Express. Now in a graph Gil project, the first thing you need to
do is defining the schema, which is the
foundation of a graph. Get RPI defining its
types and fields. We need to start with it before anything else to
ensure our APIs clear, self documented,
flexible, and extensible. To build the schema, we need to import
the function with the name build schema
from GraphQL package. Then create a schema
constant and use the build schema
function by opening two parentheses and pass
in a template string. Here we're going to define our first query by adding
the keyword type like this, and add the named query. Then open two curly braces. And inside here we can
define our queries. So I'm going to define
a simple query hello. Then we need to define the
return type of this query. To do that, add a colon, then specify the return type. Now our case, the hello query is going to return a string, make sure to add an uppercase S. Their types are used to define
the structure of the data, including scalar types
such as strings, integers and booleans,
and object types, which consists of a
collection of fields. Now in order to handle requests coming to
the hello query, we need to define
a root resolver. The root resolvable
is responsible for fetching the data
requested in the query or mutation and return it to the client in the correct
shape defined by the schema. Essentially, the root
resolver acts as a bridge between the API
and its data sources, making it a critical component
of any graph girl or PI. Now to define this
route resolver, let's create a
route constant like so and set its
value to an object. And this object, we need to add all our queries with the same
name written in the schema. So here I'm going to add a
hello property of representing the hello query and set its
value to an arrow function. By the way, every query or
mutation should be a function. Right now, here let's just
return a Hello World message. Now, we need to define a
Raphael and buoyant in our API. We can do that using Express. So it's similar to
defining middlewares. Said the endpoint path
to slash Graff girl. And for the request handler, we will use another function
named Dr. Gil HTTP. We can import it from the package express,
that is GraphQL. Now inside here,
we're going to use the GraphQL HTTP
function like so. An object as an argument. The subject will
hold our schema. You can set it like this or just the name schema
and JavaScript, we'll know that we're referring
to the constant schema. Second property is the
root value property, which will hold
our root resolver. And the last property
is Graffiti Girl, which is a web-based graphical, interactive GraphQL
client that allows users to interact
with a GraphQL RPI by executing queries and mutations and visualizing the results. When set to true in the express GraphQL
middleware configuration, the graphical option enables the graphical interface for testing and exploring
our GraphQL API. With that being said, let's save our file and open the terminal and
run this document. Then let's open the browser and navigate to localhost 4,000, such Scarf Girl, where you will find the
graphical interface. Here we can write our
queries and even check the documentation
of our gov gala PR. Here you will find our query and inside it the hello query, which returns a string. If you click on it, you will see the definition of
the string scalar. Now, to fetch the hello
query from this server, we have to define
the query like so. And then mentioned the
whole query inside it. Once you have done that, click on the Play button to
send the query to the server. If everything is
set up correctly, you should see the
result received from the queries over on the
right side of your client. Great work. In the next lesson, we'll continue to explore that scale by learning
about mutations.
4. Adding Mutations: Now let's add a
mutation to our API. So GraphQL mutation
is a way to modify data on the server
using the GraphQL API. It allows clients to send the request to the
server to create, update or delete data. Mutations are defined in the
GraphQL schema and can be executed by sending a graffiti on mutation query to the server. So let's define the
mutation by going to the schema and add a new type. And I met mutation
with a capital M. And let's assume that we
want the client to add products to our
server side here. That's added mutation handle
with the name add product. This handler will accept a name as an argument
of type string, again with capital S and
a price of type int, which refers to an integer. So in growth Gil, the type int represents a
sine 32 byte numeric integer. It is used to define
fields that return or accept integer values
in graph G 0 schema. Then type can be used to enforce type validation on GraphQL
queries and mutations, ensuring that only
valid integer values are provided as an arguments. We can define this
type as required by adding an exclamation
mark in front of it. We can do the same thing
for the name argument. Alright, now we need to define a return type for this mutation. So GraphQL mutation needs a return type to
specify the shape of the data that will
be returned from the server after the
mutation is executed. So to do that, or the colon
here and add an array, because we want to return an
array of product objects. And to define the type
of this product object, Let's go here and add another type and name
it product like. So. This product will
contain a name of type string with the
exclamation mark to make it required. And then hit Enter to go to the next line and
make sure not to add a comma here because it's not allowed in
drift gill schemas. The second property is the
price of type integer. We can also make the return type requires so we can prevent
getting a null as a result. We can do that by adding
exclamation mark in front of the product
and the array itself. We can also change
the way we listed our mutation arguments
and replace them with one argument and name it input and set its type to
add by direct input. This is a custom type, not a built-in type of GraphQL. Therefore, we need to
define it ourselves. We refer to it as input type. So in GraphQL, the input type is special type used for arguments
in mutations and queries, is used to define
the shape of data that can be accepted
as input by field. The input type can contain scalar types like
string into or Boolean, as well as other input types allowing for complex
nested structures. Input types are similar to
regular types and graph care, but they cannot be
used as return type of a field to define the
Add Product input type. Go ahead here and use the input keyword and
add the same name. And inside it we will
add the same arguments, name of type string
and price of type int. Now let's save this file and return to the
graphical interface. Here we can add our
mutation query. Start by adding
mutation in opening two curly braces are the braces and the
add product mutation. Next, add an input
argument inside an object, the name of the
product that says SPC. Then be careful with the price input as entering
numbers as a string, one to work since we set
the type to integer, instead enter the numbers
directly, like so. The return object, we
want to get the name. And the Bryce. If you hit the play
button, it didn't work. And that's because
we didn't define the add product resolvable
in the root object. That's very quick.
Add a comma here and create the ad product
and resolve or legs. So this resolvable will
accept an input argument. But not directly like this, because the input
argument exists inside the primary object
argument named args. So we can get access to the
input argument like so. Then this resolver should
return an array of objects. So that's hardcoded and add an array with one object
with two properties. Name, we can get the name
from the input like so. And price from input dot price. Then let's save and test again. And there we have it. Our array with one
product object contains the name and the price. We can tell the server to
only return objects with the name property by removing
the price field from here. Hit play. And as you can see,
we got an array of object containing only
the name property. Great, Now that you have a basic understanding
of GraphQL, let's take the next step to level up your
experience by learning how to use a portal graph gail with Express and TypeScript. Apollo GraphQL is
a powerful tool that provides features
such as caching, error handling, and
schemas stitching, making it easier to build scalable GraphQL API ice by combining a photograph Gil where they express
and TypeScript, you will have the
ability to build highly maintainable and
type-safe GraphQL APIs. So get ready to dive into the
award of a photograph Gill, and let's take your graph girls
skills to the next level.
5. Installing and Configuring Typescript: Now before we get into
the TypeScript syntax, we first need to install the
TypeScript compiler to begin had an oval or TypeScript
lang.org slash download URL. And here you will
find a number of ways to bring the
TypeScript compiler into your project depending on what tools you may be
using. In this course. However, I'm going to be
relying on the NPM environment, since that's what I
expect the majority of JavaScript applications
are using these days. Now, regardless of where or
how you install TypeScript, if you don't already have node, you will have to install it
by heading over no js.org and then follow the instructions
to install the latest FTC. Now, if you've got npm
installed in your system, Let's go back to the
download page of TypeScript and copy
this line here. Then we're going to run it said our project and
inside our terminal. So make sure to create
a new directory here, open VS code and run
npm install TypeScript. Does that save that stiff
if you want to install TypeScript inside your project and only inside your project. But it means that
with the desk does saved as div option here, you will only be able to use TypeScript
inside this project. You can do that by, for me, I want to use TypeScript
globally inside by system. So I'm going to add
the flag dash g for global and then
run npm install. And you may need to use sudo to run this
command as an admin. And if you're using Windows, you have to open a
new admin console. Now, I already have TypeScript and stored globally
inside my system. So I'm not going to
run this command. And to make sure that you have installed TypeScript
successfully inside your system, you can run TSE, that's V, and you will get
the version of TypeScript. I'm currently on version 443. And it doesn't
matter if you have a higher version of
TypeScript than mine. Because all the
TypeScript centers that we're going to use in this course is going to be applicable with any higher
version of TypeScript. Now, in order to use the TypeScript syntax
and self-doubt project, we need to add some
TypeScript configurations. We can do that automatically
using the TSC command line, then dies in it. And as you can see here, we have a new file inside our project and named
ts config dot json. So let's open it. And as you can see, TypeScript offers a
lot of options here. Lot of them are commented because we're not
going to use them. Now the first uncommitted
option here is the target, which represents the
sigma script version. In this course, we're going
to use my script six. And if you scroll
down a little bit, you will find the module
property set to comment. Yes. This means that the modules and the
compile dot js files. We'll use the common
tier syntax because almost all the browsers have
support for this module. But if you want your files to be compiled with modern
JavaScript syntax, you can try to use years 2015, like this, ES 2015. But as I said, we're going to use coming to us
in this project. Now under this, we have the
root property which specify the location of the files that uses the TypeScript syntax. So let's end commented and keep it pointing to
the root of this project. Now let's scroll down to
the out their property, which specifies the location
of the compiled files. So let's increment it. And we want all our files are our compiled files to be
located inside the dist folder. We don't need to create
this folder manually inside our project because TypeScript
will do that for us. Okay, Now this is all
we need to get started. So let's save the ts config file and start using TypeScript syntax
in the next video.
6. Basic Typescript: It's time to start learning
about TypeScript syntax. And for that, we need a new
file inside our project. So go ahead and create
a new file and name it main dot ts for TypeScript. Then here we're
going to start with declaring a variable
and name it firstName, and set it to John. Now we just wrote
normal JavaScript. But if I hovered over firstName, I will find that firstName
is of type string. Now, we can find the same
thing with pure JavaScript, but let's try to set
this variable to five. Now, we got an error. If you hover over that error, you'll find that TypeScript
is complaining about the type or the assign a
value to this variable. It says type number is
not assignable to type string because five here is a number and John is a string. So it's like TypeScript
is saying that if you assign this variable from the beginning to a string, you have to keep it like that. This is why TypeScript is
helpful because it prevents small bugs like this from
appearing inside our project. Now, we want the firstname variable to be always
of type string. But let's imagine that another
developer comes in and he changed the value of the
firstname variable to a number, let's say like ten. Now the IRS has disappeared from the second line or
the third line here. But we have introduced
another buck goes here. Firstname variable is no
longer of type string, but it's of type number. Now, you might ask
yourself why TypeScript didn't alert us that we
have made a mistake? Well, it's obvious because we
didn't tell her to do that. So in order to
tell TypeScript to only accept strings
for this variable, we have to add a colon
and then string. Now you can see that
we got two errors. Well, it's the same error, but in two different places. It has the same
message as before. Number is not assignable to
type string the same message. We can find it here. To fix this, we can just remove the ten from here and replace
it with a string. John. We can actually
remove this because it's that variable and assign this variable in
the third line like this. And here we can see that
the IRS has disappeared. But let's just keep
it in one line. So I'm going to take
this again and I'll move the firstName from here
and assign this to John. Now, if you want to declare
a number instead of string, we can create another
variable, name and age, set the type to number, and then sign into any number, going to choose 22. And now you know
that if you do this, you will get an error saying that type string is not
assignable to type number. Now we don't have only
strings and numbers, we also have objects. So let's declare
another lead variable. Name it LPG for object, and then set it
to an object with the property first
name set to chart. Now in the next line, let's try to reassign the firstName property to a number and see what's
going to happen. So object firstName equals five. Now here you can see that
we have an error from TypeScript saying type number is not assignable
to type string. And that's expectable
because declaring object properties is similar to declaring normal variables. And same thing if we try to assign the object to
the firstname variable, we will get the same error. And this time, time type string is not assignable to type than the object we assigned to the variable
from the beginning. Now, let's remove
this and assign the variable type to an object. Now, you can see that we
didn't get any errors. But I want to make it
clear for the eyes that this arbitrary variable
is an object that contains a firstName
property of type string. We can do that by removing
the object from here, replace it with
two curly braces. And inside here are the first name property
and set it to type strain. Now let's say that I want to add a new property to
this subject, e.g. H. We will get an error saying that type object
with property firstName, string, and age number. It's not assignable to the object that has only firstName property
of type string. Now this is going to be
very helpful if we want our objects to only contain
a very specific properties. And of course, if we want to add the age property,
which is a number, we can come here and then
add age of type number, and the arrow will disappear. Now after strings,
numbers, and objects, let's see how we can define
functions with TypeScript. So let's declare a new
function and name it. Create user. This function will
accept two arguments. First one is the first name, second one is the age. Now you can see here
we have two errors. Firstname or the parameter firstName implicitly
has an any type. Now functions arguments
are also variables. And JavaScripts set the type of these arguments by
default to the type any. But TypeScript
doesn't allow that, so we need to add types
for these arguments. So for the FirstName, addString, and for
the age is a number. Now let's return something
from this function. Return firstName plus the h. You can see that we didn't
get any errors because even TypeScript know that we want to convert the age to a string and then concatenated with the
firstName variable. Now, if you change the plus
sign with a minus sign, you would get an error. Because TypeScript
knows that performing a subtraction operation between a string and a number,
it's not logical. So let's remove all of this and replace it with a
literal string. And inside it we
will have FirstName, then add the firstname variable, and then age, the age variable. Create another variable
and name it stl string. And then let's execute
the create user function. Now you can see here that
we need two arguments. So that's first name to
John and the age to 22. Now, if you hover over
the STR variable, you'll find it of type string. We can specify the return
type of a function as we can do it for a variable
using a colon here, and then add type string. Now, let's say I want to
return an object from this. Thanks for that. Going to change
this with object. And we will get an error saying that type string is not
assignable to type object, but the Variable STR
is of type object. Now, let's fix this
error by moving this string and replace
it with an object. The other is gone. But let's say I want
to return this object with an age property set
to the aids argument. If I want to use
the return value of the current user function, which is an object that's supposed to contain
an age property. We will get an error saying that property age does not
exist in type object. Can fix that by removing this, add an object and the A's
property with the type number. Now, if you hover over h, you'll see that it does exist
and it has type number. Let's add also the
firstName inside this object and set it equal
to firstname argument. Here we have an error
because we have to add the firstName property to the return type
of this function. So firstName of type string. Alright? Now let's say that if we
have an aids higher than 20, we're going to reject that aids and only return the first name. So here, let's add an if
statement and check that if the age is equal or bigger than. 20. If so, we're going to return only an object with
the firstName property. Now, TypeScript
doesn't want to let me return this object
without the age property. That's because we told her that this function is going
to return an object with only the age property of type number and the firstName
property of type string. We can fix this by making the age property as not required by adding a
question mark here. Now the error is gone. And if you hover over aids, property will find that h is
of type number or undefined. Now, you can do
the same thing for the age argument
dissatisfaction, but only if this argument is the last one in the
arguments next. Now, if we add the
question mark here, we will get an error saying that object is
possibly undefined. Now, you may wondering why we have upset here, not number. Well, that's because
in JavaScript, everything is a, an
object, even functions. But you can find the type of the age argument as
number or undefined. If we change the location of this argument and put it
from the beginning here, you would get another error. Saying that a required parameter cannot follow an
optional parameter. So if you add a
question mark here, the arrow disappeared
from the arguments list, but you will get many errors in the function and outside the function where
we executed it. Now we don't want this. We want to change
everything as it was. So let me remove this from here, put it there, and remove the question mark
from the H argument. Now, let's say that we want
this function to accept the aids as a number
or as a string. We can do that by adding one vertical bar and
adding the string type. Of course we have to
do the same thing for the return type
of this function. Let me close this to
have more space and add a bar here with
this string type. Now the age here is of type, string, number or undefined. We can do the same thing
for a variable, e.g. for the FirstName here, Let's add either a
string or a number. Or let's do it for the age. So here we have a number. Let's add a string. Alright? Now we can
assign the age variable either to a number
or to a string. Like this.
7. Advanced typescript: Now, going back to the
current user function, imagine if we have more properties
inside this object with more complex types. In that case, it's not
going to be convenient to have a big object in
front of this function. And to fix this, TypeScript has introduced
something called interfaces. An interface is also an object, but you can only use it
as a reference to a type. This is how we can declare
an interface using the interface keyword and then
the name of the interface. And I'm going to choose user. And as I said, we're going to create an object, but without any equal
sign or a column. You can think of it as a class. Now, let's take all of this, the age and the firstName. Cut it from there,
remove the object, remove the colon, and paste them here inside
this interface. Let's adjust the
code a little bit. You can hear use a comma, but also you can use a semicolon or you can just
remove anything from there. But I want to use semi-colons because it looks more
organized in this way. Now, we can use the user
interface like any other type. Here, the colon and then
the user interface. And if you hover over aids, you'll find that
the age still have the type string,
number and undefined. Put in mind that interfaces only refer
to a type of an object. So if you remove the
curly braces from here and just leave
the aids, you'll find. You'll get an error saying
that string or number is not assignable type user
because the user has, first of all, has two properties
and user is an object. And do the changes. And let's create another
function and name it login. This function is going
to accept one argument and it's going to have
the name login ox. It's going to be of type login. And of course we're going to
define an interface login. So let me copy this name. Here is interface and
define the login interface. So in order to, for a user to login, you will need an
email of type string and the password of
type string as well. Actually, I want to have
control of the password type while using the login interface inside the login function. To do that, we can change
the center face to be generic by adding two
angle brackets like this. And then you can think
of this interface now as a function that will accept
an argument as a type. And we can choose the
name of the argument. I'm going to choose
PWD for password, and I'm going to assign
that type for the password. Now, here, if you
hover over login, you will find that
generic type login requires one type argument. As I said, functions
has arguments. So for the generic type
is the same concept. Now here we can define
that argument by adding a angled brackets
and the type of the password inside
it as a string. Now in satisfaction,
let's very quick check if the login args dot
email equals e.g. he made and the login args dot password equals e.g. password. That case, we're
going to return true. So the user is authenticated. Now here, else, we're
going to return False. Going to set the type of the return value of
this function to body. And this is a new
type that we can use. And it doesn't need a definition because they boolean
is just either a true. False. Now, going back to
the login interface, we can use another variable
that only refers to a type and holds the
password type for us. And because the password
is not an object, we cannot use the
interface keyword. We only can use the
type alias and then name the type to
be password type. And then we can add these
string to that type. So here string. Then we can use
the password type inside the login interface. If you hover over
it, you will find that password type is a string. You can add here either
a string or a number. And you can add
any type you want. But passwords can only
be strings or numbers. We can also use the type
alias to add a type to a variable or to a
return value of a function. So here I'm going
to add type login, result equals volume and
make sure it's uppercase. And here for the body, and we can use a lowercase
or an uppercase. It's the same thing. Let's change this
to login results. Now, imagine that we
have an async function. We're going to use the async
keyword to define that. In this case, we got an error saying that
the return type of an async function
or method must be the global promise generic
with the T argument type. And then he says, Did you mean to write
this generic type? Now, if you know JavaScript, you will know that
an async function will return a promise. And this is how we can define a promise type with TypeScript. So let's change the
login result to be a promise because this function is going to return a promise. And then the generic
or the argument of this generic type represents the real return type
of this function, or in another word, the resulted value, or the type of the
resolve that value. So here we're going
to resolve a Boolean. And you can see that
the error is gone. Now, let's say that if we
authenticated the user, we will send an object
instead of true. And this object will contain
a username property, said to John again, and another property
status set to active. So we have an active user
inside our database. Of course we don't
have database here, but just imagine
that we have one. Now of course we're
going to get an error. And to fix that, let's add an
interface here and name it, number, and cite it. We will have the username
property of type string and the status property
set to string as well. And here we will get
a promise that will resolve either a
Boolean or a number. So have made a mistake here. Right now the Earth
has disappeared and we can return either
a force or an object. Now about the status property, we can only return three status, the active status,
inactive status, and the new status. So here you have to always
return one of these strings. But if I wrote active like this, in this case, I made a typo. But TypeScript
didn't say anything. Because TypeScript doesn't know about these strings only cares about the type of the status property
being said toString. And as you can see here, I already made a typo. So how we can prevent these
type of errors or mistakes? Well, it's very simple. We have to declare an enum type. So here, Use the NM
ideas and then name this enum type member status. And it's going to be an object, but it's not going to
refer to an object. I will tell you how we can
use it in just a second. Now here we can define
these three types by adding active and inactive. And then the new status. Now we have to replace
this string here with the members status enum. And for that, we have to use numbers status enum also to 2s to select
the active status. That active. You can see here that VSCode
has the three types here as a success since this is very helpful to speed up the
development process. Now let's choose active. And if you hover over it, you will find that members status that active equals zero. Now, you can refer to the active status as a
string by adding equal. Then active. Same thing for the inactive. Here, string, copy inactive. And for the new also knew. Now, if you hover over active, you'll find it active
equal active as a string. Now inside the member interface, we can only accept one
of these properties. So now we will not be afraid from misspelling
one of them. Now, let's save this file and let's open the terminal
and try to compile. We can do that easily by using the TSC command and
just hit Enter. The TSC command is
going to check if we have any errors or any
type script errors. And then if everything is okay, we will have a dist folder
containing the main.js file. If you open it, you will
find here a lot of code. But all the types from before, from the main.js file has disappeared and we only
have pure JavaScript. You can see how TypeScript
compile the enum type. And did all of this for us. Skulls, the main
dot, dot js file. And now you can say that you learned pretty much
everything about TypeScript. Before finishing the video, let me very quickly
show you how you can use the login method. So here, take the
non-game and pass the object with the email set to email, password, to password. And then because this
is an async function, we're going to use then
method to get the result. And then we're going to check if the status is active or not. And to do that, we're
going to create an if statement and check
first if the result is an instance of
the class Boolean. And by the way, the Boolean with the uppercase B is glass. And it is a whirlpool for the type Boolean with
the lowercase b. So this type here refers
only to a true or false. But the Boolean with
uppercase B refers to a class that contain
the type Boolean. So I'm going to do this. If it is an instance of
Boolean, then return. Otherwise take the
result that status. Check. If this is, this status is equal
to login or a member. Status active. If so, we're going
to return true. Or maybe console log. User is active. User is active. Now if you want to use
body and with lowercase, change this and
you can check for the type of Boolean with type of keyword and then result equals to
ensure its volume. And if you want to
do a check like this with the exclamation mark, you can remove the
Boolean type from here and replace it with null. And instead of returning false, going to return null. This is better. And you can
see that null also is a type. Now you will find
this file attached to this video with other
TypeScript resources. So make sure to download them and learn more about
TypeScript in your own.
8. Creating The graphql Schema: I'm inside VSCode and
here I have two fives, the ts config file and the
bank has the JSON file. Now for the ts config file, we have some new options here, like the library is
set to yes, 2021. And the module
resolution also set to note with the types
also set to note. Now, for the strict property
initialization option, make sure it's set to false or you can just remove it
from your ts config file. Now, close this and by the way, I will add a zip file to this video so you can find it in the resources and downloads. Now we're going to add
some packages from MPM. So here I have the
terminal from the VS Code. You can open it by clicking here under terminal and
click on new terminal. So here I'm going to
install new packages like the express with the
types for express. And we're going to work with
Apollo server with express. So I'm going to install
a polo Server Express. And of course we need GraphQL
and the types for GraphQL. So here at types
slash graph gear. And just in case I'm going to
install the types for note. Now let's run this command and install all of
these packages. Alright, so now let me clear this terminal and inside
the root directory, let's create a new
source folder. And inside here we
will have a main.js file and a module dot txt file. And for now we're
not going to touch these two files because we have to create the GraphQL
schema first. And for that, make sure
you are again inside the root directory and create new file and name it schema. That Raphael. Alright, now here it's GraphQL schema consists
of mutations and queries. We use mutations when we want to change data inside the database. And we use queries when we want to get data from the database. You can think of
them as posts in requests if we were
using a RESTful API. With that being said, let's
define our first mutation. And to do that, we have to use the type keyword and
then write mutation. And inside here we will
have the sign-up mutation. So right, sign up and sign up. It's going to accept
the user input. So here we're going
to write input as a argument for this method. And we have to define the
type of this input so that when we use this
mutation with our API, we will know which arguments are required and which are not. And define such type. We have to use the input
keyword here, right? Input, and then name this
input type Sign-up input here, and make sure you have an
uppercase S here for us, for its type or it's sharp
input and it's not required, but it's a common thing
to do with GraphQL. So here, sign-up input side here we're going to
define the sign-up arguments. And by the way,
you might not have the syntax of GraphQL
colored like this. Because if you want that, you need to install a
extension for GraphQL. So head over extension
section here and search for
GraphQL extension. I have it already
installed here. So make sure to install
the same extension. And then you may, you may have to create a config file after
installing this packet. So scroll down here and you
have to create a dot GraphQL fc dot YAML file and
copy these two lines and paste them
inside your project. And you can see here
that this config file, our start targeting the
older GraphQL files inside our source folder. In our case, we
don't need that yet, but in the future, we're going to have
multiple GraphQL files. So you may need to
also copy this line and paste it inside
the config file. And I will also do
that in my side. Just to make sure that we're not going to
forget this in the future. So go back to our project
and in the root directory, create a new file and name it dot GraphQL LRC than the GMO. And here, paste that
code here and close it. And then you will have the
same styles for GraphQL. And not only styles, but also autocomplete. Yeah. That's why I recommend you to install this
extension and use it. Whenever you create a graph
kill schema just to prevent typos and unnecessary debugging if you made any mistakes here. Yeah. So just to make sure I have the GraphQL inside the
root directory in here, Let's continue with
the sign-up input. So to create a new user, we need an email of type string. And make sure you have
an uppercase S here. And also make sure
you don't have any semi-colons or comes here. And now define also the
password of type string. And also we need a first-name
of type string as well, and the last name
of type string. And now let's use this type
here for the sign-up input. And as you can see here, it's, it's similar to the
TypeScript when it comes to defining and
using types. Yeah. So you might not have
any difficulties in understanding the
GraphQL syntax, right? So now let's define the return type of
the signup mutation. Now, whenever we sign off, we will get back the user, the user document,
and the JWT token. So let's define that. And this time we have to
use the type keyword, because the input keyword is only used when we want to define the inputs for
mutations or queries. And the type is used to define mostly the return types of
mutations and the queries. Now here define a type and
name it earth response. And here we will have a user. And then this one is
going to be of type user. And we will define
this in just a second. And here we need also the
JWT token of type string. Now, on top of all of
these are going to define the user typed in. We have to define the ID
as well for the user, because when we start
using the database, we're going to return the ID. Yeah, so we have to define
that otherwise will not, will not get the id from the GraphQL schema when we
request for the user document. So the ID here. And because we're
using the type rep, we will use diaper
around. With Postgres. The id is going to
be of type number. But in GraphQL, we cannot use, we don't have a number
type like this, but we have an N-type. Yeah. And just to make sure
that we're going to get the ID every time when
we request for the user, we will add an
exclamation mark here. And with this symbol, we can define a required
field inside our schema. Now, the user also have
all of these here. So let me copy this
and paste them here. And that's it. Now let's use the user type here or the author
response type. So here, off balance,
and then save. Now we have to make sure
that the sign-up input also required to prevent
getting undefined inputs. And the same for
the earth response. Yeah, and now we have
to define our schema. And to do that, I'm going to use
the schema keyword. Then cite this. We're going to
define the mutation. And it's going to be
of type mutation. Then when we create the
query, the queries, we can come here and create query like this and then
give it the query type. But for now we only have
mutation and make sure that at least you have
either a mutation or query here or both of them. Otherwise, you are the GraphQL
gateway will not resolve. Now, let's save this and create a resolver that will handle
these sign-up logic.
9. Create Auth Resolver: Now that we have our
GraphQL schema defined, Let's create a resolver. And because we defined a signup mutation that is
related to authentication, we're going to create a folder
inside the source folder. And inside it, we're going
to have a resolvers file. And here we're going to define the object that will contain our mutations
and queries logic. So create a new constant
and name it as resolvers. And it's going to be an object
with a mutation property. And inside that, we're going to define the sign-up method. And of course they sign up
method is an async method. Now it's GraphQL
resolvers going to have a parent argument and
an object containing the input value and also an argument or a
constant contexts argument. We're going to explain the context argument when we define the main and the module. But for now, we know
that we're going to get an input from our
sign-up resolver. And all the arguments inside the sign-up input is
going to be defined here. For the parent argument, we're going to have
the return value of the parent resolver. And that's only when we
have a nested resolvers. But in our project we're
not going to use that. But I can, I can
show you an example. So imagine if we wanted to
find a signup mutation, but not in this way. We will create another
type and name it, sign up. And here we will have
like a with email. And this method will
return the set of user. And we will also have, like with Google,
sign up with Google. And this one will return
a token of type string. So to use this, we can also add the input
to the width method here and then remove
these parent disease. And here the signup mutation is you're going to
be of type sign up. And with this, we have defined a nested resolvers inside
the author resolvers here, we have to define the
signup mutation like this. And we will only return
like an empty object. And then outside the
mutation object, we're going to have
a sign-up type. So create a sign-up
property here. And inside it, we're going to define the width e-mail
and with Google methods. Yeah, So here with email and then it's
going to be a method. Of course it's async. But now we can get the parent. Yeah. And in our
case, this example, the parent is going to
be an empty object, but this is how we can get the nested result
of how to build nested resolvers and use
the parent argument. But like I said, we were
not going to use this because it's over killing. In my opinion. We just want a simple resolvers. Let's return everything like before and remove
the sign-up type. Alright. Now, inside the Earth resolvers, you can see here that we have a type script errors
saying that all of these arguments here doesn't have types and that's not allowed with
TypeScript, as you know. In the next video,
we're going to learn how to automatically generate TypeScript types from the schema dot GraphQL file.
10. generate Typescript files from schema: Now, in order to generate automatically type struck
types from our GraphQL schema, we need to install
some new packages. So head over your terminal
and run npm install dash, uppercase D for dev dependency. And then Right the bucket name, GraphQL, that's
cogent slash CLI. And also we need some other
plug-ins for this package. So again, co-gen. And we
need the TypeScript plug-in. And also again,
GraphQL slash or a dash co-gen slash
TypeScript resolvers. And run this command. And now inside our
package.json file, we're going to create
another command here and name it generate. And we're going to use
the GraphQL coaching. And then we're going to specify a config file and name
it cogen dot YAML. Of course we don't
have this file yet. So let's created inside the root directory and
save this config file. We're going to specify
the schema path. In our case it's inside
the root directory, then Schema dot GraphQL, and then under generates. We're going to save our generated types
inside the source folder. And then side generated folder. And then say the resolvers
types dot txt file. And now we have to
specify the plugins. So plugins, we installed
the TypeScript plugin, and we installed also the TypeScript resolvers. And now we have to add some configurations
related to TypeScript. So I'd use index
signature to true. And this option here allows us to generate dynamic
TypeScript types. Now add a colon here
and save this file and go back to the package.json
file and save it as well. Then let's run this command
from the terminal npm, run, generate, and wait for it. All right, We have
successfully generated our divs inside the
resolvers types. You can see that
we have our user, our author response here in our mutation signup input types. Yeah, user is here and
everything is declared. Of course there is a lot of configurations and other
types related to graph girl. Yeah, so let's close this file now and go back to the
author resolvers and import resolvers from the generated resolvers. Let's close this file
and let's go back here. And so they all three solvers and use these enumerated
types that we have to import from source or from
the generated folder. And from resolvers types file, we're going to import resolvers. And we have to implement
that type here. And now we can see that all the TypeScript
errors has disappeared. Because now
TypeScript knows that the mutation property
has a sign-up method. In the sign-up method is a GraphQL resolvers
and its draft, your resolver has a parent
has an input and a context. And now if you write input, then you will get
suggestions for the from the sign-up input type. Now, let's continue building
the sign-up method. And I'm only going to
return and dummy data here. So we need to return an ID. Or actually you have to
return a user property. And inside it, we will have the user ID and it's
of type number. Let's add ID1 and the e-mail. Then let's just add
a random email here and the return the first name and the last name. This is just for testing
because we should start working on the main and the module files and
test our results. Right now, let's
add the last name. It's going to be
just user and safe. So actually we have to
use the input here. We have to return back the input coming from the
user, from the client. So I'm going to spread all the properties inside
the input argument. And you can see
that we don't have any errors from TypeScript. So it means that we're returning the same properties that
exist to cite the user. So you can see that side, the schema dot GraphQL, we have email password, FirstName, LastName is
that the user type and the same inside the sign-up inputs. That's why we don't
have any errors. Now, we still need to
return the JWT token. And this one is going to
be just a random string. Now, if you noticed, we didn't get any errors when we was missing the JWT token. Yeah. And that's because Saturday
schema dot GraphQL, we didn't add an
exclamation mark here. So we have to add
this or to make these two properties
required and save this file. And now we have to run again
the generate command so we can make sure that
the author resolvers. We are returning the JWT
token with the user property. So here we have
generated the schema, and here we go we have a TypeScript error saying
that we are missing the DWT. So here if we added
the DWT property, the Earth will disappear. And for now we're going to
just send some dummy data. Then after we are finished the
main and the module files, we're going to get back here and finish all the
mutations and queries. And that's because we still
need to connect to a database and create the user entity
inside that database. So with that said,
the next video, we're going to start working on the module and
the main files.
11. Setup our Apollo-express server: Now inside the
module, the ts file, we're going to set up our Apollo server to work
with our GraphQL schema. And the author is solvers. So let's start by
importing few packages. First one is the Apollo server from Apollo server express and import express
from Package Express. And we also need the
http from HTTP packet. And you don't need to
install it because we have it by default with NodeJS. Alright, now let's export and create a new class
and name it app module. Inside this class
we're going to have a constructor that will
accept a resolvers. So here create a public
variable and name it resolvers, and it's going to be
of type resolvers. We have to import that from the generated folder and from
the resolvers types file. And here we're getting an
error because we have to import and use
resolvers, not resolver. So add an S there. And here we're going to create a method that will initiate
the Apollo server. So icing start Apollo. And this method should return. Of course it's a promise, and the promise should resolve an object with HTTP server, property of type HTTP server. And we have to return a server or property of type
Apollo server. Now let's create a constant
and name it server, and use the Apollo server class to create new Apollo
server instance with some configurations. So here we need to pass our
GraphQL schema and resolvers. And I'm going to
start with resolvers and going to pass
this variable here. And of course, we
are inside a class, so we need to use the this
keyword and then results. Now for the schema, we have the type
deaths property. And we need to take this file, the schema dot GraphQL, and pass it here. And for that, we're going to use the read file sync method
from our from alphas packets. So here Read File Sync
and imported from fs. And before this, the server, we're going to create
type deaths constant. And here we're going to use the Read File Sync to read the GraphQL schema from the schema
that GraphQL file. So let me delete this. And here, let's continue. We need to specify the path. And because we have this schematic GraphQL file
in the root directory, we can write it like this. Schema graph girl. And we will read this file with encoding set to UTF eight. Because without the
encoding option, we're going to get
back a buffer. But here we need a string. So remove the colon and just give the type
deaths like this. Now we have to start the server, so await server that start. And then we need to create
or use the apply middleware. And here we're going to
add an express medulla. So let's first create an app with Express and
use it here as a medieval. Ok, now, we need to
return a HTTP server. But the app here is
not of type HTTP. That server is this
is of type express. So we're going to use the HTTP packets to create an HTTP server
using the app application, I mean the Express application. So here, create a new constant
and name it HTTP server, and then use http dot, create server and
pass the up there. Then here, that's returned the HTTP server with
the Apollo server. Now, we have to initiate a new instance and exported from
the module file. So here, export
constant up audio and then create new
app module instance. Here we have to
pass the results. So we have to import first
the author is overs. So here import our resolvers, and I think we didn't export it from the
author's ogres file. So let's do that here. Export, export. The author is solvers. And then let me copy this, go back to a module and
import the author resolvers from go out or inside the Earth folder and
inside the author results. Now, let's go back here and
use the resolvers here, and then save this file. And in the next video, we're going to start working
inside the main.js file.
12. Run and Test our Api: Inside the main.js file, we have to import
the module from the module file and then
create a bootstrap function. And it's going to be async. And inside here, we will
extract the HTTP server and the server from the module
that start a bottle method. And then we will use
the HTTP server to initiate or start listening for connections are
both for thousands. Then we will return a message
to the console saying that server is ready at
http localhost. And then the port 4,000. And here we need to get the path of the GraphQL gateway
from the Apollo server. So I'm going to append here another string
from the server, that GraphQL path property. And then we will get the a
path for the GraphQL gateway. Alright, now, we still have to execute the bootstrap function are called the
bootstrap function. And now let's save the
main.js file and head over package.json file and add
some scripts to run our app. So here, create a new
start command and we have to compile
our code and then run the main.js file. And this time we're
going to use the dash, dash what option with no Ts, instead of using
Node man packets. And in order to use this option, you have to have the
latest version of notes. Yes. So I have the version
19 should have at least 18. So makes sure to update
your Node.JS version. Then here, let's the path
to the main.js file. So this folder, then source folder and
then the main dot js file. And now let's take the TSO command here and
put it in another command. So here, create a
compile command and paste the TSC there. Then here we're going to
run npm, run compile. So we will compile our code
and then run the main.js file and watch for updates
or watch for changes. Now let's save the
package.json file and clubs. And here in the terminal, Let's run npm, run start. And we have an error. And that's because we need to
add a query to our schema. So for now we're
not using queries, but I'm going to add
very simple query. So here, under mutation, going to run or create
new type query inside it, I'm going to create a query
that will return a string. And here the schema
define a query. And then here also
query and save. And this time we
need to regenerate the types and create
a resolver for that. So npm run, generate. And then inside the
author resolvers. Let's create a query
here and get method. So it should be a property
and then cite it. We will have the get
method and we will only return a string. So I'm going to send back
a LK string and then save and run the
application again. Here, npm, run, start. Alright, now we
have our server at localhost 34,000 slash GraphQL. And here it seems that
we added a slash, but we will fix that later on. Now let's go to our browser and navigate
to localhost 4,000 GraphQL and click on Query your server so that
we can get access to. Photo Studio. And from here we're going to test our mutations and queries. Alright, now we are inside Apollo sandbox and under root here you will find the
query and the mutation. Now, we will start with dusting the mutation and
tried to sign up. Of course, we have a dummy data, but we will only test the
GraphQL gateway here. So to add the sign-up query, click on the plus icon and you will get the mutation and then the sign-up with the input. And this is how we define
our mutation from the light, from the front end
or from the client. So here, the input here is a variable and it's of
type Sign-up input. So we're getting this type
from the scheme, of course. And here we are defining the signup mutation
or the sign-up method. Yeah, So this is just the
type of the mutation, and this is the actual mutation. And we are adding or setting the input argument to be
equal to the, this variable. And we can, ah, edit the input
variable from here. As you can see here we
are inside the variables section and here we can define all the
required variables. Now inside the sign-up method, we will also define the return type and
Saturday schematic GraphQL. You can see that the signup
mutation will return a response and the other spouse have a user field
where the JWT field. So here we can specify which
property we're going to get. So in our case, we're going to get user and we only interesting and the id. And then we will get x, or we are waiting for
the DWT. And that's it. This is the cool
thing about GraphQL. Allows you to decide which data you want
to get as a response. So rather than returning the whole user document with the email password,
firstName and lastName, we can tell draft girl
to only send back the ID and also the JWT token, of course, because we will use it to
authenticate the user. Now let's run this query by clicking on Sign
Up button here. And of course, we forgot
to add the input. So this request will fail. Says that variable input
is not, must not be null. So here, let's change that. And instead the arguments
here, arguments section, we can find the input of
type Sign-up input and you can add the necessary
types here. So let me clear all
of this in email, password and
firstName, lastName. All of them are required. Now for the email
going to choose just the user one at email.com, and for the password, going to send just password, firstName, user one, and
last year last name, user. Now let's send
again this query or this mutation and wait
for the response. All right, we got the ID of one and the JWT token
as this string here. And you can verify that this
string is the same string we defined inside the
return statement here. So the JWT or property. So we have our GraphQL gateway working with Express
and Apollo server. And let's very quick
also test the query. Yeah, so we're going
to run the get method, makes sure to just leave it
there and add the query get. So add this here
in the getMethod. And for this query, we are not expecting
to get any inputs. Therefore, we can just write the get method like
this without parentheses, n without any arguments, then execute the query and
we should get, okay, String. Alright, we have this here. And now we know that all
our queries and mutations are working and that we
can execute our resolvers. Now, we have to
finish working on the signup mutation and also finished all the resolvers here. But before that, we need to use type around to create a
user entity so that we can create a user and
save it to the database.
13. install and configure typeorm: In this project,
we're going to use type round to manage
our database. There is a lot of
organisms out there, but the most popular
one is type ramp. And that's because it
has a lot of features and it's easy to use,
especially with TypeScript. Now an OR is an Object
Relational Mapper. You can click on this
link here and it will lead you to
a Wikipedia page. And you can read more about object relational
mapping here. Yeah. But TypeScript or our type ramp altogether
stands for TypeScript, object relational,
mapper, and type around. It's a library that
makes it easy to link our TypeScript applications
up to a relational database. And in our case, we're going to use Postgres
as our relational database. And of course, diaper amps supports a lot of
other databases, even MongoDB, but it's still
in experimental stage. So if you want to use
MongoDB, you can do that, but you have to add a
lot of other setups. Because as you know, when we use Mongoose, we had some difficulties
when we want to implement TypeScript
with Mongoose modals. Yeah, So the same thing
happens here with diaper m, but we have a lot of other
options like Postgres, C-Corps, and my SQL or sequel light and all other databases. Now you can navigate to type parameter title and learn
more about its features. But of course, in this project, I'm going to teach you
how to install type Rahm, how to use it, how
to create an entity, what type around, and
a lot of other stuff. So let's open our VS Code. And inside the terminal, let's start by installing type around and the Postgres driver. And also we need another
package named reflect Meta data because we're going or type or M is
using the characters. And to use the characters
with TypeScript, we need to install the
reflect me the data here. So this is me that data. Otherwise we will get, we will not be able to
run our application. So make sure to install that. And also if you didn't
install the types for note, make sure to do that. Yeah, I have installed
that from the first video. So I'm going to run this command and wait
for the installation. Alright, now inside
the main.js file, we have to import the reflect meta-data Beckett in order
to use the characters. So let me copy this. And the reason why
you choose main.js is because it's our main
file for this project. So I'm going to import
everything from the import. They reflect me that
Theta brackets here. And we have to add some
configurations here. And so the ts config
that say some fun. So we need to add
two more properties. First one is the emit, the characters, metadata
and set to true. And also the experimental
decorators also set to true. And these properties are
very important when we want to run or use the characters
inside our application. Now if you don't know what, what is the characters, we will learn about them when we start creating our entities.
14. Create user entity: Now that we have type
ramp inside our project, Let's start by creating
the user entity. So inside the folder, create another folder and
name it user side here, create another
folder named entity. And then inside
the entity folder, create the user, that
entity, the ts file. Now side here, we're going to import the character
entity from type wrap. And then we will export
a class name, a teaser. And here we're going to
use the decorator entity. And this is how we define
entities with diaper m. Now, the characters are
just simple functions. Yeah, That's returns
another function, but also uses a very
specific arguments and they need the reflect me
the data package to work. Yeah, So if you want
to create a decorator, you can search online, but I will give you a
very simple example. So imagine we have entity here. Imagine that we wanted to create an entity, the character. So I will do, I will create a constant
and name it entity. And then I will
create a function. And this function has to
return another function. Now, the second function
here should accept a target and accept
a property key. For Tiki can be of type string and also a
something called distributor. This grip there. And this one is of type
property descriptor. Of course, we should not use the function
distributor for a class. That's why we have
an error here. Yeah, but you can
learn more about the distributors inside
the documentation. By TypeScript here, can read
more about the characters. So copy this link and you can know a lot of things here
about class distributors, e.g. here, how we build them here. So here we create a function
with a constructor. The constructor is
just a function. And then we use a lot of
JavaScript methods here to handle or to extract
the properties and the methods from the class and do
something with them. Yeah, Now, make sure to, if you want to read
more about them, make sure to come here and e.g. try to build your own tools
or your own decorators. Yeah, but for now, let's remove this and continue
with our user entity. Now, we need a first-name
inside our user entity. So here, write first name. And this one is going
to be of type string. And make sure it's a
TypeScript string. I mean, with a lowercase as. And we need to use
another character here. And this character
is called a column. So column from type
around and use it here. Column. Now we have the firstName, the lastName, last
name of type string. And of course going to
use the column again. And we need the email
of type string as well. And then the column.
And the cool thing about type where m is, even if you used a MongoDB
or any other databases, you can define,
you have to define the entities always in the
same with the same structure. Except for Mongoose. Because as I said before, it's still in the
experimental stage. Yeah. And it was there
for a long time, but you still can work with it, but you have to create a schema. We cannot create an entity
with Mongoose with diaper. Now let's continue
with the user entity. We still need the password, and it's always
type string here. And I'm going to add a
configuration for the password. And to do that, we can
pass a object here and say or a property, select and set it to false. And this mean that we don't want to we don't want the password to be returned to the client. Yeah, we we want to hide the password and this is
how we do it with type. Now we can add a lot of
configurations, e.g. for the e-mail here, we can. I mentioned that we don't
want the image to be not. So we use the nullable
property and set it to false. So the email will not be, No. This is like a validation
with diaper n, but we're going to use another way for validation,
more advanced way. Yeah. Now we still, still
need the ID here. But because the idea is automatically generated
by posteriors, we're going to use another
decorator to define the id and that the character is called
primary generated column. And here we will
use that decorator. So primary is generated column. And here we will define the id. And as I said for PostgreSQL, we're going to get
ideas numbers. Now let's save this
and the next video, we're going to create
the user service and start using the user
entity. So see you there.
15. Create-user method | user.service: Now inside the user folder, let's create another file and
name it user service tiers. And here let's export and
create the class user service. And then going to
add a constructor. And inside it we're
going to define a public variable and
name it user repository. And it's going to be
of type repository. And we're going to import the repository interface
from typewriters, so forth, repository
from type ramp. And here at repository. And we have to pass
the entity here. If you hover over this, you will find that it's required in order to use
it inside our class. So here we will add
the user entity and we have to import it from
the file user entity. So for each user from the user entity or
inside the entity file, then use or that entity. Now, we can start by
creating the first method, which is the create method, where we will create a new user. So Async create. And then here for the, for the arguments, it's
going to be a sign-up input. And we're going to use the automatically
generated types here too. The type for the
same input here. So let's assign the sign-up
input argument to sign up input, interface and type. And we're going to import that from one level out
and then two levels. And Site Generated
going to select the resolvers type and
the sign-up input. And if you hover over
the sign-up input, you will find all our
required properties here, like the email first name and
last name and the password. Of course, without
the ID because he's, It's
automatically generate. And here fix the async. Now, first thing we
have to do inside the create method is
hashing the password. For that we need the
another package. So let's open the terminal. And I'll stall the
Beckett named decrypt. And we would use it to
pass our password and also compare the passwords when we want to
sign in the user. So here, npm install the crypt. And we also need to install
the types for, be correct. Then run this command. Wait for the installation,
and that's it. Let me clear this and
close the terminal. And here, let me abort the
Big Rip class from the crypt. And here we're going to create new passwords,
a hashed password. Wait for the encrypt. That hash. And side here, we're going
to pass the sign-up input. So sign up here. Input that password. And then we need to
choose a salt rounds. So here, going to choose
ten as the Salt rounds. And here we're getting an
error because the password, if you hover over this, you'll find that all the
properties inside the sign-up input type is not required. To change that. We have to go
to the schema that GraphQL. And aside here add an exclamation mark on
all the properties here. And let's do the same
thing for the user. So we need to only
for the password. Yeah. Because we were not
going to send it back. And then it sets,
Let's save this. And we need to open the terminal
and generate the types. So here, npm run, generate. And that's it. Let me clear this or
just close the terminal. Go back to the user service and the error has disappeared. And now the password is of
type string before it was of type maybe or
something like that. They will use another interface or a class here to define
not required properties. But when it is required, we're using this type here, but it's automatic, degenerated. We don't care about that. Let's now continue
building the user. Now, create a user constant, then use this dot
user repository and then create method from
the user repository. And here we're going to spread the user or the sign-up
input, sign up input. And then we're going to
add the password here. So I know that inside the sign-up input we
have already a password, but when we spread the older properties like this
inside an object and then adding another property with the same name that exists
in the sign-up input. We're going actually to
update that property. And we're going to have the password set to this value to the
HashSet password here. Now, let me, we need
to save the user. And to do that, we need to use this user repository
dot save method and pass the newly
created user document. Of course, we're going to return this document to the client. So it's returned this
and wait for it. And then save. Now in order to use the user service inside
the Earth resolvers. And because the author is
solvers, is an object, we cannot add a public variable and set it to the user
user service type. So we have to create
an instance from the user service and export it so we can use it
inside the author results. Yeah, so, right, export, then constant User Service and going to add a new
instance of user service. And here we need to pass
the user repository. And for that, we need to
create the data source. And from the data source, we're going to get
the user repository. We will do that in
the next video.
16. add app data source: Now let's create
the app data source and then come back here. So inside the source folder, create a new file and name it, does data source, the Ts. And here we have to import the data source
class from diaper. And here we have to
export a constant, name it up data source. And it's going to be equal to a new instance of
data source class. And here we are going to have
the type of the database. And in our case, we're going to use the
Postgres database. You can see here that we
have a lot of databases, as I said before, have my sick or have a lot of other databases
like MongoDB here. So for us we're going
to use Postgres. And you have to choose
Postgres in this project because then after
this we're going to use Docker to run Postgres database inside
our local system. Yeah. So if you want to
follow this course, you need to also
choose Postgres. Now, we need the host for
the database and our case, we are going to run the
database at the local host. And we're going to
choose the port. And for Postgres, we
have the port 5432, and we need a username
for the database. All of these information
would be also added, are used when we initiate our Postgres
database with Docker. So here the username
going to choose Admin. And for the password, I'm going to choose also admin. And then the database name, it's going to be main database. And now we have to
define our entities. The entities. It's an array that contains all
the entities files. So you can specify the
path of its entity. But there is a better way
to do it is by first, we have deselected dist
folder and then add two stars for to choose or to
search inside any folder. And then add one star here, then dot entity that G has. Now, by using this path, we're going to select all the files inside the
dist folder and inside any folder that exists
inside this folder that has a dot entity at the
end of its name. Yeah. Now, still need more
properties like the logging. We don't want to get a sequel queries
inside our terminal. So for logging, I'm
going to choose false. And we need to add the
synchronize option here. But make sure to
set it true only for when we develop our
database or our project. And you can read here the
documentation of this property. And that is a
warning that if you use the option and
the production mode, you will lose like you
will use production data. So basically this option here, we'll update the database. And because we're
using Postgres, it's a relational database. It will, the
subsequent will update the database tables
between all the entities. But this is not a safe
way to update the schema. Therefore, we will only use it. Use this option here. Then we will work with, we will add make patients, and we will get rid
of this option here. So for now, let's keep it set to true and go back
to the user service. And we have to import
the data source, data source from
the one level out, two levels than the update. So it's there and we
will use it here. Data source, then
dot Git repository. And we have to add the target. And the target is
an entity type. So we have to pass
the user entity here so we can get
the user repository. Now in the next video, we're going to start working
with the author is solvers. And we're going to finish
this sign up or not finished, but start working on this sign-up method.
So see you there.
17. Create signup resolver: Now inside the signup mutation, Let's remove this code here. And first thing we have to
do is creating a new user. So constant user than wait
for the user service. And he can afford that from
users slash user service. And then we're going to use the create method and pass the input or
the sign-up input. And for us, it's just
the input argument here. Now, the return type of
the signup mutation is object that has a user
field with the JWT field. So we need to
create a JWT token. And for that, let's
open our terminal and install npm JSON Web Token. And of course we need the types, types JSON Web Token. And let me clear this,
close the terminal. And here we have to generate a JWT token using
the JWT package. Import JWT from JSON web token. You know how to do
this, we have to use JWT than the sign-in method. Here. We will pass the payload. And this time I'm going to
add a payload with email from the input that e-mail and a
user ID from the user ID. And we need a JWT token. So we have to create and dot length file and add the
JWT token variable there. So in the router
level of our project, let's create a file. And inside here a JWT key, and add some random characters. And then save. Then we need the
package that will read the files for us inside
a TypeScript project. And that's the brackets. So npm install dot. Then that's, that's
safe. Not save dev. Let me clear this now. Close the terminal and
we're going to use the dot m that gets
inside the main.js file. So here I import
everything as in. From here we're going to use in the Gothic
and call this method. And make sure to
import this in the top here for all the import
statements, and then Save. And here inside the
bootstrap method, we're going to check if we have a process that in the
JWT token or not. Yeah. So if you don't have GDB T key here, if you don't have this, we're going to return
or throw a new error. And it's going to have a
message that say database. Alright, now let's save this, go back here, close
the main.js file, and going to the process
that in that JWT key. So we have to add an exclamation mark here because we already checked
inside the main.js file. If we didn't, we didn't
get the DWT key. When we run the app, we're going to throw an error and we will not start the API. Now we need to add
a new object here. And we will add the
expires in property. And here we can
define the time in which we want this JWT
token to be expired. So in my case, I want to expire this
token in seven days. So you can choose
anytime you want. Let's save this. And of course we can add
days like this goes. If you hover over exposed
in property here, you'll find that it except like time with seconds or with just some
strings like this, two days or 10 h, seven days. There is a lot of Ways to define the time here. So now after creating
the JWT token, we have to return an object
with the user and the JWT, and it's going to be
set to the JWT token. Now, the error has
disappeared because we are returning the
correct value here. And we still need to
check if we have a user with the same e-mail that already exists
inside the database. So for that, we have to create a new method
inside User Service. And It's going to be
named find one by email. And we're going to get
the email as a string. And then we're going to return 08 and use
the user repository to find one by fast
the e-mail here. Now save this and go
back to the resolvers. And here, create a new
constant and name it, exist or existing user. And here we're going to
use the User Service that find one by email and the
email from inputs that e-mail. And we need to wait for this. And then save. And we need to check if
we have an existing user, then we need to
throw back an error. And for the error, we need to use a very
specific type of errors that comes
from or with GraphQL. And this is a GraphQL. And we can import it
from a graph deal. Yeah, so if you didn't
install graph here, make sure to install it and also installed the types for GraphQL. And here we need
to add a message. So the message is going to
be user already exists. And we can just send back a message or we can add some other configurations, like the extension
or the extensions. And here we can add the
code of this error. So another object
here, Code property. And I'm going to send
back a bad request code. So if you want to send back some information
about this error, like maybe the fields or
the error type like this. You can add all of that inside the extensions
object here, and it will be sent
to the client. Of course, if you want to
create a custom errors with create the like we did inside the
other two projects. Create a separate
classes for its error. And of course, you still
have to use the GraphQL here to create those classes. But it's really not
required because we already have a class
and we can use it every time when we want
to throw an error. And it will be parsed as
the graph care or yeah, we don't need to create
any new classes from that. Now, that's it for
the sign-up method. The next video, we're going
to create the sign-in.
18. Creating signin resolver: Now we cannot work on the
sign a mutation here. So the resolvers,
because we still didn't define the sign in
mutations are the schema. So first let's do that and then go back
to the Earth resolvers. Right here. Let's set the mutation, Let's create the
site in mutation. And we'll get an input
of type, sign-in input. And by the way we can, we can use another
structure here. So rather than adding a argument and the time
for that argument, we can define all
the arguments here, like and email of type string. And it's required. And then add a comma and we
need a password as a string. Yeah, but because we are using the types also inside
our resolvers, inside our user service, we need to define its input
type as an object here. So let's change this to
input sign-in input, and this one is going
to be required. Again, we're going to
return an earth response. And of course it's required. Now, we need to add
the sign in here, input that's created here. Input and sign in input. We only need the
email as a string, and it's acquired
with the password as a required string. Right? Now, Let's save this
and open the terminal. And we have to
generate the schema. So npm run, generate originary
the type of the schema. Alright, now close the terminal and close the schema dot GraphQL file and go back here inside
the author is solvers. Under the sign-in. Let's define the or
under the sign-up, let's define the sign in method. We should get a succession
for this method here. Just wait for a second here. Sign in. And if you hover over it, you'll find that sine
n is of type resolver. So we did generate
the types correctly. So here, that's defined current
input and the contexts. And as I said before, we're going to talk
about the contexts and just the future videos. Yeah, so here we
have to check if we have a user with that email
saved inside the database. So I'm going to copy
these two lines here from the sign-up
method and paste them here. And change this with just user. Here. If we don't, if we didn't have a user, we're going to send back
a long written so error. So here, wrong, Cree potentials, outer and the code
still bad request. And then we have to
check if we have, if the passwords
inside the input is equal to the hazard password
from the user database. So let's add a constant
and name it correct PWD, and then await for B crypt. And we have to import that. So this dog Alport be corrupt class from
the decrypt packets. And here going to use
the method compare. And this method
accepts the buffer or the the input and then compare it with the
acid password. Yeah. So here input password. And then we will get the hazard password
from the user password. But in our case, we cannot get the
password from the user. That's because inside
the user entity, we said the buzzword
canon, select false. And that's why we will get
here and define or not. Yeah. But we can change that
site, the user service, and instead they find one by
e-mail that's removed it, find one by method. Here. Let's create a query builder. So from the user repository, create a query builder. And here we're going to declare a variable
and name it user. And equal to query
builder. Here. Add a Ideas for user, and then we will use a method to select the user password. So here, user password,
now the query. But there is a method
that helps with building SQL queries by just adding
some other methods like this. Yeah, so we're going to create, degenerate a sequel
query here using the Query Builder to
select the user password. And then we're going to find or select a user where the email equals to e-mail. In here we will define
the variable email. So what's happening here
is that we're going to check if the email equals
to this variable, yeah. And to define variables
inside this query here, we add the colon and then
the name of the variable. And then in the
second argument here, we have a object containing the values of that
variable here, and it should be in the
same name as we wrote here. That's why when we execute
the query builder here, this will be changed to email equals and then
the email of the user. Now, we need another method
here to get one user. And if you hover over user now, you'll find that user as a promise that will return
either a user or not. Therefore, we need
to await here, and this time we will
get either user or not. And that case, we're
going to return the user and save
the user service. Go back here. Or actually let's go back
to the user service. And here, I forgot to mention that we should add
a select here. Yeah, this is going to be the primary
select and we're going to select the user document or all the columns instead
the user document. And then we will add a select where we will
use the password. And then we will do the filter here and then send back the
user with the password. Now, unfortunately,
the select force here only hides the password
from the find methods. So either find one or
find by something. Yeah, but it doesn't
hide the password. When we save the user here
or saving new entity. Therefore, we need to create a constant here
inside the create method. Create a constant that
holds the service user, and then create another Find
method after this here. And search for the
user with the ID. Yeah, but this is going
to be a waste of time, especially when
working with GraphQL. Because with GraphQL,
we can hide passwords or anything by just
removing the fields here. So for password, we can
remove the password field. And then inside
the off response, inside the user, we will
not get the password. This is how we hide
credentials using GraphQL. But here I just wanted to
show you how you can get a height field from the database using the
Create Query Builder if you're not using GraphQL. So I will leave this
code like this just for you to remember how
to get a high tunnel, like the password
and sad user entity, I'm going to remove the select false here
because I don't want to change or to write the same code every time
I want to get the entity. And I will add a
common tier two. To indicate that this, you can use this approach
only when you have a hiding. So here, only if entity that Carlin and you have
the select set to false. Now save the user service and close these two files
and go back here. And that's a very quick continue
with the sign-in method. So now we need to
check if we didn't have or we didn't get
the correct password, then we will return an error. And here I'm missing the air, correct or see and hear, throw new graph girl error. And it's going to be the same message you
credential, wrong credential. And the same extension. So let me copy this. And here I'm going to
copy the extension. And then we have to regenerate the JWT token and then return
the user with the JWT. So I'm going to copy
this and paste it here. Now the user is going to be
returned with the password. But because we changed
that inside the schema, we will not return or
the graph gear will not select the password from the return statement
of the mutations here. Now, of course, you need
to open your terminal and run npm run degenerate because
we updated the schema. So let's do that. Npm run generate. And that's it. We finished working on
the sign-in method.
19. working with the graphql context: Now we're going
to create a query that will return the user
email and the user ID. And again, we're going to
start from the schema. Does GraphQL and crea create a new query and name
it current user. And here we're going to
send back a JWT payload. Now we have to define this type. So copy the JWT name
and here create type, then JWT, fade out and cite it. We will have the
email of type string, and we will have the
user id of type int. And both of them are required. Now let's save this, close the schema that GraphQL and we have to
generate the types. Run npm, run, generate, and close the terminal. Go back to the author resolvers. And here inside the query, let's remove the get
method from here. And the current user method. Hearing it. Parent, we need
the input and the contexts. All right, now we have
an error here because we didn't add an input variable
to the current user query, like we didn't
assign the mutation, so we will not get an
input argument here. So let's remove this and replace
it with our x like this. And just to make it clear that we don't
have any arguments, I'm going to replace it
with an empty object. Alright, now here
we're going to extract the JWT payload
from the JWT token. But we used to add
that logic inside a middleware and then use that middleware
inside other routers. And then get an object from the requests containing
the user payload. But that's of course
inside a RESTful API. But because we are using
Express with GraphQL, we can do the same thing. We can add. We can use a Create
Image and then use up the 2s method to
apply the Midwest here. And then we will get the, we can extract the JWT
payload from JWT token. But we can implement the same process with
Apollo server by adding a contexts that will contain the request and
the JWT payloads. So here, say the Apollo
server at a context. And it's going to
be an function that will return an object. Now, before start working
on this function, we have to add a new interface
and name it contexts. So here, export interface,
then my context. We have to extend the Express contents so we can
get access to the request. And here we will add
our custom properties. But first, we have to
import the Express contexts from the Apollo server. So here, Airport Express context from a photo
Server Express. Now here, that's our
first custom property, which is the current user. And it's going to be of
type T, W T payload. And the DWT payload is imported from the incinerated types. So make sure to import
that from this path here. Then we have to add
another property, which is the
authorized property. And with this property, we can check if we have an
authorized user or not. So this property is going
to be of type Boolean. And save this. Go back here. And we need to add the mike contexts interface
with the Apollo server. So going to return Apollo server with my contexts of
type, my contexts. Alright, Now, go back
inside this method here. And now we can extract. From the dysfunction,
extract the request and the response because we
extended the Express contexts. And here, let's create a
constant and name it payload, and then use the DWT
packets from DWT, we have to import that. So let's do it. Important, JWT from Jason token back here, CWT dot verify to
verify the token. And we're going to get
the token this time, not from the session, but from the headers. So we will use the request
payload or the tethers. Make sure to select
headers, no tether. And then we're going to get the token from the
authorization header. Yeah. So the Heather's can be no. So let's add a
question mark there. And now we need to pass
the JWT secret key. So I can get that
from process that the DWT key and add
an exclamation mark. So we can tell TypeScript
that this is not null. And now we need to return
that payload here. So have an order here, because it seems
that the request is also can be new or undefined. So let's add
question mark there. Or let's just check if we didn't have didn't
get a request. Yeah, we will return an object and we will come
back here in just a second. Now let's define the
current user property and give it the payload. And then we will also send back or pass the request
to the resolvers. And then we will the
authorized property, and the authorized
property is a Boolean. So we will check if you
have a payload or not. So if you didn't have a paid out means that the user
is not authorized. So for that I'm going to
use a special syntax. So our two exclamation marks
and then Abby paid out. So if the payload is
null or undefined, the first exclamation mark
is going to return true. Then the second
exclamation mark is going to return the opposite
of true, which is false. And then they authorized, it's going to be set to
false means that the user is not authorized because
we have a null, a pilot. Now, if we didn't
have a request, which is a problem for us because we cannot
extract the JWT token. So we're going to send a
on the same object but with Korean chooser set
to null and the request, of course, and the
authorized to false. We have to return this. Alright, now, let's remove
the question marks from here. And actually here
we have to check if we didn't get or we didn't have a request for
authorization header here, then we'll just send
back this object. Otherwise, we will
extract the payload and sent the required property. Now let me adjust this
code a little bit to do this and save this file. Then close it and go
back to the Koreans user resolver or query here. And we're going to check if the authorized property
inside the context. True or false. Yeah. So if the authorized
property so here authorized. And we are not getting the
authorized from the context. That's because we have to
update the code chain here. Yeah, and update the
types of the results. So we need to specify the context type and say
so under the config here. And in the same
indentation level, contexts, type, or contexts, types by type, and specify the path or the file that
contain our context. In our case, are my contexts exist inside the module file. So make sure first to export
the mike contexts interface and come here and add the birth according to
these generated folder. So according to the
generated folder, the module or according to the resolver type that is exist side the
generated folder. The module file is one
level out, then module. And we have to select
the interface. So we're going to add a hashtag here and add the name
of the contexts. So my contexts. And in this case, we are selecting a, a, an exported interface
or variable from the file using
a YAML syntax. Now let's close the
co-gen config file and open the terminal. Then here we have to run
the generator commands. So npm run generate, and then we will get our
context of type, my context. Yeah. So let's wait. Right now. If you hover over contexts, you should have the bike
contexts type here. Now let me close the terminal and authorized is
of type Boolean. So we're going to check if
the user is not authorized, then we're going to send
back a unauthorized error. So Throat knew GraphQL
and MS is not authorized. And we need to add an extension. Extension. Then add the code. And here we will add the code. Unauthorized. Alright? Now if we have a authorize
that user or going to return from the
context, the current user. Now let's save. The current user is of type G deputy payload contain
the email and the user ID. The type name is just
something related to GraphQL. But inside the GraphQL
schema or a response, we're going only to get
the email and the user ID. Alright, now let's save this. And in order to test all
of these resolvers here, we have to have a
working database. So in the next few videos, we're going to learn how
to use Joker, of course, how to install Docker
and then use Docker to install the Postgres database and run it in our local system.
20. what is Docker: Docker carves up a
running Linux system into small containers, which is its own little world with its own programs
and its own everything, all isolated from anything else. These containers are
designed to be portable so they can be shifted
from one place to another. And Docker does the
work of getting these containers to
and from your systems. Now, what is the container? You know, when you build
something with e.g. an OTS and then you push
your code into GitHub and somebody else tries
to pull and run your code inside
his local system, but it didn't work for him. And then he will think
that your code is garbage. And you may also think that
you are not a good developer. But sometimes that's not the
case because your Node.JS applications requires a
very specific softwares and unique environment to work. And these requirements are only exists inside
your local system, where you are,
where you developed your Node.JS application. Like if you have
Linux and instead of Windows or you have an
older version of nodes, yes, all of these variables cannot be the same
in all systems. Therefore, Docker has introduced something
called container. A container is a standardized
unit of software that allows developers to easily there from
its environment. So you can develop e.g. your Node.JS application and
build a container around it and push it to
Docker Hub as an image. And then anyone can pull that image and run it in
his own local system. In that case, your
NodeJS app will be running on the same environment
as in your local system.
21. Install docker Linux: Now to start Docker on your window or on Linux
system in general, you have to go
through a few steps. So head over darker tags. And you can use this
path here or this URL. And or maybe you can
search on Google for installing Docker engine
on your pinto because URL's can be changed
in the future. Now, the first thing
you have to do here is make sure you don't have any older
version of Docker. By running this command here, you can delete all
outdated versions. Now, the next step is you have to update
your APT packages. And then I'll start with
some few beggars and see if that will be
used in the next steps. Yeah, then you have
to run these commands to tell your system
that it's okay to use. Software is from
the Docker Engine. And then you have to run this command to set
up your repository. And the reason why
we have to add up our repository or
setup a repository that is connected to the
Docker Engine is because we're going to pull images
from Docker Hub. And sometimes we have to push images made by
us to the Docker Hub. So we need airports
was like that. It's similar to repositories
with GitHub. Yeah. Now the next step is to
install the Docker Engine. After setting out
their repository. Of course, you have to
update your packages again. And if you've got any
errors, run these commands. And then finally, you can install Docker by
running this command. After that, if you
want to verify if you have successfully
installed Docker, you can run an image
that existed by default with Docker
called kilowatt. So run this command, sudo docker run hello-world, and you should get a string
back with HelloWorld.
22. Images to containers: Now that we've got
Docker installed and talked a little bit
about what Docker is, let's try to use it. So with Docker, everything
begins with an image. An image is every
file that makes up just enough of the
operating system to do what you need to do. Now, the command to look
into your Docker images is simply Docker images. You can see here
that I have a lot of images and things going on here because I used
occur all the time. And for you, you may have some images like you've
been to or HelloWorld. Now it's image has a
name and a version, bike latest or
something like this. And also its image has an ID. And here we can find when
this image has been created. And here you can see
the size of that image. Now, let's take an image and then run it inside the
container using Docker. Let me clear this terminal
and then run right docker. Run command. Now, this command takes an
image and turns it into a living running container with a process in it that's
doing something. Now, after docker run, I'm going to specify the
image that they want to use. And for me, I'm going
to use who've been to. And for you, if you
didn't find you've been to installed by
default in your joker. Don't worry because by
running this command, Docker will try to
pull the human to image with the latest
version from the Docker Hub. So of course you have to have an Internet connection
to do that. Now, still didn't finish
from this command here. So docker run, you've been to. And then we said
that we're going to run a process in this image. So let's run the
bash shell because I want to run some
commands inside you've been to with the terminal. And also for that, we need a flag to interact
with the terminal. And the flag is the D flag. So D stands for
terminal and I stands for interactive,
terminal and directive. Then you've been to bash. And now let's run this
schematic for you. If you don't have you been to, you will see a
downloading process. Alright, we're inside
the Ubunto image. Now, if I take a look here, I'll find that we're
inside the we have the, you've been to environment. And just to make
sure going to run this command to take a look to this file LSB dash release. And we should get the version of you're going to have here, the version 20 and
the distribution ID. You're going to now to
your exit this image. You can write exit like this
or Control D does for exit. Now, in order to see
the last exited image, we can open a new terminal here and docker ps and
then dash latest. So if you run just
ps, you will find, you will get all
the running images. And with L, you can get
the latest exited image. If you run this command,
you'll find that the image is, you've been to. And you'll find the command here that was running
site this image. And of course, where
does create it? And it exited in
about 4 min ago. Yeah. And this isn't the name of
that container, not the image. So this container is
running the urban to image and running
the basket land. Yeah. And it was created 6 min
ago and exited 4 min ago. And this is the idea
of the container. And they said the name
of the container. And by the way, this name is randomly generated by Docker. Now let's go back
here and run the, you've been to image again
with the bash shell. And inside that
you've been to image. Let's create a new file with the touch command
and name it my file. And take a look here and
we will find my file. Then let's exit this. Or I let skip this container
running and go back here and then run
the same command, docker, run TI for
terminal interactive. And then you've been to
and run the bash shell. So we're running
the same image with the same flag in
the same process. And here, if we take a look, We will not find the file
that was created here. So its container
runs an image and it doesn't get affected by other containers that are
running the same image. So when we create a file here, doesn't mean that
we're going to get the same file inside this container that
runs the same image. So its container is
insulated from the other. Even though they both
running the same image, you can think of them like two computers running the same
version of you've been to.
23. containers to images: Now we can see we went from an image to a
running container. And when we start a container, the container is still there.
It didn't get deleted. So I can look at the most recently
exited container with a docker ps command. But of course, we need the dash l flag to get the
last stopped container. So here you can see that I have a container that was
running just a moment ago. And you can see all
the informations about this container here. Now, let's say I have
a stopped container. I started from a base image. Ion started my own software. I've got a container that has my software installed on it. Now, I want to use the
same container that has my own softwares
in it in the future, or maybe share it
with the community. So to do that, we have to use the docker
commit command that takes containers and
makes images out of them. So to use the docker commit, let's first create a new
container. Let's clear this. And one. You've been to, of course with the TI flag and then you've been to and
run the batch command. Now inside this container, we're going to create a file, and we need to use that file in the future
with this container. So we're going to
create an image from this container here that's using the touch command and create file and name it like secrets. We have our file here. Let's exit this container with Control D or
just write exit. And then let's take the idea of this container
by running docker ps dash L. Then we have here
the idea of this container. So let's copy this id. And then let's use the
docker commit command and paste the darker or
the container ID here, then run the docker
commit command. Now, we would get an image
out of this container. And this is the
idea of that image. But using a long ID like this, it's not a good thing
because you can lose this id or maybe you can
find it difficult to run commands using
this long string. So what I'm going
to do next is going to a tag this image with
a human-readable name. And to do that, we
don't use the tag flag. Then we add the ID
of this image from the colon to the end of this
string and paste it here. And then choose a
name for this image. So image what e.g. and run the Docker command. And now if we want to check
if you have an image with the image dash or
underscore one name, you can run Docker images and then search for the
image name here. You can find it here. So we have image underscore one, and here we have the ID. And it was created
for minutes ago. So this is how we can create
images from containers. Now, there is another way
to commit images rather than using the commit
command. And it's acumen. And that's by, first of all, let's create a Docker container and take the idea
of that container. Then we're going to use the
docker commit in another way. So here let's to create
a file like my file. And let's exit again. And then we're going
to check or to get the idea of this
docker container. And now we can take the
name of the container. Here, a human-readable name, and copy it and then run docker, commit, then the name
of the container, and then the name of the new image that
you want to create. And this time I'm going
to choose image two. Now we're running this command. We would get the same
output from before. But if you run
Docker images now, you can find an image to presented in our
local Docker repository.
24. running postgres database with docker: Now we're going to learn
more about docker, and in the same
time we're going to run our Postgres database. And for that, we need to pull the postgres SQL
image from Docker. To do that, we have
to run docker, pull. Then the name of the
image which is Postgres. You can add a colon and
then the latest tag. But by default, well, install the latest version. Now I'm not going to run
this command because I already have this image
in my local system. So now I'm going to
use the docker run command to run the
postgres image. Of course, with the DI flag
here and the postgres image. Now, we're not going
to run this command. Take this because we have
to add some configurations. And this time I want to add a custom name
for my container. And for that we have to use the desktop named flag and then choose a name like
up. Underscore. Both stress. Now, in order to establish a connection to the
Postgres database, we have to expose a port
and Postgres natively uses the 5432 port to
accept connections. So we have to expose
this port from the container using
the dash P flag. And now we have to map this Postgres port with a
port from the container. And I'm going to choose a 545
5 v and add a colon here. Now, you probably didn't understand what I'm
talking about here. That's why I prepared
a diagram here to simplify the idea
of exposing posts. So here we have the Postgres, postgres app container or
outputs glass container. So let me change the
name here at Postgres. And this container has an
image running inside it. So this image is
the postgres image. And Postgres natively
uses the 5432 port. But we cannot connect
an app to the this to the Postgres
image from the 5432 port. When we run this image
from the container. Because as you know,
containers are isolated from each other and from
the local system. So this approach will not work. Therefore we have
to use another way, which is exposing another board from the container and map this port to the Postgres port or to the image that is
running inside this container. And then in order to connect
to Postgres database, we have to connect to
local host colon 5455. So we have to connect
to the container, not to the image itself. Now, if you have
more than one of, you can of course connect them to the same
container that's running the postgres image. Because here we're
using localhost. But of course it's not
recommended to use the same database with
other applications because you can lose data and destroy your database
tables, like e.g. if App one is registering a user into the database
with an email a, then in the same time
we have the app too, registering also a user
with the same e-mail. That case, you will
have instead of your database to users
with the same email, even though you have validations running
in your applications, doesn't prevent such
mistake to B happened. So what you have to do is creating another container
with the same image. But this time you have
to name that container with another name and then
also use another port here. But of course you will keep the same port for
the postgres image. So here changed the name to, up to and here the
Postgres icon. So we have the Postgres
image here and you have to change this port. Otherwise, you cannot
connect to the container, or even you cannot
build this container because you're
using the same port in another container here. So I'll just change it to 5456. And then let me remove this. The outdo is going to connect to this address, to this container. And this is how you can have, like if you're building
a microservices out, this is the right approach
to connect to database. So it's app has to have its own containers that
runs the database. Now, I don't know why I'm scrolling up and down
over and over like this, but let's just ignore that
and go back here inside our terminal and finished
the app postgres container. So we have our posts here. Now. We have to define some
environment variables. And it's required to run Postgres or to
connect to Postgres. Now, these environment
variables are define also here. So we have to define the same information
like the username, password, the port, and we
changed the port 25455. Now let's change this here. And the spring,
the terminal here, and try to copy the
same information. Now, to add a, an
environment variable, we have to use the
dice e flag and then name the first variable. And first variable
is the Post truss. And it should be all uppercase. Now, Postgres underscore user. It should be the same
as the username here. So it's going to
be equal to admin. And to define another
environment variable, we have to use again
the dash g flag, and then write
Postgres password. Again, all uppercase. And then the password is the same password inside
the app data source. So password is admin, and the last environment
variable is the database name. So again, dicey, then Postgres. So most crass. Then underscore db for database. And the DB database
name is main. Lowercase. It should be the same
name as presented here. Of course, the host name
is local host because this container is going to
run inside our local system. Now, let's run this container
and wait for the result. Here we go. We have some login
going on here. And these, these information
is related to Postgres. So we are initiating a new
connection to the database. And here we go, we have database system is ready
to accept connections.
25. Test Graphql endpoint: Now, in order for our app to connect to the
Postgres database, we need to initiate the app data source
inside module.js fall. So side here, Let's first
import the app data source. Data source. From a data source. And inside these starts Apollo, and from the beginning, we have to wait for
data source initialize. Now the initialize
method is going to connect to the
Postgres database and take all our entities and
save them as database tables. And now before testing the API, we need to fix something
and study context here. So if we have an
valid JWT token, they verify method
is going to fail. And when it fails, we will not be able to connect
to the GraphQL endpoint. And we will not be able to use resolvers that doesn't require authentication because the
method is going to throw an error and it will break
our whole application. So what I'm going to
do is I'm going to put the verified method
inside a try catch block. And in that way, we can prevent errors coming
from the verify method to be thrown to the outside of
the context method here. So the first thing
I'm going to do is going to create a
lead variable here. Then remove the
constant from here, and then copy all of
this or cat or this, and create the try
catch, catch block. So here, try catch the error. Then side here, basically
payload with the verify method. And if we failed here, we're going to catch the
error inside the catch block. But we're not going to throw the error here
because in that case, we're going to do
this same thing that they verify
method was doing. So here I'm going to set
the payload to be no. Now, if we didn't get
the authorization token, they verify method
is going to fail and the payload is going
to be set to No, Yeah. And the authorized property
is going to set to false. So we don't need the
if statement here. And when we don't have
the authorization token, we're going to pass
an empty string and the verify method is
going to fail anyway. Now, the reason why
we're doing this and we were using the
if statement here, is because they
verify method only accepts strings as a token, doesn't accept undefined
or null types here. So if you didn't
have theorization, we're going to pass a string. Now in this way, we can reach our resolvers even though
we didn't get a token. Now let's save this
and open the terminal. And finally, let's test
our GraphQL endpoint. So here let me expand this
and run npm, run, start. And before running this command, we have to change something instead of
beggars adjacent file. So we use the node
dash, dash swatch here, but this command
doesn't watch for the changes inside
the TypeScript files. And we need to add the
flag watch like this. But in this case, we have to add only one and psi. Because if we use
two and signs here, we will wait for
this command to be executed and finish running, and then we will run
the node command. But the watch mode here
doesn't finish, doesn't, it doesn't have an n. Yeah, that's why we name
it watch mode, Yeah. And in that case, we will not execute
this command. So we have to remove this
and use only one and sine. That case, we will run these two commands
in the same time. But this is very dangerous because imagine if we didn't have the
dist folder here, and we ran the
node, node command. And in that case, denote command will break, but the compile command
that will be executed. And it will watch for changes
in sign the ts files. But when the DOS command
generate the dist folder. We will find that
the node command doesn't work and it doesn't
watch for the changes, so it will not know that. Now we have this folder
and in that case, our application is going to
stuck on the compile stage. Yeah. So the point I'm making here is that we
cannot use the dash, dash watch command here. And we have to use node man
again inside this project. So make sure to install Node man again in the dev dependencies. Then use it here with the node command without
the word smoked. So let's delete that, then take this command
and put it here. Then let's delete this
and use node one. And with the e flag and then ts, then they stash exact. Then we're going to execute the command, the
compile command. Here, npm, buy-in,
compile, save this. And let's try to
start our app. Right. Now. We didn't get any errors
related to a database, means that we successfully initiated a connection to
the Postgres database. Now, let's go ahead
and dust are GraphQL endpoint side the
Apollo sandbox, we have here a green icon means that we successfully connected
to our GraphQL endpoint. Now, let's try to sign up a user with the same credentials here and check if we can
save users to the database. So let's run these
sign-up query here. And if you don't, if
you didn't have this from the previous videos, you can just go back to the root here and click on mutation, then choose the sign up. Actually, I'm going to generate the query again just in case. So let's add the sign-up
query or a mutation. And the two fields, the user. And here we will just
wait for the ID to make sure that we
successfully saved the user inside the database. Now, we will use the
same inputs here. And from the sign-up mutation. Alright, we have
here the JWT token, and most importantly, we
have the ID set to one. So if we signed up a new user, we have to get that. We have to get an idea of two. So let's change
the e-mail here to user two and the
firstName user2, and then run the sign up again. And let's wait for the response. You have to get ID two. And he would go. Now, we are sure that we're
saving users and into the database and we're getting also results from the database. Now let's test the Korean
chooser resolvable because the sine n mutation is, has the same logic
as the Sign-up. Yeah, So let's test
the Korean user. And for that we need to send
a Authorization header. So let's copy the JWT token. And here let's click on the Settings icon and add
the authorization headers. So here at theorization, you can just start writing authorization and you'd
get some suggestion here. Choose authorization
and paste the token. Yeah, it makes sure you have
the cookie is set to off. Otherwise, we cannot connect
to our GraphQL endpoint. Now let's save this. And let's run the
gradient user query. So go back to the root here. Let's delete all this
and click on query, then add the current user query. And let's wait for the email and the user ID
so we don't need the input. So let's just run the
current user query. And we should get the
email and the ID. Here we go. We got the user to, because we use the JWT token
when we signed up as user2. Now we can get the
email and the user ID. It means that our
context is working and we can extract our
payload from the token. Now, in the next video, we're going to create an
image out of this container.
26. from postgres container to image: Now let's create an image that will run this
container for us. So let's exit from
this container. And then let's
clear the terminal. And let's use the docker commit command to
create a new image from the app postgres container and name this image
message database and run this command. And now let's try to
run this image where Docker run than TI for
terminal direction. And then I mentioned
the image name. And before running this command, we have to expose
also a port here, because when running an
image using Docker run, we're going to create a
container and the message, our database image is going
to run inside that container. So we're going to expose the same ports that
we exposed inside the app postgres container using the desk P flag at the port
5455 and then a colon, then the port of the
Postgres database 5432. Now, you probably
didn't understand why we're using the
same ports here. And that's because you're
thinking that when we run the NSG add DB image, we're going to actually run
the app postgres container. Yeah, but that's not the case because when
we run this image, we're not going to run the
app postgres container, but we're going to run
the postgres image with all the environment variables
and all the configurations. So the app postgres
container has nothing to do with the MSG AB DB image. And now when we
run this command, we're going to get a
container that will run the postgres image also with all the environment variables
and the configurations. So I hope now you can
understand why we have to expose the
same ports again. Now whenever we
run this command, we're going to get a randomly
generated container. Because when we exit
from a container, the container
doesn't get deleted. But we don't want that. We want the every time when we finished using a container, that container has
to be donated. And we can do that
automatically by adding a new flag to
the rand command, which is the RM command. And now we can run this command. And when we finish working
with the container, it means when we exit
from the container, the container gets deleted
and we will not get a bunch of randomly generated containers inside our Docker repository. So now let's run this command and wait for the database connection
to be established. Alright, we have our
database running. Let's go back to our
project and the terminal. Let's run npm. Run, start. Make sure you still have
the 5455 bullets here. Let's run the command. We can see that we didn't
get any database errors. Now I have here a double slash
for the GraphQL endpoint, and that's because it didn't change that here
and say the main.js file. So I'm going to do that now
and save the main.js file. Wait for the reload, and then you have to
get the correct year. Now, navigate to this URL. And inside the Apollo
sandbox, that's the API. So you can see
here I was testing the validation for the
sign-up repository. So if we signed up
with the same email, we're going to get a bad request error message saying that the user
already exists. So we know that that part of the sign-up
resolver is working. Now, let's sign up as usual one. And here, firstName
user1 and run sign up. And we should get a user
with an ID set to one. Right? We have the ID and we
have the JWT token.
27. Defining the room Schema: Now it's time to start
working on the room service. And as before, we're
going to start on the schema that graph DO file. But I'm not going to write
the GraphQL schema of the room service inside the schematic graph
Gail fail because it's, you know, it's always
a good practice to split files
into small pieces, especially if you
have a big file. So that's what we're
going to do here. Let's start by creating a new folder inside the
source folder and name it. Booms. And then here we're
going to have the rooms or room that GraphQL. And then here we're
going to write our GraphQL schema that is
related to the room service. And then we're going to do the same thing for
the off service. And we will learn the, the, the gruff DO files and then add all of
that automatically, automatically inside the
schema dot graph TO file. So here, let's start, let's start by defining
the room type. So let's create
type and name it. Room. And ROM is going
to contain, of course, an id of type int and it's required and an array of users. So we have to Use the user type. And as you can see here, I don't have an error
because we already used the user type or declared
these are type here. And like I said before, we're going to merge
the two files, the Earth graph
Gill and their own GraphQL inside the schema,
that GraphQL file. So we will have access
to the user type. So here let's define
the room with the user and the messages. Then the messages,
it's going to be an array of message object. And we have to
define this above. Here. Declare another type,
name it message. And its message is going
to have a form field. And it's the ID of the user. So we can know who sent the message and the
content of the messages. And this one is of type string. Now, in order to send a message, we need the message itself. It means the content. And also we need the bromide D and then save
that message inside the room. The message, the
message is array. Then all the users inside
the user's array is going to have access
to this message. So if we have here two users, User a and User B, the message from user a is going to be received to the user B. And of course, both users, it's going to have access
to the same message. So let's create an input here, input type and name it,
send message input. And we need the message
of type string, and we need the room
ID of type int. Now let's create the mutation that will accept this input. So here type mutation, the first mutation is the
sand message mutation. And here, so MSE is,
stands for Message. And here we will
accept an input. You can name this variable
whatever you want. I like to name it input. So here, this input is of
type sent message input. And we're going to
return back the rule. Now, let's save this. Of course we didn't finish, but I want to create the
orthographic DO file here. Yeah, so let's create
a new file inside the folder and name
it earth dot GraphQL. And here we're going to
copy all of this inside the the GraphQL schema
and make sure to cut it. And then save this file and return to the autograph Gill and paste all of this here. Now, we should not
add the schema here because this will break our GraphQL endpoint
because we're going to define this inside of the schema dot GraphQL
file automatically. So let's remove it. And safe also makes
sure that inside all the subgraph kill fails. We have a mutation
and a query and make sure both of
them are capitalized. So also inside the wrong
GraphQL have mutation. And then we will create
the query and we should name them in the same way we did
inside the Earth GraphQL. And you can change
the name here. Yeah, because it's not, it's not about the name but it's spot having the same naming. So you can have this like this. And in this case
you have to change the room GraphQL notation with
a lowercase, lowercase m. But I don't want that. I want a uppercase M, so I'm going to change this
as before, and that's it. The next video, we're going to write the logic that will handle the merging of these two files inside the schema dot
GraphQL automatically.
28. Auto generate and merge schemas: Now let's create the file
inside the source folder and name it,
generate the schema. The Ts side here we're going to write the logic
that will merge all the subgraph your schemas into the schema
dot GraphQL file. So the first thing we
have to do is importing all the files that ends with a dot GraphQL from
the source folder. And for that we have to
install a new package. So open your terminal
and run the command npm, install the package globe, and also installed the
types for that package. And run this command. Alright, now let's close the
terminal and go back here. And let's start by importing
the globe package. So here, globe from glow. So globe is actually a
function that we can call and specify the path of our files that we
want to search for. So for us, we're going
to search inside the source folder and
inside any folder, and then any file that
ends with GraphQL. And then here we have to define a callback that will
accept an error and files. So if we have any error, we're going to throw an error, throw new Error, or throw
just the, let's continue. First thing we have to delete the existing schema
dot GraphQL file using the unlink method
from the FS Beckett. So from the file system packets. So that's important. The unlink from not only us, but fs promises
here pharmacists. And we have to wait for a link and then delete
the schema dot GraphQL. All right, now let's declare a variable and name
it types array. Force is going to
be of type array and it's going to hold strings. So he's going to hold all
the schemas as strings. And that's initiated
to an empty array. And then here, Let's
read all the files and push them as strings
into the types array. So for that, we have to loop through the files
using the for each method. In here, take each
file and extract the schema from the file using another method from
the file system package, which is the read file sync. So we're going to
read actually a file. And here let's pass
the file here. So this file is a path or just the name of the file here is
the path of the file. And that's actually change
it to file path here. File path, in here, file path. And here we have to define
the encoding because we have to extract the
schema as a string. So let's encoded to a UTF eight and then take the schema and push it
into the types array. So types array, push the schema. Now after we do that, we have to create another variable and name
it type deaths. And here we have to merge
all the draft your schemas. So all the strings inside the
types array into one file. And for that, we need the merge tool from the
GraphQL tools packets. So let's head over to
our terminal and style the GraphQL towards beggars NPM. So at GraphQL, dash tools and then slush merge. Alright, let's close the
terminal and go back here. And let's first import the merge tool from
the lower spec. So import, import nourish type devs from the GraphQL. Then tools than merch. And we're going to
use this method. So let's copy the name
here and paste it here, and then cite it. We're going to pass
the type survey. Now, this method is going to
generate a document note, but we need to, in order to append
something to the to a file, we need to generate a string. So for that, we need
another tool from GraphQL. So import from GraphQL
another method called print. So the front is going to take the top typedef and
converted to a string. So say here, this put this
inside the plant methods. Now the type, this
is of type string. So let's append
the string inside the schema dot
GraphQL file using the append method
from the FS baggage. So make sure to import the here. And now let's use it and mentioned the
schema dot GraphQL. And here we have
to add the types that we can append them into the schematic
graph Gil file. And this function also
accept a callback. So let's define function
and we'll accept an error. And then if we have any errors, we're going to
throw that better. Alright, now, if we finished, I want to see a confirmation message
into my site, my terminal. So I'm going to console log the GraphQL schema
generated message. Or maybe let's use
the console dot info. And before appending
to the schema, that GraphQL file, we have to create that file
because here we are. We're going to delete
the schema.gov kill. So to create a file, we have to use the
right file sync method from the FS packets. And again, make
sure to import that from from the FSLR package. Here we will also add
the schema dot GraphQL. So this is a path for
the schema graph guilt. And when mentioning
just the name, we're turning this method
that the schema graph Gill actually exist inside
the root of this project. So here, schema graph. And I want to
append to this file a message saying that this
is a generated schema. Then here, let's say do not. Right? Now, Let's
also a new line. And here we need to add a flag at an object and
inside that div like option. And because we're going to
append something to this file, we're going to use the
a flag for append. And also because we're deleting the schematic
GF q five here, we have to use the plus sign to create the file
if it doesn't exist. Now let me adjust the code here. And Let's also add
another line here. Backslash n for new line. Now we've finished creating this script and we're
going to execute the globe function as a command from the
package.json file. We have to do that before the force-generating the types
for our GraphQL schema. So before this command, we're going to run
note and then refer to the dist folder and to source
generate schema that Z, S. And here in the compile command, we're going to compile our code, and then we're going to
run degenerate command. So here, npm, run generate
two and science also here. And then save. Now, let's open the terminal
and run degenerate come out. And of course we need to
compile the code first. So let's run TSE. And then let's run
the npm run generate. And here we go, we have the GraphQL schema is
generated message. And now we have to parse the schema graph
girl and add all these generated
TypeScript types there. Now let's check the
schema that golf gear. And here we go. We have our generated schema. We have our types and inputs. And here the mutation, you can see that we
have the off mutations, the sign-up and sign-in. And we have the Mutation, the send message mutation, automatically added
inside the mutation type. And of course we
have only one query, the current user query. And you can see also here that we have the schema also
generated here automatically. Yeah. And remember when I said that we're going to get
access to the user here, if you didn't understand that. I mean that when we generate
the schema, the schema, the GraphQL, we're going to have all the types in one file. So we're going to get access
also to the user type. Yeah. So it's okay to use the user types inside the
room dot GraphQL file, even though we didn't
declare it here. Now, in the next video, we're going to create
the room resolvers. And then inside the
module, the ts file, we're going to merge
it with the resolvers.
29. creating the room resolver: Now inside the rooms folder, Let's create a new
file and name it from that resolvers dot ds side here, Let's export a constant with
a name, room resolvers. And this constant is going
to be an object of type. Result looks. It's important that from
our generated types. So a port resolvers
from go out When level and select these
integrated folder and then select the resolvers. Dash types file are
now inside here. We're going to
define the mutation. And we only have one mutation, which is the send
message mutation. Now because we didn't
create the entity, we're going to send back
a manually created data. Here, let's define first
the sand message station. And we have the parent, have the input and the context. Alright? Now, only authorized
users can set message. So let's check if we have an authorized user by checking the contexts that
authorized the property. And then if we have force here, we're going to throw a new GraphQL error and make sure to import
that from GraphQL. And then going to send back and unauthorized method
or I message. So an authorized message, and that's an extension. So here tensions. And we're going to send the
unauthorized coat here. And for rised. And now let's send
back a room object. So let me check how
the object looks like. So we have an id,
users messages. So let's add that. All of that return id one. And for the users, we have to create an
array of users object, but we can just send
back an empty array and also the messages array. But that's a sand bag, just one message here. Let me check how the
message looks like. We have from and content
fields from sand object from, let's say one and the
content test message. Alright, now let's save
this resolver file. Or actually let's
use the input for the message object just to make sure that we can
be the client data. For the content,
I'm going to return the input dot message. And here we have the message, either a string or undefined because we didn't add an
exclamation mark here. So let's do that. Save. Of course we have to
generate the schema again. So let's run NPM, generate npm, been in, right? Alright, now we have generated
the schema and the types. So let's go back to normally servers and we can see that
the error has disappeared. We can remove the
question mark from here. And the message is now
only of type string. And here inside the ID, we're going to send back
the input that room ID. Because this is a room upset. Yeah, let's save this. And in the next video
we're going to merge. The author is over and
the room resolvers here inside the
module.js file and then test the GraphQL endpoint.
30. merge resolvers: Now in order to
merge the resolvers, we need another tool from the GraphQL to slash
merge packets. So on the top of this file, let's import the new tool, nourish resolvers from GraphQL, dash tools that smells package. And we also need to
import the room resolver. So here, rule resolvers from the rooms folder and from
the room resolvers file. Alright, now, in
the bottom here, we're going to use
the merge resolvers function or tool inside
the app module class. So let me cut this and then use the merge resolvers
as a function. And this function accepts
an array of results. So I'm going to add an array and then paste the
authors office here. And you can see that the
error has disappeared because the merchant resolvers will
return the resolvers type. And here we can
add many resolvers as much as we want because
we're accepting an array. So let's also add the room
resolvers and save this file. Now, let's check if
we still can run our PI and that we
didn't break anything. So let's open the terminal and
run the npm start command. First. Let's make sure that
we have a running database. So using Docker, that's one. Then this dash or M for removing the container once
we finished working with it, then TI for terminal
and direction and then name our message out DB image. And don't forget to expose
the port 5455 and the 5432. And let's run this command. Right. Now. Let's go back here
and run npm start. This command is
going to generate the schema first
and then generate the types and then one, the API. But first, we're
going to compile the code and then run
the incinerate command, and then run our API. We have here GraphQL
schema generated. We have also the types. And now we should get
the success message. Now, let's open the sandbox. Sandbox and let's first sign up. So here I have the
sign-up mutation, I have all the inputs. Let's try to sign up. As you can see here,
we have the id one. And that's because every
time when we close the database and rerun it again, we will get a fresh database. Yeah. So let's take now the JWT token and go to the settings and add the
authorization header there. Here, authorization or
the JWT token, save. And let's now run the
sent message mutation. Let me clear all of this. And let's go back
to the mutation. And sand message, or the send
message predation at all. The results are the fields. Here. We don't have users, but going to add the ID there. And we're going to get the from, from the message
and the content. Now for the input, Let's clear all of this first and then add the
message and the room ID. So the message here,
that's the message. And the room ID.
Let's say, Well, of course we don't have room
ID table, set the database. So let's send message. And we should get back. And object of the room ID with
id1 users an empty array. And we have here
our message with the message test
message straight here. Now, everything is working. We still have to create the room entity and finished
the room resolvers.
31. creating the room entity: Now let's create
the room entity. So inside the rooms folder, create another folder
and name it rule. And then side here, create another folder
and name it entity. And then finally let's create the room dot entity that yes. So let's start by Export and defining a class
with a name Rome. Then we have to use the entity, the character from tape around, support entity from type or N, and use it for this class. Now inside here, we
will start with the ID. And again, we have to import the primary generated column, the character for the ID. And here it's odd
that the character, then we need an array of users. So here let's add users and define it as an array of user. And we have to import the
user entity from the user, that entity dot ds. So here, user from
go back one level, two levels, and three levels. Side The off and
sad user folder. Select the inside
the entity folder. Select the user,
that entity file. Now, the users are
a, is a column, so we need to import a color and the character
from type around. So here Carlin, and use it here. Now for the users column, we're going to create a many-to-many relation
with the user entity. And for that we have to
import the many-to-many, the character from
tape around and use it here at many, many. Now we have to define the
type of the relation, and in our case, it's the user entity. So here we have to
add a function and find or return the
user entity as a type. Now, in order for this to work, we have to add
another character, the join table to
Garreta and use it here. So the join table, the character is going to take the IDs of the room and the user entities and
save them into one table. Now, we only need to use the many-to-many relation in
one side of the relation. And this is called a
unidirectional relation. If you want to create a
bidirectional relationship, you can go to the user entity. And inside here you can
create another column and name it rule, and use the many-to-many
the character. Now here we have
the room entity. And this should be an array. And use the many-to-many
to character here and define the type. So here we're going to
get the room entity. And also here we need to
add another function where we have to return the property that owns the
other side of the relation. So in our case, aside the room object, we're going to get
the other side of the relation from
the users property. And we should do the same
thing inside the room entity. But for the join
table, the character, we should only use it in one
side of the relation here. So here, let's do the same
thing, just an example. So here the user
and then return, returned user dot room, because the room property owns the other side
of the relation. And should, it should be
rooms, not room here. But again, it's just a example. Yeah, we're not going to use the bi directional
relation here. We will only need to the union relation, uni
directional relations. So let's study this and save it, return back to the user entity
and remove all of this. And make sure to delete any input statements of the
rule entity and close this. Now we still didn't
finish because the Postgres database
does not support arrays. Yep. And for that, we need to use a very
unique type of JSON, which is the JSON B. Now the datatypes Jason and Jason B, are almost identical. The key difference
is that JSON data is stored as an exact copy
of the json input text. What is JSONB stores
data in a decomposer. Binary for that is
not as a string. Yeah, like JSON, but
as a binary code. And that's what the
binary stands for. Our ADB stands for, it stands for a binary. Now, this is the
only way to work with arrays with Postgres. So we have to do this. You have to use JSON v rather than simple arrays
are just simple JSON. Now I'm going to add an
option here and say nullable, false because I
don't want to get a null value here for
the users property. Alright. Now we still
need the messages. So let's add it here. And it's going to
be of type message. And we can import that
from the generated types. Make sure to change this. Go out two levels, think of three levels. And here, let's
make this an array. If you hover over this type, you can find that
we have the content and the form as
required properties. And we have here the type name, which is not required. Therefore, we can use
the message type here. Yeah, it's not the problem. Now. Here, let's define a column. A column. And we have to use the adjacent began because
we cannot use simple erase. And I want it to
be not nullable. So I'm going to add
nullable equal to false. And safe.
32. creating and updating rooms : Now inside the room folder, let's create another
file and name it room dot service that yes. Here let's export a
class room service and thus create the constructor. And here we're going to create
the room, our repository. And it's going to be
of type repository. We can import that
from tape around. So import repository
from type around. Here we need to add
the room entity as a argument here, Rome. And that's important
that from the entity. Now let's define the method that will create a room for us. So here, acing, create room. And this method will accept
an argument of participants, which is an array of users. So here, array user, and this is the user entity. So we need to import
it from here, port user from go out two levels and then
select the oath folder. And from there, the user folder, the entity and the
user entity file, not only the participants here, we also need the message. Because when we are
going to create a room, when the user tries to send a message to another
user for the first time. So here the message is going
to be of type message. And again, we're going to import that from these generated types. So here, Import message from out
to a level, two levels. And then Saturday
generated folder, select the resolvers types file. Now here we're going
to create a room with the repository repository and
where they create methods. Now it's true excepts users. So the users or
the participants, and also accepts
the messages array. So because we're going
to create a new room, we're going to add the message
manually into an array. So we're going to get an
array with one message. Alright, now we have
to return a weight, this dots room repository that save and pass the room document. Now it seems that
they have a typo here in the repository. So let me fix this. And I'm going to use multi-select and change this
to maybe a repository right? Now let's save. And now we have to handle
the case where we have, where we want to send a
message to a specific rule. So here let's create
another method. It's going to be async again and going to choose the name, add messages to roll. You can choose the
sand message name. But I want to be very clear here that we're going to
send a message to a room, and here we're going to
create a room. Yeah. So now to do this, to send a message
to a specific room, we need the room ID, of course, of type number. And we need the message
of type, message. Here, message. And now sides here. We're going to use
the Query Builder. And that's because we have to
deal with Jason be arrays. So if you can remember, we define the messages
column as JSON B. And because we have here
the array sign from TypeScript is going to be
of type Jason be array. So the only way to
deal with Jason be arrays is by using
the query built. Now let's define
Query Builder here. This dot positively
our own repository. Create Query Builder. And here let's create a weight Query Builder. Update. It goes, we're going to update an existing room and then add the new message
into the messages array. So update, we need the
room entity here as type and the property that will
be updated inside an object. So we're going to update
messages and we're going to use very specific and unique way
to update this property. So we're going to
use a method or a function to return a query. In this query will have the messages property name
and then add 2 bar here. And I'm going to explain
this just in a second. Yeah. And now let's add
quotation marks. And inside here we're going to add the messages
as a JSON string. So we're going to use the
JSON stringify method and pass the message. And then remember that we have to add messages as Jason beat. So we're going to
convert this to Jason V by adding two columns. And then, right,
Jason, be like this. So this whole string
is a Postgres query. And when we add the two
columns and the Jason be like this is going to be parsed
by Postgres as a query. And Postgres will handle the
converting of this string, of this JSON string,
two adjacent beat. And the 2 bar here means
that we're going to append this JSON B string and
to the messages property. Now by using the Query
Builder in order to add a query with
the update method, we need to return it
as a from a method. Okay, now, let me adjust this code and continue
with the Query Builder. So we're going to update a room, but we have to identify the room that we're
going to update. And for that, we will
use the Where method. And this method is going to add a where condition into the query builder and
create the where condition. We will add a string here
and say that we're going to update a room where the
ID equals to variable ID, and we can define that variable in another
argument of this method. And inside an object, define ID, and it's going
to be set to the room ID. Now, we still need to execute this query using
the execute method. And if you hover over it, you will find that this
method is going to return a row database results, but we need to
return a room type. And so this execute
query execute method, it's not going to work with
a TypeScript environment. Therefore, I'm going to return a weight and search
for the updated document. So there's the room repository and then use the
find one method. And I'm going to search
for a room with or where the id is equal
to the room ID. And also in order to
return the relations, we have to add the
relations property and specify an array or an array of the relations that
we want to fetch. In our case, we only
have one relation, which is the user's array. And that's it. This is how we can update a, an entity and return it
with the Query Builder.
33. CreateRoom resolver 1/2: Now, before we start
using the room service, we have to import
the app data source and create an instance here and pass the
room repository. So let's first import the app data source from two levels out and then
select the data source. Now here, we have to export a constant and name
it room service, and create a new instance
of room service. And pass the or use the app
data source and then dot Git repository and
pass the room entity. And here change this to export. All right, let's save
this and close it. Go back or open the rooms
of resolvers and as import the room service service
from the room service file. And here let's set the message
or send message resolver. Let's delete the
return statement. And we're going to
return the weight. The rooms service
that message to Rome and pass the room ID
first from input through my d. And then we need to
pass the message. And the message is an object
with property content. And we're going to
get the content from input that message. And also we have to
add the from property, which is the ID of the sender, which in our case
the current user ID. And we can get
that from context, that Korean user, that user id. Now we finished from the
sand message resolver. Now here, let's add
a comma and then create the create room resolver. So Async, create room, then for the parent. So here create room and
I'd parent and then add input and the
context, context. Right? So here we
have create room, but we're getting these errors
because we didn't define the creator of mutation inside
our room GraphQL schema. So let's do that really quick. Here. Let's create another
mutation and name it. Create room. And input of type. Create room, input. And let's define this here. Input, create room input. So I have here autocomplete. Very annoying. Let's fix that. And instead they
create room in book. We're going to accept
a message field with a type string and a receiver. The type int guys are going to get the ID of the receiver. Now here we have to return
back the return type. So I'm going to return
the row and then save. Go back to the room resolver. And for that we need to generate the schema or the types
from the GraphQL schema. So open your terminal and
run npm, run generate. Right now, I think
we forgot to add. Very important thing is
that our schema here, which is to make the create
room input required. Otherwise you will get
TypeScript errors when trying to use the use
the create ROM input. So here, let's run this command again and clear this or
just remove the terminal. Go back here. Now, first thing we have to
authenticate the user. So I'm going to copy all
of this and paste it here. Now, we have to check
if we already have a room that contain the
receiver and the sender. And for that, we have to create another method inside
the room service. So let's open room
service and here, create another async
function and name it, find, boom, with a user's Id. Alright, now we're going to get, or to search for user for a
room with the receiver ID, type number and the sender
id of type number as well. And here we're going to use the Query Builder in order to
fetch the users relations. So let's create a constant here and name it Query Builder. And then use the room repository to create a new query with them. Let's add an alias
here to refer to the room entity of room
table inside a database and create a Rooms constant
because we're going to fetch all the rooms that contain a user with the ID sender
ID. So let's do that. Await query builder. Then in another line, let's select everything and then use the inner join method to join all the relation inside the room dot
users property. So here let's find
that the users, then we're going to add a, an alias to refer to
the users relation. And I'm going to choose
simply the letter U here. And then we're going to add a where condition
satisfies condition. We're going to map through
the user's relations array. And that's by adding a double
quotation marks like this. You aureus, and then add another double
quotation marks with a dot here to select the ID. And then check if the ID equals to the
variable sender ID. And here we can define
that variable sender ID. And then we're going
to use the get many methods to get many rooms. And then we're going
to use JavaScript to return a Boolean here, return and then add double
exclamation marks here. Then rooms at a question mark
because we can get null. Then sum is the sum method
to check if some rooms has a very specific condition
which is rounded users. So we're going to check
if it's one of the rooms. And inside these users property, we can find at least one user that has an AD that is
equals to the receiver AD. Alright, now we have
here to some methods, but it's a gay. And now we can prevent creating multiple rooms
for the same users.
34. CreateRoom resolver 2/2: Now, if we have a room that has two users with the
receiver ID and center ID, we're not going to create
a new room for them. So let's create a constant
here and name it. Room has users. Then use the room service that find room with
user ID or users ID. And then we have to pass
the receiver ID from the input that receiver and the sender ID from the contexts that Korean user, that user ID. Alright, now let's check. If we have a room
that has users, then we're going to throw back
in YouGov KL and say that receiver already exists or
just room already exists. Yeah. So here room already exists. And that's it. Now, if we didn't find a room where the
receiver and the sender, we're going to send or create the room and then
send the message. So let's return 08, the room service
that create row. Now this method accepts
a participant's array, which is a user array. So we have to fetch users
from the user repository. And for that, we're
going to create another method inside
user service that will return multiple
users by their IDs. So let's create async method
and name it find by ID. Because we're going to a, pass an array of ideas, going to name There's
find by ID in array. And here at ideas, arguments of type,
array of numbers. And return. Await this that
user repository dot find by ID that exists
inside the ID's array. And for that, we're going
to use a method from type of RAM called in method. So make sure to import
that from type ramp first, then the ID's array. And the method will find all the users that has IDs
that exists inside the array. So we can name this
method find by IDs rather than find
by ID in array. Alright, now let's
save the user service, close it and go back here. And we're going to create
a participant, survey. Participants. And going to use
the user service. First that's imported from the off folder and from
the user service file. And then here, await user
service that find by ID. And we're going to pass an array with two IDs from the
contexts that currentUser. Going to get the user ID. From the input. We're going
to get the receiver ID. Yeah, Then here, Let's pass the participants
and the message. So the message has
two properties. First one is the content. Can get that from
input that message, and the from property that represents the receiver
or the center array ID. And we can get that from
currentUser dot user ID. And that's it for the
create room resolver.
35. Creating getRooms resolver: Now we're going to
create a query that will fetch all the rooms
from the database. So we're going to start
from the room that GraphQL schema and
define a new query. And name this query, get rooms. And it's not going to
accept any inputs, but it will return
an array of objects. Let's save this GraphQL schema
and generate the types for the resolvers using the
NPM run generate command. All right, close the
terminal in the room, that GraphQL file and then go
back to the room resolvers. And here under the mutation, going to define a query. Then define the get
Rome's resolver. And we're going to get a parent and empty
input and the context. And here we have to
authenticate the user. So let's copy this
and paste it here. Now we need to create
a new method inside the room service that will
fetch all the rooms for us. So in the top here, create a new async method
and name it, get all groups. And we're going to get all the
rooms for a specific user. So here we will accept a
user id of type number. And we're going to use the Query Builder
in order to fetch the users relations
and then return all the rooms that has a
user with this user ID. So let's first define the query builder from
the room repository. Create Query Builder, and
choose an alias room. And then here let's
use the Query Editor. So going to return
08 Query Builder. And then we're going to join the users tables inside the room that users
property of the room table. So here is inner join, join the relations tables
and select rounded users. And we're going to refer to the users array
with the letter U. And then here we're going
to use the word condition. And we're going to select
all the rooms that has a user with an ID that is
equals to variable ID. We can define the
variable ID here, SAT, this object ID
equals to users ID. And then we will get
many, many rooms. So we're going to
use that, get many. Right now. Let's
save this and go back to the room resolvers
inside the get room query. We're going to return a weight. Room service, gets all routes and pass
the user ID from contexts, currentUser, then user ID.
36. Test graphql enpoint: Now it's time to test our APIs. So make sure you have the
Postgres database running. And you can do that
using this command. Make sure to expose
the correct ports. And then come here and run npm. Run start. Alright, now let's navigate
to a polo sandbox. Make sure to sign
up and copy the JWT token inside the
connection settings and inside the
authorization header. Now, silent mutation,
we're going to test the, the send message mutation. But first we have to create another user in order
to send a message to. Yeah, let's here. Resent the signup
mutation with user to the email and the
firstName and ran sign up. Now this time,
we're not going to copy this JWT token inside
the authorization header because we have to authenticate ourselves as the first user
or one of these users. Yeah. So here we have the user with id2
and we're going to send a message him
with the, with his AD. So that's clear. This remove the signup
mutation and remove the input. Click on Send message. And the send message mutation. We're going to get the id users and the messages of the room. So I said the users. Let's take the first the
first name, and the ID. And then for the messages that's fits the form field or from
field and then the content. Alright, now for the inputs, we have to add the
message and the room ID. But we don't have rooms
yet inside our database. So what we have to
do is we have to create a room before
sending the message. So let's add the create
through mutation and add. Let me clear all of this first. And then we added
create room or the, all the fields here. And for the users, you will get the id
and the firstName, the messages we're
going to get it from. We don't care about all of
this from just message. Even here we're going to send
a message to a user. Yeah. Let me check what
fields we have side, the messages have content. Now let's go back.
And the inputs. Let me clear all of this
message and add the receiver. The receiver is going
to be user with id2 and the message just as simple
text, like text message. Now let's send or create the
room and send the message. Alright, we have here the
room object with ID one. And we have the array of users. Yeah, and we have the
ID of the user one. Yeah, firstName is one, and the second user
inside this room. So these two users now they can send messages to each other. We have here the messages
array with just one message. And this message has been
sent by the user with ID1. So the user one. Now, if we try to create another room with
the same receiver ID, we should get an error saying
that room already exists. So let's see if it works. Alright, so here we have
the message room already exists and we are safe to go. So the next mutation that
we're going to test, it is the sand message. So let me clear all of this input and add the
send message mutation. We're going to get
the idea of the room, the ID of user, and the first name of the user. And then we're going to get
also the messages here. Going to get the from
field, the content field. And then for the inputs, we have to add a message input, iran my D input going
to send a message to the room, ID one. And let's add a message, another test message,
the send a message. And here we go. We
have the same room, a room document with
the same users. And here we have added another message to
the messages array. And user2 also can send
messages to this room, but we have to sign
in as user to get the JWT token added to the authorization header
and then send the message. Let's do that very quick. So a sudden mutation. Let me clear all of this. And at the sign-in mutation, going to get only the JWT token. So here, remove this JWT token. For the inputs, we need
the email and password. All of this. So the e-mail of
fuser two is user to add email.com and the
password is password. Now let's sign in. And remember we're going to
send a message to room ID1. So let's take the JWT token added to the
authorization header. And safe. Now we are signed in as user to, let's go back to the mutation, remove the sign any mutation
and sign in inputs. And here are the send
message mutation. Going to get the
idea of the room, the idea of the users, maybe also the firstName. And going to get
all the messages. Alright, let's add the inputs. The message input,
the room ID input. So I said that we're going
to send a message to my d1. And the message is message from user to user
one, send messages. And here we go. We have this same room ID, room document to
the room ID one, and same users and a new message inside
the messages array. This is how we can send messages
using the rooms concept.
37. Implementing validation with class validator: Now we're going to add
validation to our project. And for that we're going to use the class validator packets. So Sage or a terminal npm
and style class validator. And the reason why we're going to use the
cluster the data, is because the classified
data users decorators. And we're going to use the
characters Class validator inside the entities to
check if we have e.g. a. Valid email or to apply a certain conditions
to the password. So first, to validate an
email of the user entity, we're going to import
from the class validator. Going to import the
character is e-mail. So this is a decorator
that we can use. Bake other decorators
of diaper around here. Apply the decorator to
this property is email. And you can see inside
the definition here that this decorator checks if
the string is an email. If given body is not string, then it returns false because any man should
be always a string. Now for the password, we're going to validate the
length of the password. Yeah. So here we need another, the character which
is the Min Nan. Then apply it on the
password property. So here at length. And the minimum, then
for the password is going to be six
and save the file. Of course you can explore more. The characters inside
the cluster meditator just write is like this. And you can find a lot
of the characters that to validate a lot of
things like uppercase, like checking if a
property is and URL, or is an array. And a lot of things like
even it's empty or not, or it's uppercase or lowercase.
And lot of things, yeah. So check all of that and play around with
the class validator. Now we're going to learn how
to handle the validation.
38. Catch validation errors: Now all the validation the
characters are going to be executed when we
create a new entity. And before saving that
entity into the database, we're going to use
another method from class validator to catch
all the validation error. So let's head over
the user service. And here where we created
a new user entity, let's first import the method, validate or reject from
the class validator. And we're going to use that method before
saving the user entity. Here. Wait, validate or reject. Then we're going to
validate the user entity. And then we're going to
use the catch method to catch the vibrations
or the validation errors. So here we're going to define
a callback that will catch the errors of type validation,
validation, error array. And we can import this type
from the class validator. So here, port validation error from class validator and
continue with the callback. So we're going to throw new GraphQL error if
we had any errors. Yeah, so important, the GraphQL. Graphql and going to send back a message validation
error with extension. Extensions. And we're going to send
back all the arrows. Yeah, with the code
that user input. Now we have two problems here. The first one is
that the password is going to be harassed. So if the user send a
three characters password, we will get a, we will validate only
the hazard password, which is a long string password. Yeah. And the second problem is that even though we're
throwing an error here, we will still passing a running, executing this line here, we will still saving the user. So to prevent all of
that from happening, we're going to use
the then method. And here inside a callback. We're going to take this nine. Added here. Remove the password
from the create method, and just give the aura. Just leave the sign-up
input like that. And we will change
the password here. Make sure this method is async because we're
going to use a weight. Now when we generate the
password, the password, we will take the user entity and change the password to be
equal to the hash password. Alright, now we
fixed problem one. Now for the problem to, we're going to return the user. We're going to save the
user and return the, save a document from here. And then we will
get a result here. This result is the same
that user can see here. Result is of type
user because here we returned the user from there. So we will just use the
return statement here. And that's it. Or actually we did
a mistake here. We should not use the cats
method before the then method. Yeah, So the catch
method should be the last, the last method. So here, catch any errors. So if we have any error here, we're going to send
back a graph earlier. But if we didn't
catch any errors, we're going to execute
this, go back. Alright, now we can
save this file and then run the server
from the terminal. So here, I already
have did that. And you can see that node
man is restarting all the restarting the server
because we changed the file. And now let's test our API
inside the Apollo server, I mean the affordable sandbox. So here we have our
signup mutation. So I'm going to send a valid
email and a valid password. So I'm going to send three characters password and execute the signup mutation. And we should get a validation. Here we go. So we have to get
two arrays here and say the errors
array have to object, means that we did
get two errors. The first error is
for the e-mail. So here property, email. And you can see inside the contracts we have
is email property. And with the message, email must be an email. And for the property password, you can see that we have
the message password must be younger than or equal
to six characters. So now we know that our
validation is working. You can add more validation
using the class validator. You can now validate
the room entity and play around with
the clustering data. Now, before ending this video, I want to note that if you close the Postgres database
by hitting Control C, like this, you have n and then you rewrite it again with
the docker run command. You have to close the GraphQL server by the
Control C and rerun it again. Yeah, rerun it again when now the Postgres database is ready
for accepting connections. Otherwise, you'll
get many errors and you will not be able
to work with the database.
39. why we should use ScoketIo: Now we can send
messages between users, but in order for a user to
see the latest message, he has to reload
the browser or e.g. here, we're using sandbox. We need to fetch all the
rooms in order to see the latest messages sent by
the participant of that rule. And you know that users will not appreciate that because all messaging app now
have some sort of real-time data transfer
mechanism so that users can get and read new messages in real
time without light, refreshing the page or closing an app and reopen it again in order to fetch the rooms and getting you the latest messages. And that's the feature that we have to implement
in our application. So we're going to use Socket IO, a very popular
library that enables real-time communication between
web clients and servers. You can navigate to
socket dot io and read the documentation and
see some examples. Yeah, but of course
we're going to work with Socket IO in this project, and we will start
in the next video.
40. Join rooms with socketIo: Now let's start by installing
socket IO from MPM. So open your terminal and
run npm install socket IO. Alright, let's now
open the module, the ts file and implement
the socket IO server there. Here let me close the terminal. Open modular tears fire. And the first thing we
have to do is import the server class from
from socket that IL. Now we're going to implement
the socket IO server inside the startup auto method and implemented with
the HTTP server. So here we're going
to create the A0 constant and create a
new instance of server, pass the HTTP server. And now we have to start
listening for connections. And we can do that
with the on method and listen for the connection
event here, connection. Then we're going to
add and go back here. That will get a socket. And we will do something
with that sort of gets. So in order to be able to use the socket inside
all our resolvers, we're going to put it
inside the request and we can get access to the
request from the ad constant. Then request and create a custom property and
name it socket IO, and set it to the socket that we
received from the client. Now we have to add the socket IO property to the
request interface, and we're going to do that
inside the main.js file. So let's open it. And inside it, we're going to declare a
global variable or namespace. And we're going to select
the namespace express. And inside it we're going to select the interface request. And here we can add the socket IO and make it as not required. And then the socket IO is
going to be of type socket. We can import this type from
the socket IO package and then save this file and then go back to the
module, the ts file. And you can see
that the error is, the error has disappeared. Now. Because we're using
the rooms concept inside our application, we can do the same
thing with Socket IO. So we're going to use
the client socket to join all the rooms
that the user have. And then whenever we emit a new event to one
of these rooms, and of course, I've a socket
didn't join the room, it will not get the event. So we're going to do all of
that inside the context. And here we have
to check if the, if we have a payload. Yeah, if so, we're going to fetch all the rooms
from the database. In order to be able to do that, we have to create a constant here and get the updated
source instance. So here, create our data source. And we're going to use
it to fit all the rooms. So here at Data Source, dot manager, going to use the manager and then
Git repository. And we're going to get
the room repository. So import the room
entity and use it here. Room for that from here. And port the room entity. From the rooms folder
inside the room. Select the NSAID entities
like the room entity. Now here we use the room to
select the room repository. And then we will use, we will create a query builder, an alias for the room. And then we're going to select all the rooms that
contain the user ID. So for that, we need to use inner join to join all
the users tables from the Rome dot users and add a
alias for the users array. And then add a where
condition here and loop through all
the users array and only select everyone with a user ID equals to
the ID variable. And we're going to get
the ID of the user from the payload, that user ID. Alright, now we will add another method here
to get many rooms. And here let's create a
constant and name it Rooms. And wait for the
result. Alright. Now here we're going to get
an array of froms objects. And then we're going to use the socket IO from the
request to join them. So here, Quest Dot
socket IO that join. And this method is
going to allow us to add rooms to the socket. And you can see here that we can add either a single room
or an array of froms. Now, we need a unique
reference for, it's true. For us. We have the room ID as a
reference, but it's room. It's, it's room reference
should be a string. So here we have one string and here we have an
array of strings. But as you know, the rooms IDs are numbers. So we're going to
convert them to strings. And we're going to create
an array of strings IDs. Here, create a Rooms IDs, and then take the rooms
array and map through all the rooms and return a string using
the literal string, and return the room dot id. Now we have an array of strings. So let's pass it here. Rooms IDs. Alright,
now we finished. But here we have an error saying that the
payload doesn't have a user ID property and
that the payload is of type string or a JWT palette. We can fix that by
checking if the type of payload doesn't
equal to string. And we have to check
if we have a payload. Yeah. Then we can use
the user ID property. Now, this is how
we can join rooms. The next video, we're
going to learn how to emit events to a specific group.
41. Emit messages to a specific room: Now, in order to emit events, we have to use the IO constant, which represents the
socket IO instance, and we have to pass it
to the return value of the contexts methods so we can use it inside our resolvers. So here inside this object, add A0 and save the
model to ts file. Then inside the room
dots service file. Here we're going to use the IO instance inside the
ad message to room method. So here let's add
as an argument of type server and make sure to
add the question mark here. Because sometimes
it can be known. Now for the server here, can import this from
socket IO package. And then after we update the room and add a new
message to the message array, we can check here. If we haven't IO,
then that case, we're going to emit a new
event using the image method. Now this method
accepts two arguments. The first argument is
the name of the event, and the second argument
is any type of data. It can be an array or object, or a string, or even a json. Now again, Jason is
just the string. So here we can name
the event message. And for the data here, we're going to send an object containing the message
and the room ID. Now, in this case, we're going to emit this event
for all connected users. But we don't want that. We want to emit this
message to a specific rule. So we're going to use another method here
before they emit. This method is the method and pass the room
ID as a string. So let's use the literal
string and add the room ID. Now, we will emit this
message, this event, to only two users who have
participated to this room. Now, let's save this. And inside the room that resolvers and inside the
send message resolver. We're going to pass the IO here past the AL from the
context that I0. That's it. Now, let's save
this and that's the APR.
42. Testing socket Io server: We cannot listen for events
using the Apollo sandbox. That's why I've created a very simple
application where we can send and receive messages
by listening for events. And all of that logic is
inside the index.js file. You can see here that I'm
connecting to the server. And here I have some queries and mutations to send the message. And here I'm listening for the Connect event and
for the message event. And as you can see here, I'm parsing the data
into the HTML document. So to run this server, you have to have an extension
called Live Server. So here you have to search for the live
server extension and install it into your VSCode. Then you will find you would get an button here.
Let me disconnect. Yeah, you'll find a
button that says Go Live, click on it, and then you
will have a live server. So let me rerun the server and start testing
the application. This is it, this is
the simplification. I only have a place where I have to put the JWT
token and sign-in. Then here we will
send the messages. I have two browsers here, so two instance of
the same application. Now we need to enable
course inside the socket IO server in order to be able
to connect using this URL. So copy this. And then here inside the
module, the ts file, and inside the Stockdale server, Let's add a new object and
set the course option. Set the origin to the
this URL, and save it. Then let's go back here
and make sure that you have restarted your server. And we're going to get the
JWT token from the sandbox. So here, make sure
to sign in and then take the JWT token and copy it, and then paste it in one
of these applications. Click on sign-in. And then I have signed in as another user and I
saved the token. So I'm going to paste it here. Make sure to do the same
thing. Click on sign-in. And now let's test the server, go into sand high
and it didn't work. And that's because
here, alright, we have to remove the sludge
from the end of the URL. So let's go back to the
module, the tears far. Remove the sludge from there, and make sure that the
server has restarted. Let's wait and then test again. Alright, you can
see that we are now connected because we have
the connected message here. Now, if I click on sand, we should get high here. And here maybe we have to
reload the application. So let's copy the JWT token, reload and then click
on sign in again. And maybe the same
thing here because we have to join to the rooms. And before, I think that we
didn't do that in the server. So let's sign in and send high. Alright, we got the
same message inside the two instance here that
send here like hello. And here we go, our
application is working. Now as I said in the
beginning of this video, you'll find the source code attached so you can download
it and test it by herself.
43. Database migration with typeorm: Now we've finished
developing our API, but we still need to
handle one thing, which is the case where we
want to update our entities. That case, we also need to
update our database. So e.g. if you want to change here
firstName to only name, of course we need to
update our files, but most importantly, we need to update our database tables. Now, if we sold it in push our project including the
database into production. So we don't have
any importing data. We can shut down the
database and rerun it again in order to remove all the tables and
create new ones. But if we already pushed our
project into production, we have active users, e.g. that case, we should not follow the first
approach because we don't want to lose
all of our users data. And also it's going to be a
big disaster for our app. So what we should do here, well, the app data source, we have this property
synchronized set to true, means that we're going
to update our database tables every time you make
a change to our entities. This substance is
going to only change names and types of some columns, but it's not going to
preserve any data. So this option is
as dangerous as shutting down the database
and rerun it again. So long story short here, the only way to
prevent data loss is by using something
called Database Migration. There is a lot of
ways to do that. But with type ramp, we can automatically
generate files that contain secret queries that will be responsible
for the integration. So in a sense, the synchronize
option to false and safe. And you have to make
sure that we have a running database using
the Docker command. And we have to run our server so we can create the user
tables and the bone types. Alright, now let's go out to
the user entity and change firstname to name and then
generate a migration. So here, name, save this, and let's update
the GraphQL schema here from firstName to name. And the same thing,
site the user type, firstName, tunic, Save. And here we're going to
generate the schema. It failed because we have to generate this came
out by ourselves. So here npm run generate and then we're going to
run the server again. Alright, npm starts. Now we're not going to update the database
because we have the synchronize
option set to false. So we're going to wait
for the server here, and then we will generate
the migration files. Alright, now let's
open another terminal. And order to generate the
negation of Phi is we have to install that program globally inside our project
using this command. Yeah. And sometimes you need to add the keyword in order to
gain admin privileges. And if you're inside Windows
machine and you have to open a administrator console. So I'm not going to do
this because I already on start type ramp globally
inside my system. So I'm going to proceed
to the next step, which is using the NP x, which is a package that comes by default
with no GS and NPM. And I'm going to run
tape around using the ts node and CommonJS
module for JavaScript. And we're going to
run regression, Carlin generate, and specify the location
of our iterations files. I want them sites source folder, and inside a Migrations folder. And then we will get files with timestamp and migration keyword. So we're going to get
files with timestamp, does regression dot Yes. Okay, Now we have to specify the location of the
app data source. So here, source of Dutch data
source that yes, that's it. Let's run this command.
And here we go. We have successfully generated the immigration
fights or one file. In this case. This file is a TypeScript file with a class. And here you can see the name
duration and a timestamp. Then here we have two
methods up and down. So the add method is going to be executed every time
when we run this, every time we run
this migration. And we're going to execute this sequel query that will
change firstname to name. Now the down method can be used to reverse the immigrations
as represented here. You can see the query here that will change
name to firstName. So if we change our mind and we want to revert immigration, we can execute the town method. And of course, all of
this is going to be executed automatically
using the type RAM CLI. Now to run this regression, there is two ways to do that. First one, we can use the same
command here but changed. Just a few things like
keeping the data source and moving this nine here and
change your rate to run. That's it. And we can run the
immigration and we're going to get a updated database. Can do that, or you
can run the migration inside or from tape around automatically using
the data source here. And by adding the immigration, immigration is run property. And we're going to specify
where our migrations located. So because tapeworm here
cannot run Ts vice obviously. So we're going to point to the dist folder that contains
the GS migrations, fats. So here, this and then source, and then inside
emigrations folder. And then run all files that has a Dutch migration were gs and ends with the
dot js extension. Yeah, so let's save this. And actually it's not migrations, Run
button integrations. That's it. We're going to use the
immigrations run to specify that we want to run all the migrations when
we run this server. So here set to true. Now let's save this and going to see that our
server is restarting. Alright, now let's
try to sign up. And this time we're not
going to use firstName, but we're going
to add just name. Right here inside the
sandbox, Apollo sandbox. Go to a mutation and select
the signup mutation. So here, signup
mutation and wait for the JWT token or the inputs. And you can see here that
we updated the graph gear, but doesn't mean that we
updated also the database. But we're going to check
that in just a minute. Yeah. Let's add
all of that here. And let me expand this. Alright, choose any email. Email, email.com, lastName,
user name, say John. Password. Password.
Let's sign up. And we should get the JWT token. If we successfully
updated database and we have
successfully done that, we have here the JWT token, and we have a user inside our database with the new property name
instead of firstName. So let me take if we can
return the user here, alright, we can, so let me return
all of these fields. And I'm going to sign up
with another email may have one user one, and that's it. Let's sign up. Here we go. We have here
name set of firstName. So we have successfully updated our database using
the immigration. So this is how we can do that. But makes sure to set
this to false always. Or it can just
remove it from here. I'm going to keep it
just to remind you that this property
has to be false. And for the immigrations, we need to always
execute GFS files. And you can see inside the
dist folder that we have immigration folder in here we have two files for
the immigration. And that's because I tested this API and try to
run the migration. But when we have multiple tiers, migrations files, and
you delete them here, doesn't mean that
it is going to be deleted side the dist folder. So always make sure to remove
the dist folder here and make sure to generate a new one every time you run the API. Otherwise, you will execute all the integration files and you will corrupt
your database. And remember also to
do the same thing. Site here removed immigrations because we already
executed them. But if you want, you can keep the immigrations files
inside another location. Yeah. Not not necessarily. Side the project folder. That case, you can use the down method to revert the immigration
if you want to do that. Yeah. So for us, it's not a big
deal because we're just changing firstName to name. So I'm going to delete
this whole folder. And that's it. Now every time when we want
to migrate the database, we will get a new files and run them from the
type around data source. Now, before ending this video, I want to mention one
thing which is if you want to run the migrations
using this command, you have to have the migrations property inside the
data source, right? And you can add immigrations run or just remove it from there. Does it matter? But order to run this command, you have to have the
immigrations property and specify the migrations location. And that's it.