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

@rytass/wms-module-graphql

v0.6.0

Published

Rytass WMS Module — GraphQL schema, resolvers and DTOs for warehouse management

Readme

@rytass/wms-module-graphql

GraphQL resolvers, mutations, queries and DTOs for the Rytass Warehouse Management System (WMS). Exposes the full WMS domain (loaders, materials, batches, stock, locations, transfers, inventory orders, quality inspection, scrape, warehouse map) as a NestJS GraphQLModule.

Pair with @rytass/wms-module-core for the service layer and @rytass/wms-module-react for ready-made admin UI.

Install

yarn add @rytass/wms-module-graphql @rytass/wms-module-core
# peer deps
yarn add @nestjs/common @nestjs/graphql @nestjs/apollo @apollo/server graphql dataloader reflect-metadata rxjs

Quick start

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver } from '@nestjs/apollo';
import { resolve } from 'node:path';
import {
  WmsModuleGraphqlModule,
  LoaderService,
  createWmsLoaders,
} from '@rytass/wms-module-graphql';

@Module({
  imports: [
    TypeOrmModule.forRoot({ /* ...db config... */ }),
    WmsModuleGraphqlModule.forRootAsync({
      useFactory: async () => ({}),
      defaultLoaderSerialId: 'DEFAULT',
      defaultLoaderTypeKey: 'DEFAULT',
      defaultLocationKey: 'DEFAULT',
    }),
    GraphQLModule.forRootAsync({
      driver: ApolloDriver,
      inject: [LoaderService],
      useFactory: (loaderService: LoaderService) => ({
        autoSchemaFile: resolve(process.cwd(), 'schema.gql'),
        playground: false,
        introspection: true,
        cors: true,
        autoTransformHttpErrors: true,
        context: ({ req }) => ({
          ...createWmsLoaders(loaderService),
          // your own context fields stay yours — no silent overwrite
          user: req.user,
        }),
      }),
    }),
  ],
})
export class AppModule {}

WmsModuleGraphqlModule.forRootAsync takes a single argument (WmsModuleCoreModuleAsyncOptions, re-exported from @rytass/wms-module-core — same shape as WMSBaseModuleAsyncOptions plus the three default* keys). It registers the resolvers/queries/mutations as composable NestJS providers and exposes LoaderService as a @Global() DI token; it does not start Apollo Server.

You register a single GraphQLModule.forRootAsync(...) of your own, mix your resolvers with the WMS resolvers in the same schema, and own the Apollo / context configuration end-to-end.

WmsModuleGraphqlModule internally wires WmsModuleCoreModule.forRootAsync so you do not need to import the core module separately when using this package.

Authorization

Every WMS root @Query / @Mutation is annotated with @WmsOperation({ resource, action }) — a host-neutral permission requirement enforced by the library's own route-level WmsAuthGuard, which delegates the allow/deny decision to an authChecker you supply. The library never imports your auth system; you map WMS's resource/action onto whatever you use (Casbin, RBAC, JWT scopes…).

⚠️ Using a global guard (e.g. member-base's CasbinGuard as APP_GUARD)? A deny-by-default global guard blankets every resolver in the schema, including WMS's, and rejects them all because they carry no host-specific metadata — "every WMS operation is forbidden". WMS fixes this by stamping each operation with a host-skip marker so your global guard defers, while WmsAuthGuard does the real enforcement. You do not touch your global guard.

import { WmsModuleGraphqlModule } from '@rytass/wms-module-graphql';
import { createMemberBaseWmsAuthChecker } from '@rytass/wms-module-graphql/adapters/member-base';

WmsModuleGraphqlModule.forRootAsync({
  useFactory: async () => ({}),
  defaultLoaderSerialId: 'DEFAULT',
  defaultLoaderTypeKey: 'DEFAULT',
  defaultLocationKey: 'DEFAULT',
  // optional — omit to stay fully permissive (local dev / demos)
  authChecker: createMemberBaseWmsAuthChecker({
    map: ({ resource, action }) => [[`wms:${resource}`, action]],
  }),
});

The member-base adapter reuses everything CasbinGuard already put on the request (verified payload, enforcer, your custom casbinPermissionChecker) so WMS enforces with the exact same policy engine as your own resolvers — with zero member-base import on the WMS side. For custom JWT/RBAC, pass your own authChecker: (ctx, { resource, action }) => boolean.

➡️ Full design & recipes: docs/authorization.md (the problem, execution order, member-base / custom-JWT / no-guard recipes, the single coupling point, public API, FAQ).

Events & Hooks

Intervene in and get notified about mutations. Every root mutation carries a typed operation event (material.created, ship-inventory-order.shipped, …) and fans out stock domain events (stock.allocated, stock.shipped, …) driven by the ChangeType state machine. Wire two opt-in hook points via forRootAsync:

  • beforeHooks run before any DB work — throw to veto the mutation (zero side effects).
  • afterListeners run after the outermost transaction commits — never notified about rolled-back changes.
import { memberBaseActorExtractor } from '@rytass/wms-module-graphql/adapters/member-base';

WmsModuleGraphqlModule.forRootAsync({
  ...coreOptions,

  // (v2) populate payload.actor on every event — reads req.payload → { id, domain }
  actorExtractor: memberBaseActorExtractor,

  // (v2) append materialId/batchId/locationId/loaderId to stock event payloads
  enrichStockEvents: true,

  beforeHooks: {
    'ship-inventory-order.shipped': async ({ args, actor }) => {
      if (!allowed(args, actor)) throw new ForbiddenException('blocked');
    },
  },
  afterListeners: {
    // v2: payload.args carries items[]; payload.actor carries { id, domain }
    'ship-inventory-order.shipped': async ({ payload }) => {
      await erp.markShipped(payload.id, payload.result, payload.actor);
    },
    // v2 with enrichStockEvents: materialId/batchId/locationId/loaderId also available
    'stock.allocated': async ({ payload }) => { await erp.reserve(payload); },
  },
});

v2 payload enrichment (upcoming minor — [email protected] / [email protected]):

  • Operation events gain args (the GraphQL input that triggered the mutation) and actor (WmsActor | null). Nine action mutations return boolean; their real information is in args and the stock events they fan out.
  • Stock events gain opt-in materialId / batchId / locationId / loaderId when enrichStockEvents: true is set. Caveat: split / merge / reclassify order-items have no materialId / batchId at the item level — only locationId / loaderId are available for those three domains even with eager enrichment.
  • WmsOperationResultMap provides a per-event typed overlay so subscribers receive the concrete DTO (MaterialDto, ShipInventoryOrderDto, …) instead of unknown.

Post-commit delivery is guaranteed by an AsyncLocalStorage dispatch scope (events buffer during the request and flush only after commit). Prefer @OnEvent? Opt into the optional @nestjs/event-emitter bridge from @rytass/wms-module-graphql/adapters/event-emitter — every event also emits as wms.<event>. Nothing configured ⇒ no events, zero overhead.

➡️ Full design, the complete 56-event list & limitations: docs/events.md.

DataLoader context

The module ships a HelperModule (auto-registered) that provides a singleton LoaderService. To avoid N+1 queries, the WMS resolvers expect a per-request DataLoader trio at context.loaders:

type WmsLoaders = {
  batchSingle: DataLoader<...>;
  batchMany: DataLoader<...>;
  batchManyWithPagination: DataLoader<...>;
};

Inject LoaderService into your GraphQLModule.forRootAsync factory and call createWmsLoaders(loaderService) per request inside context. The helper returns a { loaders: WmsLoaders } fragment that is safe to spread:

context: ({ req }) => ({
  ...createWmsLoaders(loaderService),   // adds `loaders: { batchSingle, ... }`
  user: req.user,                       // your fields preserved
  csrfToken: req.headers['x-csrf'],
}),

Per-request is non-negotiable — DataLoader caches results inside the instance, so reusing one across requests leaks data between callers. createWmsLoaders is a thin factory you call from inside context, never at module scope.

Schema artefacts

The schema is generated at boot via autoSchemaFile (above). Commit the resulting schema.gql to your repo if you want downstream codegen (e.g. for @rytass/wms-module-react) to consume a stable schema snapshot.

Documentation

Full documentation is shipped inside this package under the docs/ directory (available after npm install):

| Document | Description | |---|---| | docs/integration-guide.md | Step-by-step wiring guide for NestJS + Next.js consumers | | docs/authorization.md | Complete resolver-authorization design & integration recipes (member-base / custom JWT / no-guard) | | docs/events.md | Mutation event/hook system — before-hooks (veto), post-commit after-listeners, complete event list, event-emitter bridge | | docs/api-reference.md | Complete export catalog with signatures and usage examples |

GitHub hosted links (always up-to-date):

License

MIT