Open nathanielrindlaub opened 1 week ago
- When to use the
HydratedDocument<>
generic? ... Maybe we should just use it everywhere we create new documents or fetch them from the MongoDB, even if we're not using those methods?
Yes, I think this pretty much captures it. Any time that we are pulling data out of MongoDB or initialize a model class (e.g new Task({ ... });
), that is a hydrated document. To quote the docs you linked:
The
User()
constructor returns an instance ofHydratedDocument<IUser>
.IUser
is a document interface, it represents the raw object structure thatIUser
objects look like in MongoDB.HydratedDocument<IUser>
represents a hydrated Mongoose document, with methods, virtuals, and other Mongoose-specific features.
The part that I'm admittedly uncertain of is if the _id
property will be set on something like new Task({ name: 'foo' });
. The typings for a HydratedDocument<>
state that it's set, but that's a bit unexpected to me when we havn't yet sent it to the DB. However, that line of thinking comes mostly from my experience with a RDBMS like Postgres where the ID is often generated by the DB itself, not sure if that's how it works in Mongo (I see that there's a the ObjectID()
function that generates IDs client-side). 🤷
- ObjectId best practices? (a) if we don't explicitly set an
_id
property in a Mongoose Schema and instead rely on Mongoose to add one of typeObjectID
for us (e.g. like we do here here), doesmongoose.InferRawDocType<SomeSchema>
know about the_id
field, or do we have to explicitly set it in the schema? ...
By mongoose.InferRawDocType<...>
, do you mean mongoose.InferSchemaType<...>
? Either way, it does seem that Mongoose falls a bit short with regards to the presence of _id
values on generated interfaces. I opened a similar issue regarding DocumentArray
values. It would likely be safer for us to explicitely specify the _id
field on all models even though they are automatically added.
- ObjectId best practices? (b) Are we using
Schema.Types.ObjectId
in our schema definitions (as described in here) correctly? I know we use a lot of string_ids
, for both good reasons and bad, but I did a search through the code forSchema.Types.ObjectId
and only found one instance of it. Is it possible that we only ever explicitly set an id of typeObjectId
once in the whole code base?
I'm assuming that you're talking about this blurb:
To define a property of type
ObjectId
, you should useTypes.ObjectId
in the TypeScript document interface. You should use'ObjectId'
orSchema.Types.ObjectId
in your schema definition.
I think we're safe here, because we don't explicitely specify document interfaces for our schemas. Instead, we use mongoose.InferSchemaType<>
.
- Subdocument best practices? We use a lot of subdocuments in our schema. I am less sure if we have any "Nested Paths" (subtly different, see subdocument docs), but I wouldn't be surprised if we have a couple instances in there somewhere.
Yes, I also haven't seen any nested paths in the system.
- Subdocument best practices? ... I am also not sure if we ever use Mongoose methods directly on subdocuments in the code? I think in most instances when we create or modify subdocuments (for example
Image.objects
), we then callsave()
on theImage
rather than theImage.object
. If that is 100% the case this might not matter to us, but according to the docs, you need do some work to make sure that the returned, hydrated subdocument types have all the regular mongoose document properties and methods.
I find the Mongoose TS docs to be not great. They discuss how to be super hands-on with the type system (eg manually creating interfaces for each schema, passing those to mongoose.Model<...>
and new mongoose.Schema<...>
), however I have found that we haven't needed to be as hands-on. While the generated types that come from mongoose.InferSchemaType<...>
and typeof mongoose.Model<...>(...)
aren't perfect, they seem to be pretty good and are allowing us to avoid worrying too much about these issues. So, basically, I think this is one of those "not a problem until it actually proves to be one" kind of situations.
There are a bunch of things I don't understand about how Typescript works with Mongoose, but we should read up the best practices and be sure we're implementing it correctly. Here are a few points of confusion:
HydratedDocument<>
generic? From what I can tell from the docs, HydratedDocument provides the document properties as defined in the Schema/document type, as well as all of the standard Mongoose properties/methods. So my guess is any time we are using a Mongoose method likeSomeDocument.save()
we'll need to be dealing with aHydratedDocument
type. If we're just returning the document without using any of it's methods we probably don't need to useHydratedDocument
, but it probably wouldn't hurt, either. We obviously use.save()
in lots of places, but I'm not sure what other Mongoose document methods we are using and where, but it would be worth auditing all of the model files to make sure we have fullHydratedDocument
coverage. Maybe we should just use it everywhere we create new documents or fetch them from the MongoDB, even if we're not using those methods?_id
property in a Mongoose Schema and instead rely on Mongoose to add one of typeObjectID
for us (e.g. like we do here here), doesmongoose.InferRawDocType<SomeSchema>
know about the_id
field, or do we have to explicitly set it in the schema? (b) Are we usingSchema.Types.ObjectId
in our schema definitions (as described in here) correctly? I know we use a lot of string_ids
, for both good reasons and bad, but I did a search through the code forSchema.Types.ObjectId
and only found one instance of it. Is it possible that we only ever explicitly set an id of typeObjectId
once in the whole code base?Image.objects
), we then callsave()
on theImage
rather than theImage.object
. If that is 100% the case this might not matter to us, but according to the docs, you need do some work to make sure that the returned, hydrated subdocument types have all the regular mongoose document properties and methods.