Edit: I implemented below, published it to GitHub for a user auth.
Edit based on comment: Can a DTO file be replaced by the classes that @nestjs/graphql generates based on GraphQL types? Can I create a NestJS / MongoDB / Mongoose / GraphQL app by generating these classes, then extending them for my MongoDB Schema. Then, after this question, any best practices opinions are welcomed, but the answer will be accepted that answers the above. Below is the original post:
What is the best way to describe a user model? Is it to define the graphQL types and use that to generate classes to replace dto
files and import into Mongoose for the MongoDB schema? Below I'll explain what I'm doing, and what may work better. The number of files I have repeating myself doesn't seem to be scalable.
Here are the many ways I describe the same user:
users.types.graphql
- GraphQL types, which contains create user inputs, update user inputs, etc. It contains things such as:
type Mutation {
createUser(createUserInput: CreateUserInput): User
}
input CreateUserInput {
username: String
email: String
password: String
}
type User {
_id: String!
username: String
email: String
password: String
}
user.interfaces.ts
- Describes the user type, used by MongoDB Schema and my user.service.ts which contains:
export interface IUser {
email: string;
password: string;
username: string;
}
user.schema.ts
- MongoDB Schema. Describes a user to Mongoose. It also extends the user interface in user.interfaces.ts
and Document
to expose instance methods for strict type checking (I can add .checkPassword to an IUserDocument):
export interface IUserDocument extends IUser, Document {
checkPassword(
password: string,
callback: (error?: Error, same?: boolean) => any,
): void;
}
export const UserSchema: Schema = new Schema(....
UserSchema.pre<IUserDocument>('save', function(next) {
UserSchema.methods.checkPassword = function(....
create-user.dto.ts
and all kinds of dto
s for each operation. These seem redundant with my GraphQl types file with describing inputs above. Here is a dto
:
export class CreateUserDto {
readonly email: string;
readonly password: string;
readonly username: string;
}
I'm wondering what the best practice is to have one truth data to my user models. I'm thinking:
Use
GraphQLModule.forRoot({
definitions: {
path: join(process.cwd(), 'src/graphql.classes.ts'),
outputAs: 'class',
},
And use that for my interfaces and my dto
files since it outputs:
export class CreateUserInput {
username?: string;
email?: string;
password?: string;
}
export class User {
_id: number;
username?: string;
email?: string;
password?: string;
}
Would dto
files even be needed then? Does it matter they aren't readonly? Can I split up these classes into my respective folders (Users into user folder, products into products folder) automatically?
I'll post a public GitHub link when I'm done with a cookie cutter NestJS, MongoDB, Passport-JWT, GraphQL backend with user authentication so people have a reference (there is one out there that uses DTOs).
I stumbeld over your question having similar issues setting up some nest.js/graphQL/mongoose kind of API. Coming from a laravel/REST/SQL background I was very annoyed with the redundancy and have no idea of how to build up some kind of generic CRUD standard, where adding new resources would be easy and fast e.g. using a node script to create the boilerplate code etc. So one could focus on implementing the "new" functionalities instead of writing tons of code for always the same stuff.
I took a look into your GitHub project and it seems to me you already have optimized this in a way (e.g. user.schema is both for mongoose and graphql)? I started using the code first approach concerning graphQL but I think you are following the schema first approach there? Would be very interested in exchanging some thoughts about that topic as there is not much to be found here or somewhere else concerning nest.js!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With