npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aplinkosministerija/moleculer-accounts

v1.8.0

Published

Wrapper around biip-auth-api for managing users & tenants

Downloads

266

Readme

BĮIP Moleculer Accounts

Usage

  1. Install the Package

    To get started, you need to install the @aplinkosministerija/moleculer-accounts package. You can do this using yarn or npm:

    yarn add @aplinkosministerija/moleculer-accounts
    npm install @aplinkosministerija/moleculer-accounts
  2. Use in action

    import { DatabaseMixin } from '@aplinkosministerija/moleculer-accounts';
    
    export const YourService = {
      name: 'serviceName',
      mixins: [DatabaseMixin(knexConfig, options)],
    };

Deep Query Mixin

The DeepQuery mixin is included by default with the DatabaseMixin. If you are using a DatabaseMixin other than moleculer-accounts, you can add it like this:

export const YourService = {
  name: 'serviceName',
  mixins: [DeepQuery(), DatabaseMixin(knexConfig, options)],
};

Note: The DeepQuery mixin must be added before DatabaseMixin.

Setup

To enable deep queries (similar to left joins), you need to add a deepQuery property to your database fields schema.

Automatic Deep Query

The easiest option is to specify the sub-service name in the deepQuery property. This will automatically join tables using the <fieldName> and subService.<id> columns.

fields: {
  tenant: {
    type: 'number',
    columnName: 'tenantId',
    deepQuery: 'tenants',
  },
}

Manual Deep Query

Manual deep queries offer greater control but are more complex. We provide several helper functions to simplify writing these queries. Generally, you need to extend the predefined q: Knex query (usually with with) by defining sub-fields and joining them with the main query.

The most challenging part is prefixing all fields correctly. Here are some examples:

fields: {
  // one to one
  tenant: {
    type: 'number',
    columnType: 'integer',
    deepQuery: {
      // Optional: providing service name continues recursion deeper
      service: 'tenants',
      handler({ leftJoinService }: DeepQuery) {
        leftJoinService('tenants', 'tenant', 'id');
      },
    },
  },

  // one to many
  huntings: {
    virtual: true,
    deepQuery: {
      service: 'huntings',
      handler({ leftJoinService }: DeepQuery) {
        leftJoinService('huntings', 'id', 'huntingAreaId');
      },
    },
  },

  // one to one "custom" select (could be done easier with helpers like `withQuery` and others)
  lastHunting: {
    virtual: true,
    deepQuery({ knex, subTableName, tableName, q, deeper }: DeepQuery) {
      q.with(
        subTableName,
        knex.raw(`SELECT DISTINCT ON (hunting_area_id) hunting_area_id AS ${subTableName}_hunting_area, status AS ${subTableName}_status, created_by AS ${subTableName}_created_by
FROM huntings
WHERE deleted_at IS NULL
ORDER BY hunting_area_id, id DESC`)
      );

      q.leftJoin(subTableName, function () {
        this.on(
          `${tableName}.id`,
          '=',
          `${subTableName}.${subTableName}_hunting_area`
        );
      });
    },
  }
}

Usage

Using deep queries is straightforward and supports multiple levels.

# Single-level deep query
call huntings.find {"query":{"huntingArea.municipality":{$in:[33,72]}}}
call huntings.find {"query":{"huntingArea.municipality":72}}

# Multi-level deep query
call huntings.find {"query":{"huntingArea.createdBy.firstName":"Peter"}}

# Unlimited depth
call huntings.find {"query":{"huntingArea.createdBy.createdBy.firstName":"John"}}

# Combine manual and automatic deep queries
call huntingAreas.find {"query":{"lastHunting.createdBy.lastName":"Naudotojas"}}

How It Works

Simple left joins are not possible with DatabaseMixin because it does not prefix fields with table names. To address this, the DeepQuery mixin generates sub-queries with prefixed column names. These prefixes are hidden from the end user, as shown in the examples.

Here is an example of the generated SQL:

WITH "hunting_area" AS (
  SELECT
    "id"              AS "hunting_area_id",
    "name"            AS "hunting_area_name",
    "display_name"    AS "hunting_area_display_name",
    "mpv_id"          AS "hunting_area_mpv_id",
    "municipality_id" AS "hunting_area_municipality",
    "tenant_id"       AS "hunting_area_tenant",
    "created_by"      AS "hunting_area_created_by",
    "created_at"      AS "hunting_area_created_at",
    "updated_by"      AS "hunting_area_updated_by",
    "updated_at"      AS "hunting_area_updated_at",
    "deleted_by"      AS "hunting_area_deleted_by",
    "deleted_at"      AS "hunting_area_deleted_at"
  FROM "hunting_areas"
  WHERE "deleted_at" IS NULL
)
SELECT *
FROM "huntings"
LEFT JOIN "hunting_area"
  ON "huntings"."hunting_area_id" = "hunting_area"."hunting_area_id"
WHERE ("deleted_at" IS NULL) AND "hunting_area_municipality" = 33;

Querying jsonb fields

DeepQuery mixin supports querying jsonb fields. Field must have object or array type. Deeper structure description is optional except for cases with arrays - mixin needs to know which properties are array type (in any depth). Querying is done by providing object - imagine jsonb field as MongoDB document, and query values as MongoDB query.

Examples

fields: {
  address: {
    type: 'object',
    // properties are OPTIONAL for this functionality in this case, no arrays in any depth
    properties: {
      street: 'string',
      city: 'string',
      phone: {
        type: 'object',
        properties: {
          number: 'string',
          countryCode: 'string',
        },
      }
    },
  },
}
call users.find {"query":{"address":{"city":"Vilnius"}}}

# deeper query
call users.find {"query":{"address":{"phone.countryCode":"+370"}}}

# mongodb operators
call users.find {"query":{"address":{"phone.countryCode":{"$in":["+370","+371"]}}}}

# deepQuery first - jsonb next
call tenants.find {"query":{"craetedBy.address":{"phone.countryCode":{"$in":["+370","+371"]}}}}

Array

As said earlier, deeper structure description is required for arrays. This is because the mixin needs to know which properties are array type (in any depth).

fields: {
  address: {
    type: 'object',
    // properties are REQUIRED this time, if you want to be able to query by array fields (and deeper)
    properties: {
 //      street: 'string', // <-- not required, not array
 //     city: 'string',
      phoneNumbers: {
        type: 'array',
      }
    },
  },
}