Auto-preload multiple relationships when retrieving Lucid models

Overview

Adonis Auto-Preload

Auto-preload multiple relationships when retrieving Lucid models

Build npm License: MIT Typescript

Pre-requisites

Node.js >= 16.17.0

Installation

npm install @melchyore/adonis-auto-preload
# or
yarn add @melchyore/adonis-auto-preload
# or
pnpm install @melchyore/adonis-auto-preload

Configure

node ace configure @melchyore/adonis-auto-preload

Usage

Extend from the AutoPreload mixin and add a new static $with attribute.

Adding as const to $with array will let the compiler know about your relationship names and infer them so you will have better intellisense when using without and withOnly methods.

Relationships will be auto-preloaded for find, all and paginate queries.

Using relation name

// App/Models/User.ts

import { BaseModel, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

import { AutoPreload } from '@ioc:Adonis/Addons/AutoPreload'

import Post from 'App/Models/Post'

class User extends compose(BaseModel, AutoPreload) {
  public static $with = ['posts'] as const

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>
}
// App/Controllers/Http/UsersController.ts

import User from 'App/Models/User'

export default class UsersController {
  public async show() {
    return await User.find(1) // ⬅ Returns user with posts attached.
  }
}

Using function

You can also use functions to auto-preload relationships. The function will receive the model query builder as the only argument.

// App/Models/User.ts

import { BaseModel, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

import { AutoPreload } from '@ioc:Adonis/Addons/AutoPreload'

import Post from 'App/Models/Post'

class User extends compose(BaseModel, AutoPreload) {
  public static $with = [
    (query: ModelQueryBuilderContract<typeof this>) => {
      query.preload('posts')
    }
  ]

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>
}
// App/Controllers/Http/UsersController.ts

import User from 'App/Models/User'

export default class UsersController {
  public async show() {
    return await User.find(1) // ⬅ Returns user with posts attached.
  }
}

Nested relationships

You can auto-preload nested relationships using the dot "." between the parent model and the child model. In the following example, User -> hasMany -> Post -> hasMany -> Comment.

// App/Models/Post.ts

import { BaseModel, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

class Post extends BaseModel {
  @column({ isPrimary: true })
  public id: number

  @column()
  public userId: number

  @column()
  public title: string

  @column()
  public content: string

  @hasMany(() => Comment)
  public comments: HasMany<typeof Comment>
}
// App/Models/User.ts

import { BaseModel, column, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

import { AutoPreload } from '@ioc:Adonis/Addons/AutoPreload'

import Post from 'App/Models/Post'

class User extends compose(BaseModel, AutoPreload) {
  public static $with = ['posts.comments'] as const

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>
}

When retrieving a user, it will preload both posts and comments (comments will be attached to their posts parents objects).

You can also use functions to auto-preload nested relationships.

public static $with = [
  (query: ModelQueryBuilderContract<typeof this>) => {
    query.preload('posts', (postsQuery) => {
      postsQuery.preload('comments')
    })
  }
]

Mixin methods

The AutoPreload mixin will add 3 methods to your models. We will explain all of them below.

We will use the following model for our methods examples.

// App/Models/User.ts

import { BaseModel, column, hasOne, HasOne, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

import { AutoPreload } from '@ioc:Adonis/Addons/AutoPreload'

import Profile from 'App/Models/Profile'
import Post from 'App/Models/Post'

class User extends compose(BaseModel, AutoPreload) {
  public static $with = ['posts', 'profile'] as const

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @hasOne(() => Profile)
  public profile: HasOne<typeof Profile>

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>
}

without

This method takes an array of relationship names as the only argument. All specified relationships will not be auto-preloaded. You cannot specify relationships registered using functions.

// App/Controllers/Http/UsersController.ts

import User from 'App/Models/User'

export default class UsersController {
  public async show() {
    return await User.without(['posts']).find(1) // ⬅ Returns user with profile and without posts.
  }
}

withOnly

This method takes an array of relationship names as the only argument. Only specified relationships will be auto-preloaded. You cannot specify relationships registered using functions.

// App/Controllers/Http/UsersController.ts

import User from 'App/Models/User'

export default class UsersController {
  public async show() {
    return await User.withOnly(['profile']).find(1) // ⬅ Returns user with profile and without posts.
  }
}

withoutAny

Exclude all relationships from being auto-preloaded.

// App/Controllers/Http/UsersController.ts

import User from 'App/Models/User'

export default class UsersController {
  public async show() {
    return await User.withoutAny().find(1) // ⬅ Returns user without profile and posts.
  }
}

Note

You can chain other model methods with mixin methods. For example, await User.withoutAny().query().paginate(1)

Limitations

  • Consider the following scenario: User -> hasMany -> Post -> hasMany -> Comments. If you auto-preload user and comments from Post and you auto-preload posts from User, you will end-up in a infinite loop and your application will stop working.

Route model binding

When using route model binding, you cannot use without, withOnly and withoutAny methods in your controller. But, you can make use of findForRequest method.

// App/Models/User.ts

import { BaseModel, column, hasOne, HasOne, hasMany, HasMany } from '@ioc:Adonis/Lucid/Orm'
import { compose } from '@ioc:Adonis/Core/Helpers'

import { AutoPreload } from '@ioc:Adonis/Addons/AutoPreload'

import Profile from 'App/Models/Profile'
import Post from 'App/Models/Post'

class User extends compose(BaseModel, AutoPreload) {
  public static $with = ['posts', 'profile'] as const

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @hasOne(() => Profile)
  public profile: HasOne<typeof Profile>

  @hasMany(() => Post)
  public posts: HasMany<typeof Post>

  public static findForRequest(ctx, param, value) {
    const lookupKey = param.lookupKey === '$primaryKey' ? 'id' : param.lookupKey

    return this
      .without(['posts']) // ⬅ Do not auto-preload posts when using route model binding.
      .query()
      .where(lookupKey, value)
      .firstOrFail()
  }
}

Run tests

npm run test

Author

👤 Oussama Benhamed

🤝 Contributing

Contributions, issues and feature requests are welcome!
Feel free to check issues page. You can also take a look at the contributing guide.

Show your support

Give a ⭐️ if this project helped you!

📝 License

Copyright © 2022 Oussama Benhamed.
This project is MIT licensed.

You might also like...

Interactpedia is a frontend web application that models how engagement

Interactpedia is a frontend web application that models how engagement

Frontend web app modeling the integration of engagement features into crowdsourcing platforms to improve user info retention made as research for Modeling the Effects of Engagement Methods in Online Crowd-sourcing Platforms from Governor’s School of New Jersey Program in Engineering & Technology 2022.

Aug 25, 2022

A regular table library, for async and virtual data models.

A regular table library, for async and virtual data models.

A Javascript library for the browser, regular-table exports a custom element named regular-table, which renders a regular HTML table to a sticky p

Dec 16, 2022

Browser-compatible JS library for running language models

Huggingface Transformers Running in the Browser This library enables you to run huggingface transformer models directly in the browser. It accomplishe

Jan 8, 2023

Fnon is a client-side JavaScript library for models, loading indicators, notifications, and alerts which makes your web projects much better.

𝔉𝔫𝔬𝔫 Fnon is the name of my late mother, It's an Arabic word which means Art, I created this library in honor of her name. Fnon is a client-side J

Sep 11, 2022

Sample of CI/CD auto deploy to own server via Github Actions

Psst — looking for a more complete solution? Check out SvelteKit, the official framework for building web applications of all sizes, with a beautiful

Mar 19, 2022

Quickly create an interactive HTML mock-up by auto sourcing lorem ipsum/images generators, with minimal html markup, and no server side code

RoughDraft.js v0.1.5 Quickly mockup / prototype HTML pages with auto-generated content, without additional JavaScript or server side code. section

Dec 21, 2022

Smart Auto Move learns the size and position of your application windows and restores them to the correct place on subsequent launches. Supports GNOME Wayland.

Smart Auto Move learns the size and position of your application windows and restores them to the correct place on subsequent launches. Supports GNOME Wayland.

smart-auto-move smart-auto-move is a Gnome Shell extension which keeps track of all application windows and restores them to the previous position, si

Dec 23, 2022

Example auto-generated OpenAPI client library and an accompanying example Angular app.

To utilize this demo Head into petstore_frontend\petes_pets Run npm install Go to frontend_client_lib\out Run npm install Head back into petstore_fron

Jan 21, 2022
Comments
  • fix(adonis-typings): return type of mixin

    fix(adonis-typings): return type of mixin

    Proposed changes

    Hence the mixin is not adding any instance methods, the return type of a constructor should be an empty object. Before the fix, we could not use this package with a model that extends a class that inherits from BaseModel.

    an example that didn't work without the fix:

    import { BaseModel } from '@ioc:Adonis/Lucid/Orm';
    
    class ModifiedBase extends BaseModel {
        // some basics
    }
    
    class Bank extends compose(ModifiedBase, AutoPreload) {
        // model properties
    }
    

    Types of changes

    What types of changes does your code introduce?

    Put an x in the boxes that apply

    • [x] Bugfix (non-breaking change which fixes an issue)
    • [ ] New feature (non-breaking change which adds functionality)
    • [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

    Checklist

    Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.

    • [x] I have read the CONTRIBUTING doc
    • [x] Lint and unit tests pass locally with my changes
    • [ ] I have added tests that prove my fix is effective or that my feature works.
    • [ ] I have added the necessary documentation (if appropriate)

    Further comments

    If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

    opened by lukfor85 3
Releases(v1.0.4)
  • v1.0.4(Sep 2, 2022)

  • v1.0.0(Sep 1, 2022)

    • docs: add instructions ea54731
    • docs: docs final version 90cecf2
    • build: change package name 5994329
    • build: add copyfiles package bdf0099
    • build: do not generate source map files 8c95fb0
    • docs: fix typo ecd5ef4
    • docs: add docs bad76d7
    • test: avoid unused class c308b4c
    • chore: prefix node package 7465dbb
    • test: use Nodej.js lts and latest for tests 3ed0626
    • feat: initial commit 4a40db7

    https://github.com/Melchyore/adonis-auto-preload/compare/82fbc96379449a8f62ec74cef3055360a9752beb...v1.0.0

    Source code(tar.gz)
    Source code(zip)
Owner
Oussama Benhamed
Oussama Benhamed
🍺 A public REST API for retrieving information about Systembolaget's products, and which products that are available in which store

?? systembolaget-api A public REST API for retrieving information about Systembolaget's products, and which products that are available in which store

Daniel Cronqvist 9 Nov 22, 2022
Lucid is a library, which allows you to create Cardano transactions and off-chain code for your Plutus contracts in JavaScript and Node.js.

Lucid is a library, which allows you to create Cardano transactions and off-chain code for your Plutus contracts in JavaScript and Node.js.

Berry 243 Jan 8, 2023
An ERC-721 like NFT contract with Plutus scripts and Lucid as off-chain framework

Gatsby minimal TypeScript starter ?? Quick start Create a Gatsby site. Use the Gatsby CLI to create a new site, specifying the minimal TypeScript star

Berry 10 Sep 23, 2022
☁️ Application using Node.js, AdonisJs, Adonis ACL, Adonis Kue Provider, Adonis Mail, Adonis Lucid Slugify, Adonis Validator, AdonisJs Redis, ESLint and pg

Node.js - SaaS ☁️ Application using Node.js, AdonisJs, Adonis ACL, Adonis Kue Provider, Adonis Mail, Adonis Lucid Slugify, Adonis Validator, AdonisJs

null 4 Aug 19, 2022
True P2P concept for your p2p powered website/app/client. MSC/MEP (Multiple Strategy Concept/Multiple Entry Points)

TRUE P2P CONCEPT - Lets redecentralize the web This repo is just conceptual. Active development of the endproduct (TRUE P2P) happens here https://gith

Bo 6 Mar 29, 2022
⚡🚀 Call multiple view functions, from multiple Smart Contracts, in a single RPC query!

ethers-multicall ⚡ ?? Call multiple view functions, from multiple Smart Contracts, in a single RPC query! Querying an RPC endpoint can be very costly

Morpho Labs 20 Dec 30, 2022
Labels issues using OpenAI's Classification API powered by GPT-3 models!

OpenAI Issue Labeler ?? This GitHub action labels issues using OpenAI's Classification API powered by GPT-3 models! We are using curie as our completi

Austen Stone 11 Dec 21, 2022
A tool to modify onnx models in a visualization fashion, based on Netron and flask.

English | 简体中文 Introduction To edit an ONNX model, One common way is to visualize the model graph, and edit it using ONNX Python API. This works fine.

Zhang Ge 413 Jan 4, 2023
An authorization library that supports access control models like ACL, RBAC, ABAC in modern JavaScript platforms

Casbin-Core ?? Looking for an open-source identity and access management solution like Okta, Auth0, Keycloak ? Learn more about: Casdoor News: still w

Casbin 6 Oct 20, 2022
🧙 Mage is an open-source data management platform that helps you clean data and prepare it for training AI/ML models.

Intro Mage is an open-source data management platform that helps you clean data and prepare it for training AI/ML models. What does this do? The curre

Mage 2.5k Jan 4, 2023