Look at the code below. We define a user schema by mongodb. So this shema we have email and password. When user signs up, the API returns the user object but we want to delete the password in response. We don’t want to expose it.
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
})
Let’s Ctr+Click on mongoose.Schema, we will see the class definition, especially the constructor.
export class Schema<
constructor(definition?: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | DocType, options?: SchemaOptions<TPathTypeKey, FlatRecord<DocType>, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals>);
So the email and password we provided is the definition? of SchemaDefinition type. The second object is the options of SchemaOptions. If we continue to expose it.
interface SchemaOptions<PathTypeKey extends TypeKeyBaseType = DefaultTypeKey, DocType = unknown, TInstanceMethods = {}, QueryHelpers = {}, TStaticMethods = {}, TVirtuals = {}> {
toJSON?: ToObjectOptions;
That’s an interface which has the toJSON behaviour. We expose toJSON and see transform method.
Almost everything in JavaScript is an object. In fact, only six things are not objects. They are —
null
,undefined
, strings, numbers, boolean, and symbols. These are called primitive values or primitive types.
We provide transform function implementation in the brackets like this.
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
}, {
toJSON: {
transform(doc, ret) {
delete ret.password
}
}
})
That’s it. In ruby on rails, we feel free to provide object without caring about object type as long as we follow duck typing pattern. The object has behaviours we need. But in Javascript which has Generic Type. We need to provide object according to the Generic Type. We’re able to call a function with optional Generic Types. We want to have another type for an object in function instead of default Generic Type.
interface UserModel extends mongoose.Model<UserDoc> {
build(attrs: UserAttrs): UserDoc;
}
interface UserDoc extends mongoose.Document {
email: string
password: string
}
export function model<T, U, TQueryHelpers = {}>(
name: string,
schema?: Schema<T, any, any, TQueryHelpers, any, any>,
collection?: string,
options?: CompileModelOptions
): U;
const User = mongoose.model<UserDoc, UserModel>('User', userSchema)
Above code, we see that the function model (mongodb) has Generic Type T and U. T is a Type we create Schema object and U is for the response type of model method when we define new object like new User({}) . Let’s say.
- user = new User({ email, password }) so the user object will behaviour as U which is UserModel Type we provided.
- We provide UserDoc as T, in order to provide Generic Type of Schema for userSchema.
This one is really complex comparing with ruby on rails. Let’s read more the library, we will get used to it.