Especially if you want to use a TypeORM in production this is the video that you definitely should not miss all right miss.
Особенно, если вы хотите использовать TypeORM в продакшине это видео, которое вы определенно не должны пропустить
Sorry, I don't have a legit intro lol.
Извините у меня нет нормального интро, lol
All right. Hey guys.
Все впорядке. Привет ребята
i've been making a lot of sgs content
lately and i've been getting some pretty
good feedback in the comments
i very much appreciate those comments
something that i
always use in a lot of those next gs
tutorials is typo rm
which i never really went into a deeper
detail
deep dive um explaining how to use it
and how to use it with different
databases and stuff like that so i
figured i'd make this
specific video to cover just type orm
and how to work with it and add in some
details that i didn't cover before
especially if you want to use
a typo rm in production um
this is the video that you definitely
should not miss
so we're gonna split up this video into
five
uh core parts part one is gonna be set
up and installation how do you connect
your database
part two uh we're gonna talk about
uh creating your entities your tables
how do you write migrations
which you absolutely should know if
you're gonna do production work
and then we're gonna talk about
utilizing dependency injection to inject
our
entities into our services which then
allows us to do the actual querying
which goes into our part four
which is the repository api which uh
that's where i'm gonna
cover things like how do you query your
tables how do you insert how do you
update how do you delete
you know basically your crud your
typical crud operations
um those are good fundamentals to have
i'll also cover
how to create custom queries and then
finally we'll talk about
in part 5 relations you know if you have
obviously in a relational database
you're going to have relationships
between your tables so how do you go
and uh get those relationships how do
you do joins and stuff like that so
we'll cover that
in the last part all right so let's jump
right into
part one you know set up installation
you can see that there's an
npm install script here that we're going
to copy
so i've got a basic skeleton application
here where i use the
nscli to generate this so it's just got
a basic module and a controller and a
service
i'm going to go ahead and paste that
install script
you'll notice that i didn't copy the my
sql 2 dependency
i'm going to use sqlite here
and with typeorm it has really good
support for
pretty much all of the main relational
databases that you can think of like
postgres mysql
i personally use sqlite for these
tutorials just because it's so easy to
set up
you know i just install it via npm i
don't have anything else to
to configure but you absolutely can use
this with your own
uh relational database of choice
all right so once you've got typeorm
installed
in the root app module you'll then want
to add
typeorm module
dot for root
and this expects that you're passing in
an object
that will be forwarded down to your
database driver
so for example if let's say we were
using mysql
right you'll see that in their their
example over here they're using mysql 2
as their database driver if you go into
the documentation for mysql 2
you'll notice that for for you to create
a connection you're passing an object
like this
which has at minimum host user
database you know it might also have
password and stuff like that
so that's the object that it requires
for that driver
so that's the object that you would pass
into
this for root method similarly
maybe if you're using postgres you know
you might use
the the pg package as your driver
and that's going to have its own
requirements for what should be defined
inside this object
now what i'm actually going to do here
is i'm going to create a new file in the
root
called orm config.ts
and i'm going to create the config here
and this is specifically because later
we're going to use the type ram cli so i
want
i want this config to be accessible
on that as well so we're going to import
config from
oram config and then we're gonna pass
that in here
and then we'll do our uh database
connection definition
over here so i mentioned earlier that
this object really could be
almost anything depending on your
database driver
one nice tip that i can add here is you
can actually
specify a specific type for that
connection object
so for example if we did have a mysql
database you can do something like
mysql connection options
and it's going to give you the shape of
that object
which has you know type mysql or mariadb
and then you'll also notice that if we
go into the
interface that it's extending right it's
asking for those things that we talked
about earlier like
url host username password
now in our case we're not using mysql
where i said we're
using sqlite so we can do sqlite
connection options
and you can also take a look at the the
shape of that it just requires
a type of sqlite and a database string
as your primary required
property so we're going to do type
sqlite
and then database and i'm just going to
put db over here
so this string here is going to be the
the name of the file
so sqlite kind of saves your database
into a file
so it's going to be named just db and
you'll see that in a little bit here
and then the main thing that we need for
typo rm specifically is an entities
array
this src
and then anything in that
it has entity in its file name
we're going to assume is an entity
so think about it as we're we're
following a convention and we're using
that convention to identify
which of our files represents our
database schema and i'll show an example
of that in a little bit here so another
option you can add here is
synchronize which you see is
it indicates if the database schema
should all should be auto created on
every application launch
um it also says be careful about using
this in production and really you
shouldn't you shouldn't use this
in production because what this will do
is
it will look for your entity files and
it's if it sees that
your schema has a mismatch with what's
in the database it's automatically gonna
synchronize your your database to what
your schema is so that could mean
if um you deleted an entity it will drop
a table
so you got to be careful about making
sure you don't have this to true in
production
so this is really only uh should be
utilized for local development you know
if you're just doing a quick
poc and actually let's go ahead and do a
quick example for you to
so you can see this in action all right
so in our
src folder we're going to create a new
user.ntt.ts file
again following that convention of
having dot entity
in the file name if it's an entity so
we're gonna do export
class user
[Music]
and we're gonna decorate this with
entity
so what does that mean it we're
basically telling type program that this
class represents
the shape of our user table in the
database
so for example maybe you have an id
in that database and it's auto
incrementing integer
so there is also a decorator for that of
primary generated column
so we can do id number
and then maybe we have a second column
for
a name which is just a string oops
and you just have to add column here and
that's going to turn into a varchar
column in the database
so now we have this basic entity in our
database configuration we're saying that
it's going to look for entities in the
dist
so what that means is we need to have a
build so if we run build
when we run build we're going to have a
new dist folder here and it's going to
have
src and it should have an user entity
that javascript there
so that's going to be found by this
glob pattern so let's go ahead and run
our application
which part of the thing that does is it
makes a a dist
a build for us on the fly and then what
you should notice
is that it creates a the first time you
run this it's going to create that db
file over here in a root
right because that's the name that we
provided here now let's go ahead and use
i have dbver here db
the dbeaver however you want to say that
and you can use this to or your own
whatever database client you like to use
to
connect to your databases um let's go
ahead and create
a new sqlite connection
which is in this db file
i'm just going to open that
and when we go in we can see the tables
and there's the new user table there
which has columns id and name
right so let's go ahead and let's try to
simulate the synchronize happening here
if i were to for example add
a new column here
of something let's see it's just another
string
the next time that my application reruns
if we go in here and do a refresh
[Music]
now there's a new something column right
so that's what what that's what i mean
by synchronize is to automatically gonna
make your database
match with your um schema that's
represented by your entities
again that's very good for local
development very bad for production
all right so let's talk about how do we
fix this for for production right
we don't want synchronize to be turned
on so we'll do false
in production what you should instead be
using is
you should be using migrations and
migrations are basically
you can kind of think of it as your
you're creating
sql scripts that represent the changes
that you want to run into your database
so how we're going to accomplish
migrations is
actually through uh typewrim itself it
has support for
for migrations so to accomplish it first
you need to install
uh typorm globally so that you have
access to the cli
so npm install dash g type 4m
and then you're also going to want to
global install ts node
that's going to allow you to run the cli
against
typescript files and then it also tells
us that we should add
this type or m script into our
package.json so i'm going to do that
next
now once we have that the next thing
you're going to want to do is
back into our connection options we're
going to do
a migrations property here which you
provide
an array and this is going to say
it's going to tell type 4 and where do i
look for migrations
again we're going to do this src
and then we're going to say that you
should look in db
migrations and take any file in there
that
any file that's in there now that tells
type or
where are the migrations located we also
have to tell the cli itself
where to put the migrations so we're
gonna do
migrations directory
src db migrations
right so we're trying to get it into
this folder right here
so let's give this a a shot again
remember that synchronize should be
false at this point so
what i'm first gonna do is i'm actually
gonna
delete this old tv that i have in here
and then i'm gonna start dev again to
recreate it so that we
we start with a fresh sqlite database
okay so you'll see the db got added
again
now i'm going to close this
we're starting from a fresh database
let's go ahead and write our first
migration so one of the cool things with
typo rm is it can
auto-generate the migrations for you
again
this is why it's looking for um the
entities it can look into
the entities that you have and then
looks into your database and then it
kind of does like a diff
it tries to figure out what's the
difference and then it's going to
automate
auto create a migration for you so let's
go ahead and see that in action
so in the package.json i'm going to
create a new
command here i'm going to call it type
or
m or sorry i'm going to call it
migration
colon generate
and we said that in our configuration we
want the uh
we want it to look in our dist again so
that means that you probably should run
a build first
and run build and then
from here we're gonna do
npm run type orm
migration generate and then dash dash
dash n so it's really just this type run
type rn piece is running this script
and then uh we're doing a dash n here
because we're gonna need to pass in a
name uh so i know that's probably really
confusing but
once you kind of see it running it makes
a lot more sense so
and actually i misspelled migration here
so let me fix that
now once you have that you can go into
your terminal and just do npm run
migration generate
and then we can pass in a name of
you know maybe we do user migration
because we created
a user entity recently
and you can see that it does the build
and then eventually it's going to run
this part of it
and notice that it's passing the user
migration string here
into our dash n
flag here so what did that do if we look
into the src folder we now have a db
folder in there
that also has a migrations folder and
then it created this file
right so it auto generated for us a
script that tells the database hey
create table
user with these columns
basically matching your entity and you
also notice that there is an up
and there's a down the down is always
the inverse of whatever you're doing in
the up
all right so now that we have this
migration let's go ahead and
figure out how to run this so back into
our package.json i'm going to create
another script in here
for migration run
and similar thing we want to run the
build first
npm run build and then after building
we're going to do npm run
type rm migration run
all right and let's go ahead and give
that a shot i'm just going to run it
in this utility here
all right it's an run migration run
same thing it's going to do a build it's
going to find our migration
and then it's going to pass it down to
type our migration run
it's going to run it and you can see it
in the
the terminal here it's doing a bunch of
stuff
right it's starting a transaction to
create our user table
and if you go back into our
db beaver right remember i
i deleted the the user table uh
previously
so it's not there and when i refresh
this because our migration ran
we now have a new again it's back
the user table and then if we actually
select
our
migrations table which it also creates
it
has it kind of uses that as a way to
track which of your which of your
migration scripts are you
already ran and which ones didn't
so if it notices that the next time you
run migration run and there's a new
migration
and it hasn't been added to this table
yet then it knows that oh now i need to
run that migration to
synchronize our our database
so again every time you wanna kind of
update your database all you need to do
is
you know update your entity files create
any new ones that you want and then you
run that generate script
and then it's automatically going to
create the mic the migration files for
you
and then you just trigger a run and it's
up to you when you trigger that run so
for example
maybe in production you wanna as part of
your
continuous integration or continuous
deployment
you might wanna run your migrations
prior to
deployment alright so hopefully that
made sense uh let me know in the
comments if that was confusing
i can be confusing for people that are
new to it
you also can create your own custom
migrations
if you want so maybe you want to insert
data as an example which is not going to
be represented by your entities
right like if you want to seed your
database tables you can create a manual
migration where
you know you just write your own sql
basically
just make sure to add that to the
migrations folder
and actually there's also a
you can also do type or migration
colon create and that's going to create
a new file for you that just has
the up and down methods all right so for
the rest of this tutorial i'm actually
going to turn this back
to true just because it keeps things
simpler
so that i don't have to keep writing
migrations and writing migrations
throughout this tutorial it'll just make
it
uh it'll just make it so that our
changes automatically get reflected into
the database
and that's what i want for a demo like
this all right so that concludes
the first two parts of this video you
know we covered
how to connect to the database and then
how to create
your entities and effectively represent
your schema
for your database and then how to get
your database to match that schema right
you either synchronize or you do
migrations
all right next what we want to do is how
do we then
utilize these entities to um
create queries against them so for
example maybe we want to query
all of our users all right so first
thing that you want to do
is within the module you're working with
in this case we just have this one root
module you
ideally should be creating a a new
module for each domain
so in in typical development i likely
should have created
a user module right and then that entity
would live inside it
but we're just going to stick with this
root app module for simplicity
but you know uh just go with me here
imagine that this is a user module what
you're gonna do is
you're gonna add typewriter module for
feature and then
this takes an array of entities so we're
going to add
user and what that's going to enable us
to do
is within our services or providers
we can then add to a constructor
and you can do something like inject
repository
and then we're gonna do private users
repository
which just has a type of repository
of entity user and then i actually
forgot that you also have to pass the
uh the entity itself in here which we
also forgot to import right so we're
importing our user.entity
we're passing it in here we're creating
a new
variable in this class a member in this
class called user repository
it has this type syntax is a little bit
verbose but
it's not too bad so as an example let's
start talking about the repository api
so let's do the basics first uh perhaps
you want to do
a get all right you want to do
get all users which returns a
promise of collection of users
how do you do that you do this dot
user repository and let me actually make
that plural
users
alright usersrepository.find so
that find is effectively equal to select
star
users select star from
user right if you know your your sql
and make sure to return right so
there you go how about how do you find a
specific user maybe you want to do
get one by id which
uh we said that our ids are integers
so maybe we do something like this
returns a promise of a user
right and you can do
this.usersrepository.find1
and you can pass in the id directly like
that
you can also do find one or fail
which i personally like because if you
just do find one that would be
equivalent to
again select star
from user where
[Music]
user.id equals the id that you passed in
right which i select if you if it didn't
find anything
it's just going to return nothing which
is
probably not the behavior that you want
in a typical application if you couldn't
find something you probably
want to communicate that to the user in
some way so you can do find one or
fail which what this will do is it's
going to throw an error
which you can then wrap this if you want
in a try catch
right like this
right so if we were to put
this in here
if you were to await be sure to add your
async
right if it doesn't find it it's going
to throw an error and then you can
handle that
in here if you want or you can throw the
arrow back
right
um so that's how i like to do finding by
one i always almost
almost always use or fail because i want
to communicate when i can't find that
thing
all right next let's talk about how do
you
insert how do you add to our table
to our user table you can do
let's do a create user method here
which uh probably takes in the fields
that you want but let's say
for now we just want to take in the name
which is a string and we want this to
return the newly created user so this is
also going to be a promise of a user
in our user entity we said that it has a
name property
i'm going to delete this for simplicity
so it's just going to have an id and a
name
so to do that to create a new one we're
just going to do
new user equals this dot
usersrepository.create
and then you pass in the the fields
that you want to specify so in our case
it's name
and what create is it's effectively
equivalent to something like
constant new user equals new
user right and then
you know it does new user dot
name equals the name that we passed
right so it's kind of a shorthand for
instantiating that class
now once you have that creation that
doesn't mean that it's saved in your
database you still have to do
this.userrepository.save
and then you pass in your newly created
entity that's the so if you look at the
documentation for
save it says that it saves a given
entity in a database
if it does not exist it's going to do an
insert
otherwise it's gonna do an update so
that means you can also use this for
updating the user right so this is gonna
do
insert in
our specific case here because we're
passing in
a new that doesn't have an id
so that's going to do a database insert
since we sort of covered it let's do
update you know maybe we want to allow
for
name changing so let's do update user
um you know
let's do name again here and again we
probably want to return
the updated user right like if you have
a
client you almost want to get the new
data back
now kind of similar thing we saw that
the save
can do an update for us
so we're going to copy that but we're
going to do
for us to be able to do this we first
need to find that user
right in a database which we already do
we are we've already handled in
our code over here so you can just do
const
user equals await let's add our async
this dot get one by id
and then you pass in uh the id which we
actually are missing in
our parameters here
right or you can also use this directly
if you want it's it's
basically the same thing um but if
you're doing error handling like this
you know it might be good to reuse so
this gets back our entity it queries it
for us and then we can do
our manipulations our updates
so in this case we're saying that we
want to we want to allow the
application to change the user's name so
we're going to do user.name equals
the name that passed in and then we're
going to save it
so the save is going to trigger a
database update
and it should update that row and maybe
i'll demo this
a little bit later but i want to type
out a lot of this stuff first
all right so you can probably kind of
see where i'm going with this
i'm i'm effectively covering how to do
crud
right so this is your um your reading
this is your create this is your update
and then we just need to do
a a delete
so we can do delete user
same thing we want to know who to delete
and then in this case we probably
want to return the deleted user
so you can kind of do a similar thing
here where you query the user first
and then similarly this should be async
because we're awaiting
and then you can do await
this.usersrepository.remove
and we pass in our entity
and then we're just going to return it
again because we want to communicate to
our client
the object that we just removed or i
think you can actually just
return this directly because it looks
like
remove returns that same user entity so
we can
delete this all right so that's
basically your crud right there
um if you're looking at the type
rm documentation you likely will also
notice that there's
there's also in an insert method
so like this
but the reason we don't use insert is
because
that what this returns is a promise of
the insert result
not the actual entity itself it'll just
say that oh it inserted
[Music]
one row or something like that but it
doesn't return actual user that got
inserted save on the other hand
does return the the user that got saved
they knew that row that got updated or
created so
in in a crud scenario where you're
creating and almost always you usually
want to get
back the thing that you just created
the create and save combo is almost
always
the one you want there kind of similar
thing for what we're doing
with the the update and deletes here
like i think there's a delete
um method where you can just pass in
the the id
right so you can pass in the id but same
thing if you do it that way you get back
the delete result you don't get back the
deleted
entity and there's a lot of scenarios
where you want to get
back that that entity that you deleted
updated or created
because you want to inform the client
or the user the user interface um
on what data is updated right so
i personally will use remove there
but could be your own you know your own
preference
it's also good to know that using the
repository you can also create
your own custom queries so for example
you know you can do something like user
repository dot
create query builder and you can pass in
an alias here so in our case let's name
this
user and you can do all sorts of things
with this like for example
maybe you just want to select the name
uh
maybe you want to do a left join
or maybe you want to do a aware
right so this is a good if you know sql
well and you have a very custom query
and you just want to represent the the
sql that you know
into the javascript api
um this is a good this is a good
approach right so it has a lot of those
things that you would
uh probably care about like order by
right group by kind of those core
sql uh statements
and there's really good documentation
for that in
the type or um
you know query builder documentation
right it's got all of this stuff
right so take a look at this
documentation if you want to learn more
i can't really cover this because
there's so much it basically covers like
almost everything that you can do with
sql so you can kind of just think if you
have very custom queries that's not
something you can do with the repository
api
query builder is your best friend there
alright so let's maybe go ahead and test
the stuff that we have so far
we have a basic app controller here
let's go ahead
and oh we already have our app service
imported there
let's maybe change this to uh
do something different so right now if i
run this
when i run this and go to localhost it's
gonna give me that hello world message
and that's because of the
the get hello that we have here let's go
ahead and just change that to
maybe
you know maybe we're going to create a
new user and
we're just going to return that so as an
example let's do
return this dot app service
dot create user
and let's just pass in a name here of
um let's just do my name
and then this is complaining to me
because i did not import
the user right so when i call that again
i expect that
it's gonna add me to the database so let
me
hit refresh here right and then it
returned us
a that new entity that got created and
you can go
back into your database viewer if you
want if we query
our user table now you should see that
same
brow all right so we know that our
you know our user is getting created you
know maybe let's do another one
and then see if we can do an update
right so we're gonna create a new user
with name test and then we're gonna do
this at app service dot uh
update user
which requires an id so we actually want
to do
something like this and we're going to
immediately update that name to
something else why don't we go ahead and
just return this
so what i expect to happen when i
refresh the page again is it's going to
create a new user with name
test and then immediately it's going to
update it to something else
so i'm gonna hit refresh and we got back
a new row and his name is now something
else it
ran very quickly but if you slow it down
you know you add some um
some timers in there if you want you
will notice that it first does
create with tests and then ultimately
updates so if we query the user table
again now we got those two
and then you know just to make sure
everything is working why don't we go
ahead and
do our delete um let's just do
delete user and then i'm just going to
delete the first one which is
the one that has my name on it again if
i go back to my browser and
refresh it returns me that new deleted
entity
and if we go to our database and we do a
requery here
now we just have that second row the
first one got deleted
right so all of our stuff
is is working you can also do i don't
think i did
the get all
and now this is going to return a
collection of users
and i expect that when i refresh i am
going to get back
an array of users right so all of our
crowd stuff is working
obviously i did not create this in in
proper you know uh you
i really should have created a user
module with its own user controller and
its own user service but
where in this tutorial were specifically
uh learning
the type orm repository api and stuff
like that
um make sure to check out my other
videos where i do kind of show you how
to do this properly with
you know the domains for user and having
its own module and such
alright so finally i think the last part
that i said i'll cover
is relations so obviously with
relational tables right relational
databases you
you have tables that likely have
relationships between them so for
example
maybe a user has a pet so let's go ahead
and create a new
um pet entity it's going to do pet
entity dot ts
and kind of similar thing we're going to
do export class
pet
wrap this in an entity we'll just copy
the same fields that the user has
let's say that this bet has id and name
and make sure to import any
missing imports and in the typo rm
documentation it goes into
a lot of stuff in more details regarding
relations
uh you know so you can if you know your
your sql
relationships well you know that you can
do stuff like one to one many to one one
to many
many too many and depending on which one
you choose it's gonna type where i'm
just gonna figure out for you how to
represent that in the database
so for example if you do a many-to-many
it's
going to create a new intermediary table
for you that represents the
the relationship between your two
entities if you do
something like a many to one which we'll
go ahead and do that actually
[Music]
or a one too many it's it's gonna add a
new id property in that table that
represents the relationship
so let's go take a look at what that
looks like
so for example we know that a
pet has an owner
of type user
and we can say that we have a many to
one
relationship here and you have to
provide the type
which is user and then you have to
provide
in that entity what's the matching
property in this case it would be user
dot
pets and we got a red squiggly here
because that doesn't exist yet if we go
into
the other side you can say that the user
has pets
of type pet right and you can do
your um one to many over here which is
the inverse relationship
which has a type of
pet and we're gonna do
within those path we do owner
all right so all of our squigglies
should be gone
so now that you have that relationship
uh one of the things you can do
if we go back to our um
our service here in our user repository
within the find it has a special
property that you can pass in here
called relations
and you can pass in an array of
relations in this case
we maybe want to pull pets
right we have this new property and what
what this is going to do is it's going
to do the select start from user
and then it's also going to do the join
to get the rest of the paths right join
with paths
so to do a quick example here let me run
the application
back in our controller since we're
already doing get all let's take a look
at what that
returns at the moment so it's going to
return the person with the something
else name
and it just has an empty array of paths
so
just for the sake of time i'm going to
go ahead and
manually add a new pad in here obviously
you can go through the same exercise we
just did with
creating a a service for like a pet
service that is able to create pets and
stuff but
i'm going to cheat and i'm going to add
a new pet in here instead
so i'm going to do a new insert into pet
here with name
buddy and i'll use an id of one there
if we do a select of our pet table now
we got our pet there
actually this owner id should be two
because we
we deleted owner one right so in our
user table we have
uh just that person with id2
so we're saying that this pet his owner
is this guy with the name something else
so
now if we go back into our application
if i refresh here
there you go now we have we're able to
query the user
as well as his or her pets
and all of that all we had to do was add
this array so it's super
that's something that's super convenient
with with type or m is that it's able to
pull in relations for you like that all
right so that's really it that's the
basics of it you know you obviously can
change this as
needed like for example maybe if a pet
can belong to multiple users right like
if it has a mom and a dad
um this probably should be a
many-to-many
relationship and then you can also
specify in here stuff like what happens
on delete
right like if you want to do like on
delete cascade and stuff like that you
can do that
in the configuration of this uh
decorator here
alright guys so that's it for this video
leave a thumbs up if you found it useful
drop a comment if there's anything that
you feel i should have covered or if
there's other content that you want me
to cover in the future
i would love to know of of whatever you
think
uh anyways thanks and i'll see you on
the next video
[Music]
you