Relations
Because data without relationships is just a spreadsheet with commitment issues.
Overview
SeedORM supports defining relationships between models. You declare them as an object in the model definition, then populate related documents at query time with the include option. It's like a JOIN, except you don't have to stare at SQL for 20 minutes trying to figure out which direction the arrow goes.
Defining relations
Add a relations object to your model definition. Each key is the relation name, and the value specifies the relation type, the related model, and the key fields used for linking.
import { SeedORM, FieldType, RelationType } from "seedorm";const User = db.model({ name: "User", collection: "users", schema: { name: { type: FieldType.String, required: true }, email: { type: FieldType.String, unique: true }, }, relations: { posts: { type: RelationType.HasMany, model: "Post", foreignKey: "authorId" }, profile: { type: RelationType.HasOne, model: "Profile", foreignKey: "userId" }, roles: { type: RelationType.ManyToMany, model: "Role", joinCollection: "user_roles", foreignKey: "userId", relatedKey: "roleId" }, },});Since RelationType is a string enum, you can also use plain strings like "hasMany". They're equivalent.
hasMany
A one-to-many relationship. The foreign key lives on the related model. A User has many Posts, where each Post stores an authorId pointing back to the User. One parent, many children. Classic family dynamics.
// User has many Postsrelations: { posts: { type: "hasMany", model: "Post", foreignKey: "authorId" },}// Post model stores authorIdconst Post = db.model({ name: "Post", collection: "posts", schema: { title: { type: FieldType.String, required: true }, authorId: { type: FieldType.String, required: true }, }, relations: { author: { type: "belongsTo", model: "User", foreignKey: "authorId" }, },});hasOne
A one-to-one relationship. Similar to hasMany but returns a single document instead of an array. One user, one profile. Monogamous data.
// User has one Profilerelations: { profile: { type: "hasOne", model: "Profile", foreignKey: "userId" },}belongsTo
The inverse of hasOne/hasMany. The foreign key lives on this model. Use this on the child side of the relationship. The Post knows who wrote it. The User doesn't have to keep track.
// Post belongs to Userrelations: { author: { type: "belongsTo", model: "User", foreignKey: "authorId" },}// When queried with include, the related User is attached:const post = await Post.findById("pos_abc123", { include: ["author"],});// post.author => { id: "usr_...", name: "Alice", ... }manyToMany
A many-to-many relationship using a join collection. You specify the joinCollection name and the two key fields: foreignKey (this model's ID in the join table) and relatedKey (the related model's ID in the join table). Yes, it's a junction table. No, you don't have to think about it that hard.
// User has many Roles through user_rolesrelations: { roles: { type: "manyToMany", model: "Role", joinCollection: "user_roles", foreignKey: "userId", relatedKey: "roleId", },}Populating relations
Use the include option on find, findById, or findOne to populate related documents. Just pass the relation names (the keys from your relations object) and SeedORM fetches everything for you. No N+1 queries to debug at 3am.
// Populate a single relationconst user = await User.findById("usr_abc123", { include: ["posts"],});// user.posts => [{ id: "pos_...", title: "...", authorId: "usr_abc123" }, ...]// Populate multiple relationsconst user = await User.findById("usr_abc123", { include: ["posts", "profile", "roles"],});// Works with find() and findOne() tooconst users = await User.find({ filter: { role: { $eq: "admin" } }, include: ["posts"],});associate() and dissociate()
For manyToMany relations, use associate() and dissociate() to manage the join table entries. Link things together, unlink them later. Relationship management without the therapy.
// Link a user to a roleawait User.associate("usr_abc123", "roles", "rol_editor");// Remove the linkawait User.dissociate("usr_abc123", "roles", "rol_editor");RelationType enum
SeedORM exports a RelationType enum for type-safe relation definitions. Since it's a string enum, plain strings work too.
import { RelationType } from "seedorm";RelationType.HasOne // "hasOne"RelationType.HasMany // "hasMany"RelationType.BelongsTo // "belongsTo"RelationType.ManyToMany // "manyToMany"// Both forms are equivalent:{ type: RelationType.HasMany, model: "Post", foreignKey: "authorId" }{ type: "hasMany", model: "Post", foreignKey: "authorId" }