Always a pleasure following along or simply learning something new. I see your name even made it to the TypeGraphQL Getting Started page. Thanks for this series!
I'm new to TypeORM and need some feedback on the following: I have a User and an Employee entity, the user has a field called employee_id through which I can include that relation, however, I am unsure how to include the user from the employee side (include user when querying the employee). How do I tell TypeORM in the entity class - to link that field to the user where the user.employee_id === this.id? Thanks Ben. -> gist.github.com/olafwrieden/708eeefda40b571a81a3dbd7c59eb006
Hi Ben, great series. Thanks! For those using tsconfig.json with "outDir: ./dist" and creating the ormconfig.json, at 3:57, I changed the directory for the entities from "src/entity/*.js" to "dist/entity/*.js".
thanks. this worked for me.. until I tried running a mutation.. turned out I needed both src and dist in ormconfig.json : "entities": ["dist/entity/*.*","src/entity/*.*"]
There are only few tutorials about that : Typescript Node Graphql PostgreSQL and TS Next but a lot of companies now ask for this stack... the same with Jest etc, so thanks.
Hi Ben, This series is really great.. Nice time saver. One important thing I experienced with the User class you've shown as ... "@Field() @Column("text", {unique:true}) email: string;" this does not work with MongoDB.. I've raised this with the community. Please validate this for me as I am not expert with the GraphQL yet. BTW 1000 kudos from myself. Keep rocking man.
@@bawad okay 👍, I tried with just mongo and realized that it has not handled it well. Raised as a bug and just hoping to get it included in the future release. Thanks for your time 😊
3 года назад
For anyone wanting to use a fast encryption library with no dependencies (i.e no horrible node-gyp stuff), you can check out the npm package 'hash-wasm'. Got it working (with new dynamic import feature) with this code: ` import { bcrypt, bcryptVerify } from "hash-wasm" const createPassword = async (password: string) => { const cryptoModule = await import("crypto") const normPass = password.normalize() const salt = cryptoModule.randomBytes(16) const hashKey = await bcrypt({ password: normPass, salt, costFactor: 11, outputType: "encoded", }) const isValid = await bcryptVerify({ password: normPass, hash: hashKey }) console.log(isValid ? "Valid password" : "Invalid password") return hashKey } `
Great lesson! Maybe you should consider using 'citext' as the column type for email. As I think that 'text' isn't case sensitive in postgres, so the unique check will not work if the user registers with the same email but different casing. Using 'citext' solves this.
Great question :) I'm not sure. The advantage of doing toLowerCase is that it is database agnostic, if one would like to use another provider than postgres. And emails always look better in lowercase, so why not forcing that on the users ;) Maybe there are some performance benefits of using citext, but I wouldn't know, I'm not that into postgres. Considering this, I would probably use toLowerCase instead now that you have pointed that option out :)
www.postgresql.org/docs/9.1/citext.html From the rationale: "If you declare a column as UNIQUE or PRIMARY KEY, the implicitly generated index is case-sensitive. So it's useless for case-insensitive searches, and it won't enforce uniqueness case-insensitively." -and- "The citext data type allows you to eliminate calls to lower in SQL queries, and allows a primary key to be case-insensitive. citext is locale-aware, just like text, which means that the matching of upper case and lower case characters is dependent on the rules of the database's LC_CTYPE setting." From Limitations: "citext's case-folding behavior depends on the LC_CTYPE setting of your database..." "citext is not as efficient as text because the operator functions and the B-tree comparison functions must make copies of the data and convert it to lower case for comparisons. It is, however, slightly more efficient than using lower to get case-insensitive matching." So sounds like, if you are using postgresql and you have it configured to the listed params, use citext. It will probably save a few cycles. I have not used it, but looks like a good way to go. Hoping someone with experience on this can chime in :)
Hey Ben! Suggestion: Try out tsc-watch instead of ts-node and nodemon. Much more lightweight (or so I've been told..) ` "scripts": { "dev": "tsc-watch --noClear --onSuccess \"node --trace-warnings --inspect ./dist/index.js\"" },`
Hi Ben, Great Tutorial about TypeORM and TypeGraphql. This was exactly what I was looking for. I was wondering how you would construct an architecture if you were using Next.js (react). Since you are using apollo-server-express, should I make the custom server. Or can you implement within Nextjs. Need some advice!
Hi Ben, love your videos, very great work. I'm just wondering if you can give me advice about using typeGraphQL with MongoDB. Using typescript I have to create interfaces in ts then create the mongoose model. I've read about typegoose but there are kinda some issues with typegraphql, how should I do? Thanks
Check out blog.logrocket.com/integrating-typescript-graphql/. This blogpost helped me with mongodb setup using @typegoose/typegoose. It may help you as well
Another great lesson! I'm using git and I've noticed a lot of files being created and deleted in the node_modules/cross-spawn folder and I was wondering what they were and what they are used for?
This is amazing. I don’t think it gets much easier than this. DX is great with type safety and VS Code code suggestion. Entities are really declarative with decorators. Is sessions (with Redis) within scope of this series?
@Ben you should ready use .test domain for the test emails, some@email.test will not get sent, .test was make back in 1999 to make for testing of domain system... i know this show how old i am ;P
There is a CORS issue with the current setup. Apollo-server-express returns two headers. Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true I tried using apollo-http-link/apollo-client with the server. The browser passes session cookies but outputs error "Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’" to console. Query ends up with failure as well. Found solution here: github.com/expressjs/cors/issues/134#issuecomment-413543241 server.applyMiddleware({ app, path: '/', cors: false, ///
It's because typegraphql is kind of pointless with Prisma. Prisma already provides are whole host of types and resolvers for you to use, also Prisma offers a lot more than just graphql, its's a load balance, batching & caching, as-well as being completely generic on the database(well, soon will be, already got SQL, NoSQL). They even released a tool that generates all your resolver calls, and you just write the logic inside them.
Thanks for your responses guys! I know typeorm started to support mongoDB and its still "experimental". Is this stable to use mongoDB with typorm today?
@@Iwillownyouandbehappy Prisma already has MongoDB working, I don't know about typorm, though you might want to re-check on the performance part, normally graph databases have better performance.
@@bawad i don't know, every example app that they have looks a little complex to take on head first. would be nice to have a tutorial explaining at least one of the graphql examples. Maybe, building an example app
if you are doing a try/catch when saving your user and there is an error. How to set an empty "User" to return to in the catch? As the return value should be a User.
Not sure I understand why you switched out the type for the id field for User from a number to be of type "type-graphql ID" (~ 14:00) if it's still just an incrementing integer. Is there something going on under the hood? And thanks for this! - These are great.
It's to tell Apollo to use it as a cache key on the frontend. "A unique identifier, often used to refetch an object or as the key for a cache. While serialized as a String, ID signifies that it is not intended to be human‐readable" www.apollographql.com/docs/apollo-server/schemas/types.html
Would you be kind enough to suggest me some resource which combines event sourcing + graphql + typegraph. I am fairly new to this technology stack. Thanks
@@bawad It is a record-play-pause-replay events in simple terms. Theses events will contain the data (known as payload) which user has performed on our app/web. so in case of mishap or wrongful deletion of data we can restructure entire data back or we can monitor user behavior with the chain of events by going to that time sequence. (famous example : Accounting system or book keeping) please check the entire specification in detail its really interesting.. martinfowler.com/eaaDev/EventSourcing.html
@@bawad No worries, at least now we both know that we have to dig somewhere else or start working on the same with tools we have. :).. BTW you're pro in too many things, I wish, I can learn them all very quickly.
great job. I hope one day you'll do a short video that explains how to use the Google signin api in a full server/client architecture. I'm facing an issue doing this. Website calls a route in the server. Server then calls google and get back infos from Google. But then I'm stuck when trying to send bach these information to the website...
@@bawad yep, same issue. Can "hardcoded" the url of the client such as "res.redirect('localhost:3002/login'), works fine. But something like const url = req.url; res.redirect(url) is not working.
@@bawad yes. In the callbackurl the URL is a google one. which make sense as the response his coming back from Google once you have chosen the user profile to log in with and accept that the apps access your data. Checking also the sessionID, it is a new session which also make sense. The server at the very beginning receive a request from the client, but then when receiving the feedback from Google creates a new session which is the active one. Rgds
i don't know if you show it further, but it would be nice to show people the use of @unique for composite constraints. maybe the @exclusion to. I have made a backend for concerts and it was really nice to give some extra constraint to do not have overlaping artists on same stages. although in the ui it is already kind of checked. Sometimes the producers would access the db from other clients and that would may cause conflicts down the line.
Hello, when i want to create an user wich already exist in the error i get show the password of the user (hashed of course) its any way to prevent this?
I would create a generic error type: @ObjectType() class Error { @Field() field: string; @Field() message: string; } Then try/catch the unique error and then return { field: 'email', message: 'email already taken' }
14:52 For pure computation fields based on root (this), it's better to use getters/methods in object type class: 19majkel94.github.io/type-graphql/docs/resolvers.html#field-resolvers
6:04 Please don't use Active Record - class-based resolvers architecture of TypeGraphQL plays great with Data Mapper pattern (repositories). typeorm.io/#/active-record-data-mapper You will see the difference in unit tests ;)
@@bawad In larger apps I always try to make layers. So in case of a repository pattern, I create custom repositories that encapsulates the logic of the complex queries (findByEmail, etc.). So the resolvers acts like a services, that get the inputs from graphql, call the custom repository/other service and then returns the result. So in unit tests I only focus on the service logic (if user exist, if no user with this email, when password is incorrect) while mocking the repository allows to easily test the branch flow. And I test custom repositories queries with integration test and a real database. Maybe I should show that pattern in examples in readme or maybe an article :)
I would love to read an article on that! I've been mixing my service logic and resolvers together, but I like the idea of splitting them up and testing each separately. I'll give that a try and see how it goes.
Hey Ben. I want to thank you because your videos have proven to be very valuable to me. I am using MongoDB for my API and I had to abandon type-orm in favour of mikro-orm: b4nan.github.io/mikro-orm It works nicely with type-graphql and Apollo Server. It also supports MySQL, PostgreSQL, SQLite. If you are are interested have a look at it.
Always a pleasure following along or simply learning something new. I see your name even made it to the TypeGraphQL Getting Started page. Thanks for this series!
I'm new to TypeORM and need some feedback on the following: I have a User and an Employee entity, the user has a field called employee_id through which I can include that relation, however, I am unsure how to include the user from the employee side (include user when querying the employee). How do I tell TypeORM in the entity class - to link that field to the user where the user.employee_id === this.id? Thanks Ben. -> gist.github.com/olafwrieden/708eeefda40b571a81a3dbd7c59eb006
you can do a db join typeorm.io/#/one-to-one-relations
Hi Ben, great series. Thanks! For those using tsconfig.json with "outDir: ./dist" and creating the ormconfig.json, at 3:57, I changed the directory for the entities from "src/entity/*.js" to "dist/entity/*.js".
Thank you so much for this, helped me out!
thanks. this worked for me.. until I tried running a mutation.. turned out I needed both src and dist in ormconfig.json :
"entities": ["dist/entity/*.*","src/entity/*.*"]
There are only few tutorials about that : Typescript Node Graphql PostgreSQL and TS Next but a lot of companies now ask for this stack... the same with Jest etc, so thanks.
great job as always, Ben 💪
Great video! I use this setup as well and I like it a lot.
is it worth it even till this day using this stack or should i take look at something different?
Hi Ben, This series is really great.. Nice time saver. One important thing I experienced with the User class you've shown as ... "@Field()
@Column("text", {unique:true})
email: string;" this does not work with MongoDB.. I've raised this with the community. Please validate this for me as I am not expert with the GraphQL yet. BTW 1000 kudos from myself. Keep rocking man.
You might have to use a different community name for mongo. I don't use mongo though so I don't know for sure
@@bawad okay 👍, I tried with just mongo and realized that it has not handled it well. Raised as a bug and just hoping to get it included in the future release. Thanks for your time 😊
For anyone wanting to use a fast encryption library with no dependencies (i.e no horrible node-gyp stuff), you can check out the npm package 'hash-wasm'.
Got it working (with new dynamic import feature) with this code:
`
import { bcrypt, bcryptVerify } from "hash-wasm"
const createPassword = async (password: string) => {
const cryptoModule = await import("crypto")
const normPass = password.normalize()
const salt = cryptoModule.randomBytes(16)
const hashKey = await bcrypt({
password: normPass,
salt,
costFactor: 11,
outputType: "encoded",
})
const isValid = await bcryptVerify({ password: normPass, hash: hashKey })
console.log(isValid ? "Valid password" : "Invalid password")
return hashKey
}
`
May I ask how did you find this TypeGraphQL back then? Was it mainstream already (reddit/HN level?) or you had to dig somewhere "deeper"?
Thanks ! Thanks ! Thanks ! Thanks ! Thanks !
Great lesson!
Maybe you should consider using 'citext' as the column type for email. As I think that 'text' isn't case sensitive in postgres, so the unique check will not work if the user registers with the same email but different casing. Using 'citext' solves this.
do you think it's better to do that or call .toLowerCase() when inserting users?
Great question :)
I'm not sure. The advantage of doing toLowerCase is that it is database agnostic, if one would like to use another provider than postgres.
And emails always look better in lowercase, so why not forcing that on the users ;)
Maybe there are some performance benefits of using citext, but I wouldn't know, I'm not that into postgres.
Considering this, I would probably use toLowerCase instead now that you have pointed that option out :)
www.postgresql.org/docs/9.1/citext.html
From the rationale:
"If you declare a column as UNIQUE or PRIMARY KEY, the implicitly generated index is case-sensitive. So it's useless for case-insensitive searches, and it won't enforce uniqueness case-insensitively."
-and-
"The citext data type allows you to eliminate calls to lower in SQL queries, and allows a primary key to be case-insensitive. citext is locale-aware, just like text, which means that the matching of upper case and lower case characters is dependent on the rules of the database's LC_CTYPE setting."
From Limitations:
"citext's case-folding behavior depends on the LC_CTYPE setting of your database..."
"citext is not as efficient as text because the operator functions and the B-tree comparison functions must make copies of the data and convert it to lower case for comparisons. It is, however, slightly more efficient than using lower to get case-insensitive matching."
So sounds like, if you are using postgresql and you have it configured to the listed params, use citext. It will probably save a few cycles. I have not used it, but looks like a good way to go. Hoping someone with experience on this can chime in :)
thanks excellent series, greetings from Colombia
Hey Ben! Suggestion: Try out tsc-watch instead of ts-node and nodemon. Much more lightweight (or so I've been told..)
` "scripts": {
"dev": "tsc-watch --noClear --onSuccess \"node --trace-warnings --inspect ./dist/index.js\""
},`
Hi Ben,
Great Tutorial about TypeORM and TypeGraphql. This was exactly what I was looking for.
I was wondering how you would construct an architecture if you were using Next.js (react). Since you are using apollo-server-express, should I make the custom server. Or can you implement within Nextjs.
Need some advice!
Thank you!
I get this error - when running yarn start - UnhandledPromiseRejectionWarning: error: role "postgres" does not exist
How did you solve that?
what does on 1_register means
Hi Ben, love your videos, very great work. I'm just wondering if you can give me advice about using typeGraphQL with MongoDB. Using typescript I have to create interfaces in ts then create the mongoose model. I've read about typegoose but there are kinda some issues with typegraphql, how should I do?
Thanks
idk, I haven't really used mongo with typescript
Check out blog.logrocket.com/integrating-typescript-graphql/. This blogpost helped me with mongodb setup using @typegoose/typegoose. It may help you as well
why my project doesnt migrate automatically when i start?
Another great lesson! I'm using git and I've noticed a lot of files being created and deleted in the node_modules/cross-spawn folder and I was wondering what they were and what they are used for?
I have no idea, I usually add node_modules to my .gitignore
This is amazing. I don’t think it gets much easier than this. DX is great with type safety and VS Code code suggestion. Entities are really declarative with decorators. Is sessions (with Redis) within scope of this series?
yes
@@bawad Thank you so much for this series.
Why don't you use NoSQL database @Ben Awad
I like SQL better
mutation failed. method create in User does not return id.
How do I get ip address from resolver?
@Ben you should ready use .test domain for the test emails, some@email.test will not get sent, .test was make back in 1999 to make for testing of domain system... i know this show how old i am ;P
Cool, will do
looks like the typescript variables are lowercase "string" rather than String. How to know when to use which?
When telling TypeGraphQL what type you want something to be use String and when you want to tell typescript what type something is use string
There is a CORS issue with the current setup.
Apollo-server-express returns two headers.
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
I tried using apollo-http-link/apollo-client with the server.
The browser passes session cookies but outputs error
"Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’" to console. Query ends up with failure as well.
Found solution here:
github.com/expressjs/cors/issues/134#issuecomment-413543241
server.applyMiddleware({
app,
path: '/',
cors: false, ///
Is typeorm better then Prisma? I see Prisma is not working good with TypeGraphQL and I consider change... Please for help!
It's because typegraphql is kind of pointless with Prisma.
Prisma already provides are whole host of types and resolvers for you to use, also Prisma offers a lot more than just graphql, its's a load balance, batching & caching, as-well as being completely generic on the database(well, soon will be, already got SQL, NoSQL).
They even released a tool that generates all your resolver calls, and you just write the logic inside them.
I agree with Scott, I would go with Prisma without TypeGraphQL or TypeGraphQL with TypeORM
Thanks for your responses guys! I know typeorm started to support mongoDB and its still "experimental". Is this stable to use mongoDB with typorm today?
Is ut worth it to use typorm with mongoDB? I always used mongoDB because of its performance.
@@Iwillownyouandbehappy Prisma already has MongoDB working, I don't know about typorm, though you might want to re-check on the performance part, normally graph databases have better performance.
Hey Ben, do you already the Prisma Library? I would love to hear more about it, but my google-fu is failing me :/
Yeah, what about it?
@@bawad i don't know, every example app that they have looks a little complex to take on head first. would be nice to have a tutorial explaining at least one of the graphql examples. Maybe, building an example app
if you are doing a try/catch when saving your user and there is an error. How to set an empty "User" to return to in the catch? As the return value should be a User.
you can do @Mutation(() => User, {nullable: true}) if you want to be able to return null from the resolver
@@bawad oh great ! Otherwise I was instantiating an empy object "let user:User = new User()" and then apply the try/catch
Not sure I understand why you switched out the type for the id field for User from a number to be of type "type-graphql ID" (~ 14:00) if it's still just an incrementing integer. Is there something going on under the hood? And thanks for this! - These are great.
It's to tell Apollo to use it as a cache key on the frontend.
"A unique identifier, often used to refetch an object or as the key for a cache. While serialized as a String, ID signifies that it is not intended to be human‐readable" www.apollographql.com/docs/apollo-server/schemas/types.html
@@bawad Thanks, I'll rtfm :)
Would you be kind enough to suggest me some resource which combines event sourcing + graphql + typegraph. I am fairly new to this technology stack. Thanks
what do you mean by event sourcing?
@@bawad
It is a record-play-pause-replay events in simple terms. Theses events will contain the data (known as payload) which user has performed on our app/web. so in case of mishap or wrongful deletion of data we can restructure entire data back or we can monitor user behavior with the chain of events by going to that time sequence.
(famous example : Accounting system or book keeping)
please check the entire specification in detail its really interesting.. martinfowler.com/eaaDev/EventSourcing.html
oh I see
I don't know of any resources with that and typegraphql
@@bawad No worries, at least now we both know that we have to dig somewhere else or start working on the same with tools we have. :).. BTW you're pro in too many things, I wish, I can learn them all very quickly.
Oh BTW I just wanted to share this to you.. www.npmjs.com/package/ts-eventsourcing
For MongoDB users, check out blog.logrocket.com/integrating-typescript-graphql/
Please help. when I start, i get this error: TypeError: Cannot set property EntityManager of # which has only a getter
stackoverflow.com/questions/61779561/cant-init-typeorm-project
@@ErrorNaN_1 thanks! it works.
great job. I hope one day you'll do a short video that explains how to use the Google signin api in a full server/client architecture. I'm facing an issue doing this. Website calls a route in the server. Server then calls google and get back infos from Google. But then I'm stuck when trying to send bach these information to the website...
have you tried passport?
@@bawad yep, same issue. Can "hardcoded" the url of the client such as "res.redirect('localhost:3002/login'), works fine. But something like const url = req.url; res.redirect(url) is not working.
Have you logged what the value of req.url is and seen if it's what you expect?
@@bawad yes. In the callbackurl the URL is a google one. which make sense as the response his coming back from Google once you have chosen the user profile to log in with and accept that the apps access your data. Checking also the sessionID, it is a new session which also make sense. The server at the very beginning receive a request from the client, but then when receiving the feedback from Google creates a new session which is the active one. Rgds
Are you needing to redirect to multiple urls where hard coding would not work?
Freakin' insane! :X :D
i don't know if you show it further, but it would be nice to show people the use of @unique for composite constraints. maybe the @exclusion to. I have made a backend for concerts and it was really nice to give some extra constraint to do not have overlaping artists on same stages. although in the ui it is already kind of checked. Sometimes the producers would access the db from other clients and that would may cause conflicts down the line.
I didn't include that in this series, but I'll consider doing a video on it
Ben, can you help to make mutation to multi entity ..
User
Id,username,password,login,
UserBio
Userid,fullname,caption,joindate,etc
Is that the way ?
what do you mean?
@@bawad LMAO (for his question), maybe he wants another entity named UserBio and a mutation for it....
Hello, when i want to create an user wich already exist in the error i get show the password of the user (hashed of course) its any way to prevent this?
you can add a try/catch when you create the user
@@bawad Thanks man , im your fan
@@bawad Specifically is there a way of creating a type for the errors?
I would create a generic error type:
@ObjectType()
class Error {
@Field()
field: string;
@Field()
message: string;
}
Then try/catch the unique error and then return { field: 'email', message: 'email already taken' }
@@bawad Thanks Ben!
14:52 For pure computation fields based on root (this), it's better to use getters/methods in object type class:
19majkel94.github.io/type-graphql/docs/resolvers.html#field-resolvers
sweet, I'll switch it out
I am receiving the following error message: Property 'create' does not exist on type 'typeof User'.
32 const user = User.create({
~~~~~~
PSA: Dont use postgres 12 or you would get this error github.com/typeorm/typeorm/issues/4573
6:04 Please don't use Active Record - class-based resolvers architecture of TypeGraphQL plays great with Data Mapper pattern (repositories).
typeorm.io/#/active-record-data-mapper
You will see the difference in unit tests ;)
Have you gotten value out of unit testing your resolvers? I've been doing more integration tests where the resolvers hit the database.
@@bawad In larger apps I always try to make layers. So in case of a repository pattern, I create custom repositories that encapsulates the logic of the complex queries (findByEmail, etc.). So the resolvers acts like a services, that get the inputs from graphql, call the custom repository/other service and then returns the result. So in unit tests I only focus on the service logic (if user exist, if no user with this email, when password is incorrect) while mocking the repository allows to easily test the branch flow. And I test custom repositories queries with integration test and a real database. Maybe I should show that pattern in examples in readme or maybe an article :)
I would love to read an article on that!
I've been mixing my service logic and resolvers together, but I like the idea of splitting them up and testing each separately. I'll give that a try and see how it goes.
This is so confusing. It's not you Ben, its just wtf
Hey Ben. I want to thank you because your videos have proven to be very valuable to me.
I am using MongoDB for my API and I had to abandon type-orm in favour of mikro-orm: b4nan.github.io/mikro-orm
It works nicely with type-graphql and Apollo Server. It also supports MySQL, PostgreSQL, SQLite. If you are are interested have a look at it.
mikro-orm looks pretty good, I'm waiting on a few features before I jump ship though