Netpress Laravel-inspired backend framework for Node.js
Frameworkv0.1.14 Starterv0.1.12 Docsv1.0.3
Overview Installation Architecture CLI
Data

Relationships

Relationships are defined as instance methods on shared BaseModel subclasses. The relation object is itself a query builder, so you can chain filters, eager loads, pagination, and write helpers on top of it.

Supported Relation Types

RelationFactory
one-to-onehasOne(Related, foreignKey?, localKey?)
one-to-manyhasMany(Related, foreignKey?, localKey?)
inverse belongs-tobelongsTo(Related, foreignKey?, ownerKey?)
many-to-manybelongsToMany(Related, pivotTable, foreignPivotKey?, relatedPivotKey?, parentKey?, relatedKey?)
polymorphic onemorphOne(Related, name, typeColumn?, idColumn?, localKey?)
polymorphic manymorphMany(Related, name, typeColumn?, idColumn?, localKey?)
inverse polymorphicmorphTo(name, typeColumn?, idColumn?, classResolver?)
polymorphic many-to-manymorphToMany(Related, name, pivotTable, ...)
inverse polymorphic many-to-manymorphedByMany(Related, name, pivotTable, ...)

Quick Example

import { BaseModel } from "@admicaa/netpress";
import Post from "./Post.js";
import Profile from "./Profile.js";
import Image from "./Image.js";
import Tag from "./Tag.js";

export default class User extends BaseModel {
  posts() {
    return this.hasMany(Post);
  }

  profile() {
    return this.hasOne(Profile);
  }

  image() {
    return this.morphOne(Image, "imageable");
  }

  tags() {
    return this.morphToMany(Tag, "taggable", "taggables");
  }
}

Eager Loading

Netpress now supports all of these eager-load forms:

await User.with("posts").get();
await User.with(["posts", "profile"]).get();
await User.with("posts.author").get();
await User.with("posts", (q) => q.where("published", true)).get();
await User.with({ posts: (q) => q.orderBy("title", "desc") }).get();

Relation Existence Queries

The shared query builder also supports relation existence helpers:

await User.whereHas("posts").get();
await User.whereHas("posts", (q) => q.where("published", true)).get();
await User.orWhereHas("posts").get();
await User.whereDoesntHave("posts").get();
await User.orWhereDoesntHave("posts").get();
await User.has("posts").get();
await User.doesntHave("posts").get();

These helpers work across the shared query layer and are resolved into driver-safe filters before execution.

Common Usage

const user = await User.find(1);

await user.posts().get();
await user.posts().where("published", true).latest().get();
await user.profile().first();
await user.tags().attach([1, 2, 3]);

const post = await Post.find(42);
await post.author().first();
await post.comments().create({ body: "Nice post" });

Polymorphic Relations

Morph relations are now part of the implemented API, not just planned behavior.

Example:

class Comment extends BaseModel {
  commentable() {
    return this.morphTo("commentable");
  }
}

To keep morph type values stable across refactors, register aliases:

BaseModel.registerMorphMap({
  post: Post,
  user: User,
  comment: Comment,
});

Reset the map in tests with:

BaseModel.resetMorphMap();

Notes On Defaults

  • hasOne and hasMany default to <parentClassName>Id
  • belongsTo defaults to <relatedClassName>Id
  • morph helpers default to <name>Type and <name>Id
  • many-to-many helpers still let you override every pivot and key column explicitly

Use the explicit arguments whenever your schema deviates from the default camelCase conventions.