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

@ntxdev/postman-generator

v0.1.0

Published

Generate Postman collections from NestJS Swagger + GraphQL schemas.

Downloads

67

Readme

@ntxdev/postman-generator

Self-contained, project-agnostic Postman collection generator for NestJS apps with REST (Swagger) and/or GraphQL.

Installation

Packages are public — no .npmrc or auth setup required.

npm install --save-dev @ntxdev/postman-generator

Add these scripts to your project's package.json:

{
  "postman:init": "npx postman-init",
  "postman:swagger": "npx postman-extract-swagger",
  "postman:generate": "npx postman-generate",
  "postman:sync": "npx postman-sync",
  "postman": "npm run postman:swagger && npm run postman:generate"
}

Quick Start

# First-time setup (auto-detect project, no DB required)
npm run postman:init

# Sync folder config from resolver files (no DB required)
npm run postman:sync

# Full pipeline (requires DB connection for Swagger extraction)
npm run postman

# GraphQL only (no DB required, reads src/schema.gql)
npm run postman:generate -- --skip-rest

# REST only
npm run postman:generate -- --skip-gql

The postman:init command creates a postman-generator/ directory in your project root with postman.config.json and examples.json. These files live in the consuming project, not inside the package.

Output lands in postman-generator/<collection-name>.postman_collection.json. Import it into Postman.


How It Works

src/schema.gql ──────────────► graphql-to-postman ─┐
                                                    ├─► merge + enrich ─► .postman_collection.json
postman-generator/swagger.json ► openapi-to-postmanv2┘         ▲
                                                               │
                                          postman-generator/postman.config.json
                                          postman-generator/examples.json
  1. Init: npx postman-init scans package.json, src/main.ts, and src/app.module.ts to auto-detect project info (name, port, schema path, auth routes, Swagger metadata) and writes a minimal config.
  2. GraphQL: The schema file (src/schema.gql) is auto-generated by NestJS code-first. The generator reads it directly — no server required.
  3. REST/Swagger: npx postman-extract-swagger reads the AppModule path and Swagger metadata from config, bootstraps the NestJS app without binding a port, and dumps the OpenAPI JSON.
  4. Merge: The generator converts both artifacts into Postman items, applies example variables, and writes the final collection.

Project Layout

After running npx postman-init, your project will contain:

| File | Committed | Project-specific | Purpose | | ------------------------------------------------- | --------- | ---------------- | ------------------------------------------------------------ | | postman-generator/postman.config.json | Yes | Yes | Project config (collection name, auth, paths) | | postman-generator/examples.json | Yes | Yes | GraphQL example variable overrides | | postman-generator/swagger.json | No | Yes | Generated Swagger spec (build artifact) | | postman-generator/*.postman_collection.json | No | Yes | Generated Postman collection (build artifact) |


Init — Auto-Detection

The npx postman-init command scans the project to generate a minimal config. It detects:

| Setting | Detection method | | ------------------------ | ------------------------------------------------------------------------------------------------------------------- | | collectionName | package.json name field, title-cased | | description | package.json description field | | url | Port from configService.get('PORT', N) or .listen(N) in main.ts | | graphql.schemaPath | autoSchemaFile in app.module.ts, or glob for *.gql files | | graphql.endpointPath | GraphQLModule.forRoot config; defaults to /graphql | | graphql.resolverGlobs | Scans for *.resolver.ts files and derives minimal glob patterns | | swagger.appModulePath | Finds app.module.ts relative to project root | | swagger.title/desc/ver | DocumentBuilder chain in main.ts | | auth | @Controller('auth') + @Get('token/...') pattern; AuthGuard usage | | variables | Seeds url, local_url, staging_url (placeholder), prod_url (placeholder), and authToken (if auth detected) |

If the config already exists, npx postman-init merges new detections without overwriting manual edits.


Syncing Folder Config

The npx postman-sync command scans resolver files for @Query and @Mutation decorators, extracts operation names, and writes the graphql.folders mapping in postman.config.json. This means you don't need to manually register every new resolver operation.

npm run postman:sync

How operations are detected

Two patterns are recognized:

  1. Explicit name@Query(() => Foo, { name: 'operationName' }) — the name property wins.
  2. Method name@Query(() => Foo)\n async methodName(...) — method name is used when no explicit name is set.

How folder names are derived

The folder name comes from the resolver file name:

| File | Folder name | | ----------------------------------------------- | ------------- | | src/meta-v2/resolvers/campaign.resolver.ts | Campaign | | src/meta-v2/resolvers/ad-group-ad.resolver.ts | Ad Group Ad | | src/metamarketing/resolvers/ads.resolver.ts | Ads | | src/metamarketing/metamarketing.resolver.ts | Metamarketing |

Overrides

Operations that live outside resolver files (e.g. hello in app.controller.ts) or that need manual reassignment use graphql.folderOverrides:

"folderOverrides": {
  "hello": "Debugging",
  "metaAccessTest": "Legacy",
  "allMetaCampaigns": "Legacy"
}

Overrides are applied after the auto-scan and always win. The sync script preserves overrides across runs.

Sub-folders (Queries / Mutations)

When graphql.subFolders is true (the default), each domain folder that contains both queries and mutations is split into Queries and Mutations sub-folders:

GraphQL / Campaigns
  ├── Queries
  │   ├── campaigns
  │   ├── campaign
  │   └── campaignsByBudget
  └── Mutations
      ├── createCampaign
      ├── updateCampaign
      └── deleteCampaign

Domains with only one type (e.g. a "Reports" folder with only queries) stay flat to avoid unnecessary nesting. Set graphql.subFolders to false to disable this entirely.


REST Controllers — Swagger Decorators

Swagger decorators on controllers and DTOs flow into the OpenAPI spec, which becomes the REST section of the Postman collection.

Controller-Level

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiParam,
} from '@nestjs/swagger';

@ApiTags('Leads') // <— folder name in Postman
@Controller('v2/leads')
export class LeadsController {
  @Get(':id')
  @ApiOperation({ summary: 'Get lead by ID' })
  @ApiParam({ name: 'id', description: 'Lead UUID' })
  @ApiResponse({
    status: 200,
    description: 'Lead found',
    type: LeadDto,
  })
  getById(@Param('id') id: string) {
    /* ... */
  }

  @Post()
  @ApiOperation({ summary: 'Create a lead' })
  @ApiResponse({ status: 201, type: LeadDto })
  create(@Body() dto: CreateLeadDto) {
    /* ... */
  }
}

DTO-Level

import { ApiProperty } from '@nestjs/swagger';

export class CreateLeadDto {
  @ApiProperty({
    description: 'Contact email',
    example: '[email protected]',
  })
  email: string;

  @ApiProperty({
    description: 'Lead source',
    example: 'facebook_form',
  })
  source: string;
}

Decorator Cheat Sheet

| Decorator | Where | Purpose | | --------------------------------------------- | ---------------- | ---------------------------- | | @ApiTags('name') | Controller class | Groups into a Postman folder | | @ApiOperation({ summary, description }) | Method | Request name + description | | @ApiParam({ name, description, example }) | Method | URL path parameter docs | | @ApiQuery({ name, description, example }) | Method | Query string parameter docs | | @ApiBody({ type, description }) | Method | Request body docs | | @ApiProperty({ description, example }) | DTO property | Field docs + example values | | @ApiResponse({ status, description, type }) | Method | Response docs |


GraphQL Resolvers — Description Decorators

NestJS code-first generates src/schema.gql with descriptions from your decorators. The generator reads descriptions from that schema.

Resolver-Level

import { Query, Mutation, Args } from '@nestjs/graphql';

@Resolver()
export class CampaignResolver {
  @Query(() => [Campaign], {
    description: 'List all campaigns for a Meta ad account.',
  })
  campaigns(
    @Args('accountId', {
      type: () => String,
      description: 'The ad account ID (without act_ prefix).',
    })
    accountId: string,
  ) {
    /* ... */
  }

  @Mutation(() => Campaign, {
    description: 'Create a Meta campaign. Defaults to PAUSED.',
  })
  createCampaign(
    @Args('accountId', { description: 'Ad account ID.' })
    accountId: string,

    @Args('input', {
      type: () => CreateCampaignInput,
      description: 'New campaign fields.',
    })
    input: CreateCampaignInput,
  ) {
    /* ... */
  }
}

Input/Object Types

import { InputType, Field } from '@nestjs/graphql';

@InputType({ description: 'Input for creating a Meta campaign.' })
export class CreateCampaignInput {
  @Field(() => String, { description: 'Campaign name.' })
  name: string;

  @Field(() => String, {
    description:
      'Meta objective (e.g. OUTCOME_TRAFFIC, OUTCOME_LEADS).',
  })
  objective: string;

  @Field(() => String, {
    nullable: true,
    description: 'Initial status. Defaults to PAUSED if omitted.',
  })
  status?: string;
}

Key Points

  • Always provide a description on @Query, @Mutation, @Field, and @Args.
  • Descriptions appear in the generated schema and in the Postman request docs.
  • For example hints, embed them in the description: 'Ad account ID (e.g. "123456")'.
  • JSDoc comments (/** ... */) are not reliably picked up when @Field(() => Type) is used. Stick to the description option.
  • @Extensions() is not used for examples (broken on @InputType fields).

GraphQL Example Variables

The generator can overlay example variable values onto GraphQL requests so they are ready to use in Postman.

Edit postman-generator/examples.json. Keys are the schema field names (from type Query / type Mutation):

{
  "campaigns": { "accountId": "123456" },
  "createCampaign": {
    "accountId": "123456",
    "input": {
      "name": "Test Campaign",
      "objective": "OUTCOME_AWARENESS",
      "status": "PAUSED"
    }
  }
}

Operations without an entry get type-based placeholder values from the converter.


GraphQL Query Post-Processing

After graphql-to-postman emits raw items, the generator runs a pipeline of normalization passes to produce queries that are readable, executable, and ergonomic inside Postman. In pipeline order:

| Pass | Purpose | Config knob | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | expandTruncatedQueries | The converter truncates any object-typed field at its depth limit. This pass walks each query, detects leaf object fields it left unexpanded (including fields with argument lists like feeds(pagination: ...)), and splices in a scalar-only selection so the request is still executable. | graphql.depth | | applyDedupRepeatedTypes | Three sub-passes on the parsed query AST: (1) repeat-type dedup — on every second-or-later encounter of an object type, keep only scalar leaves (including FK ids) and drop nested expansions, collapsing the fan-out that depth-only expansion produces on cyclic schemas; (2) sibling-field dedup — collapse duplicate sibling Fields to the richer variant (has-args > more-sub-selections > first-seen); (3) per-field variable scoping (see below). | graphql.dedupRepeatedTypes (default true) | | applyVariableDefaults | Exact-key override: for every key in variableDefaults that appears in a request's body.graphql.variables JSON, replace the value. | graphql.variableDefaults | | applyGqlExamples | If examples.json has an entry for the operation, it replaces the entire variables JSON. Richest form of seeding — resolver-aware, wins over earlier defaults. | examplesPath | | applyVariableDefaultsByType | For each declared operation variable, if its unwrapped input type is in variableDefaultsByType AND the key isn't already in the JSON body, seed it. Prefers inheriting from any existing sibling of the same type (so a rich $pagination example propagates to every $<field>_PaginationInput for consistency), falling back to the configured value. | graphql.variableDefaultsByType |

Per-field variable scoping

graphql-to-postman depth-expands cyclic schemas and emits one numbered variable per occurrence of the same argument ($pagination1, $pagination811, ..., $paginationN). The dedup pass rewrites these so:

  • Root operation fields keep the plain $<argName> form:

    query dealersV2($pagination: PaginationInput) {
      dealersV2(pagination: $pagination) { ... }
    }
  • Nested fields get $<fieldName>_<InputTypeName> so every collection inside the response can be paginated independently in Postman:

    query dealersByResource(
      $feeds_PaginationInput: PaginationInput,
      $mastersheets_PaginationInput: PaginationInput,
      $budgets_PaginationInput: PaginationInput,
      $campaigns_PaginationInput: PaginationInput,
      $ad_groups_PaginationInput: PaginationInput,
      $ads_PaginationInput: PaginationInput,
      $resource_key: String!,
      $resource_values: [String!]!
    ) {
      dealersByResource(resource_key: $resource_key, resource_values: $resource_values) {
        feeds(pagination: $feeds_PaginationInput) { ... }
        mastersheets(pagination: $mastersheets_PaginationInput) { ... }
        ...
      }
    }

Multiple occurrences of the same nested field share one variable. If two proposed variables would collide on the same name but carry different type signatures, later ones get _2, _3, ... suffixes.

Type-based variable defaults

variableDefaultsByType seeds defaults keyed by GraphQL input type name (unwrapped, so PaginationInput! / [PaginationInput!] both match PaginationInput). Typical entry:

"variableDefaultsByType": {
  "PaginationInput": { "take": 10, "skip": 0 }
}

Every variable declared with that type — including all the per-field $<field>_PaginationInput introduced by the dedup pass — picks up the default without needing to list them by name.

Precedence for a given variable key is: explicit resolver example (examples.json) > inherited from an existing sibling of the same type > variableDefaultsByType[typeName] > converter-emitted placeholder.


Configuration

All project-specific settings live in postman-generator/postman.config.json:

| Key | Description | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | collectionName | Name shown in Postman | | description | Collection-level description | | url | Default {{url}} variable value | | graphql.schemaPath | Path to the auto-generated .gql schema | | graphql.endpointPath | GraphQL endpoint path (default /graphql) | | graphql.depth | Query nesting depth for the converter (max 4) | | graphql.resolverGlobs | Glob patterns for resolver files (default ["src/**/*.resolver.ts"]) | | graphql.folderOverrides | Manual operation → folder assignments (applied after auto-scan) | | graphql.subFolders | Nest Queries/Mutations within domain folders (default true) | | graphql.folders | Auto-generated by npx postman-sync — operation → domain folder mapping | | graphql.dedupRepeatedTypes | Run the repeat-type / sibling-field / per-field-variable normalization passes on generated queries (default true; see GraphQL Query Post-Processing). Set to false to keep raw converter output. | | graphql.variableDefaults | Exact-key overrides applied to every request's body.graphql.variables — any matching top-level key is replaced with the configured value. | | graphql.variableDefaultsByType | Seed defaults keyed by GraphQL input type name (unwrapped). Every operation variable of that type is defaulted when missing from the JSON body; values already seeded by a resolver example or a same-typed sibling win over this fallback. | | swagger.specPath | Path to the generated Swagger JSON | | swagger.appModulePath | Relative path to the AppModule file (from project root) | | swagger.title | Swagger document title | | swagger.description | Swagger document description | | swagger.version | Swagger document version | | auth | Auth config (bearer token, bootstrap request) | | variables | Collection variable defaults | | environments | Per-env variable overrides (e.g. { dev: { url }, staging: {...}, prod: {...} }); each entry generates a *.{env}.postman_environment.json selectable from Postman's environment dropdown | | examplesPath | Path to the GQL examples JSON |


Adding a New Endpoint to the Collection

REST Controller

  1. Add Swagger decorators (@ApiTags, @ApiOperation, @ApiProperty, etc.) to your controller and DTOs.
  2. Run npm run postman (or npm run postman:swagger && npm run postman:generate).
  3. The new endpoint appears automatically in the REST folder.

GraphQL Resolver

  1. Add description to your @Query/@Mutation/@Field/@Args decorators.
  2. Optionally add example variables to postman-generator/examples.json.
  3. Run npm run postman:sync to pick up the new operation and assign it to a folder.
  4. Run npm run postman:generate (or npm run postman for the full pipeline).
  5. The new operation appears automatically in the correct domain folder.

Troubleshooting

| Problem | Fix | | -------------------------- | ------------------------------------------------------------------------------------ | | Swagger extraction fails | Ensure DB is reachable; use --skip-rest for GQL-only | | GraphQL operations missing | Run nest build first to regenerate schema.gql | | Examples not applied | Check that the key in examples.json matches the schema field name exactly | | Depth too shallow | Increase graphql.depth in config (max 4 via library, use CLI for deeper) | | REST routes not appearing | Add @ApiTags to the controller class so Swagger discovers it | | Config not found | Run npx postman-init to generate the initial postman-generator/postman.config.json |