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

@nest-langchain/langgraph

v0.5.0

Published

LangGraph decorators and discovery integration for NestJS.

Readme

@nest-langchain/langgraph

English | 한국어

NestJS decorators and discovery for LangGraph.

This package discovers decorated Nest providers, compiles LangGraph StateGraph instances, and registers them in @nest-langchain/core. It keeps LangGraph runtime behavior in this package instead of pushing it into core.

Install

pnpm add @nest-langchain/core @nest-langchain/langgraph @langchain/core @langchain/langgraph

Module

import { Module } from '@nestjs/common';
import { LangGraphModule } from '@nest-langchain/langgraph';

@Module({
  imports: [
    LangGraphModule.forRoot({
      global: true,
      checkpointer,
    }),
  ],
  providers: [SupportGraph],
})
export class AppModule {}

checkpointer is passed to LangGraph compile({ checkpointer }).

Async Checkpointer

Production checkpointers (PostgresSaver, MongoDBSaver, RedisSaver, ...) need async setup before they can be used. Use forRootAsync to resolve the checkpointer (and any other module options) from a factory that can await connection setup and inject NestJS dependencies such as ConfigService.

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres';
import { LangGraphModule } from '@nest-langchain/langgraph';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    LangGraphModule.forRootAsync({
      global: true,
      inject: [ConfigService],
      useFactory: async (config: ConfigService) => {
        const url = config.getOrThrow('DATABASE_URL');
        const checkpointer = await PostgresSaver.fromConnString(url);
        await checkpointer.setup();
        return { checkpointer };
      },
    }),
  ],
  providers: [SupportGraph],
})
export class AppModule {}

The factory returns LangGraphModuleFactoryOptions (autoDiscoverGraphs and checkpointer). Configure global on forRootAsync itself because NestJS module scope is static. When the checkpointer depends on another module's providers, list that module in imports (or make it global) so NestJS can inject its tokens.

Define A Graph

import { Annotation } from '@langchain/langgraph';
import { GraphNode, LangGraph } from '@nest-langchain/langgraph';

const SupportState = Annotation.Root({
  message: Annotation<string>(),
  intent: Annotation<'billing' | 'technical' | 'general'>(),
  response: Annotation<string>(),
});

@LangGraph({
  name: 'support-intake',
  state: SupportState,
})
export class SupportGraph {
  @GraphNode({ entry: true })
  classifyRequest(state: typeof SupportState.State) {
    return {
      intent: state.message.includes('card') ? 'billing' : 'general',
    };
  }

  @GraphNode({ finish: true })
  draftResponse(state: typeof SupportState.State) {
    return {
      response: `Route ${state.intent} request to support.`,
    };
  }
}

Decorated graph classes are Nest providers. Register them in the module providers array.

Execute Graphs

import { Injectable } from '@nestjs/common';
import { LangGraphRunner } from '@nest-langchain/langgraph';

@Injectable()
export class AgentRunner {
  constructor(private readonly graphs: LangGraphRunner) {}

  invoke(input: unknown) {
    return this.graphs.invoke('support-intake', input, {
      configurable: {
        thread_id: 'thread-1',
      },
    });
  }

  stream(input: unknown) {
    return this.graphs.stream('support-intake', input, {
      configurable: {
        thread_id: 'thread-1',
      },
    });
  }

  streamEvents(input: unknown) {
    return this.graphs.streamEvents(
      'support-intake',
      input,
      {
        configurable: {
          thread_id: 'thread-1',
        },
      },
      {
        version: 'v2',
      },
    );
  }
}

invoke() returns the final graph result. stream() and streamEvents() return async iterables; HTTP framing such as NDJSON and SSE should stay in the application controller.

LangGraph Helpers

The helpers wrap official LangGraph primitives directly.

Command Pattern

Use the Command Pattern when a node needs to update state and choose the next node in the same return value. commandTo() returns a LangGraph Command, and @GraphNode({ ends }) declares the possible dynamic destinations so LangGraph can validate the graph.

import { Annotation } from '@langchain/langgraph';
import {
  commandTo,
  ConditionalEdge,
  fanOut,
  GraphNode,
  LangGraph,
  parentHandoff,
  resumeWith,
} from '@nest-langchain/langgraph';

const ReviewState = Annotation.Root({
  approved: Annotation<boolean>(),
  owner: Annotation<string>(),
  reviewAreas: Annotation<string[]>(),
});

@LangGraph({
  name: 'review',
  state: ReviewState,
})
export class ReviewGraph {
  @GraphNode({
    entry: true,
    ends: ['approvedPath', 'rejectedPath'],
  })
  decide(state: typeof ReviewState.State) {
    return commandTo(state.approved ? 'approvedPath' : 'rejectedPath', {
      update: { owner: 'reviewer' },
    });
  }

  @GraphNode()
  planReviews() {
    return {
      reviewAreas: ['legal', 'security'],
    };
  }

  @ConditionalEdge({ from: 'planReviews' })
  fanout(state: typeof ReviewState.State) {
    return fanOut('reviewWorker', state.reviewAreas, (area) => ({
      owner: area,
    }));
  }

  @GraphNode({ name: 'reviewWorker' })
  reviewWorker(state: typeof ReviewState.State) {
    return {
      owner: `${state.owner}:reviewed`,
    };
  }

  @GraphNode()
  handoffToParent() {
    return parentHandoff('parentNode', {
      update: { owner: 'parent' },
    });
  }

  @GraphNode()
  resume() {
    return resumeWith({ owner: 'human-review' });
  }
}

For decorator-first routing, use @CommandNode when the target graph is explicit, @RouteCommandNode for same-graph routing, and @ParentHandoffNode when a subgraph must hand control back to a parent graph. If the decorated method already returns a LangGraph Command, the decorator passes it through; otherwise it wraps the method result as the command update.

import {
  CommandNode,
  ParentHandoffNode,
  RouteCommandNode,
} from '@nest-langchain/langgraph';

export class DecoratedRoutes {
  @CommandNode({
    name: 'remoteRoute',
    to: 'remoteNode',
    graph: 'remoteGraph',
  })
  remoteRoute() {
    return {
      output: 'remote',
    };
  }

  @RouteCommandNode({
    name: 'route',
    to: 'next',
  })
  route() {
    return {
      output: 'local',
    };
  }

  @ParentHandoffNode({
    name: 'escalate',
    to: 'supervisor',
  })
  escalate() {
    return {
      reason: 'needs-supervisor',
    };
  }
}

Parent handoff helpers do not auto-infer local ends; parent destinations live outside the child graph that LangGraph validates.

Typed Graph Builder

For larger graphs, defineTypedLangGraph() lets you declare stable local node keys once and use those keys for decorators, static edges, ends, commandTo, RouteCommandNode, sendTo, and fanOut. The builder converts keys to real LangGraph node names before metadata reaches the runtime.

import { Annotation } from '@langchain/langgraph';
import { defineTypedLangGraph } from '@nest-langchain/langgraph';

const SupportState = Annotation.Root({
  intent: Annotation<'billing' | 'general'>(),
});

const support = defineTypedLangGraph({
  name: 'support-intake',
  state: SupportState,
  nodes: {
    classify: 'classifyAndRoute',
    billing: 'handleBilling',
    draft: 'draftResponse',
  },
} as const);

@support.Graph({
  edges: support.edges(['billing', 'draft']),
})
export class SupportGraph {
  @support.Node('classify', {
    entry: true,
    ends: support.ends('billing'),
  })
  classify() {
    return support.commandTo('billing', {
      update: { intent: 'billing' },
    });
  }

  @support.Node('billing')
  handleBilling() {
    return {
      intent: 'billing',
    };
  }

  @support.Node('draft', {
    finish: true,
  })
  draft() {
    return {};
  }
}

Use support.node('draft') when an existing API needs the runtime node name. Parent handoff and remote CommandNode destinations stay string-based because they point outside the local graph node map. The string-based decorators and helpers remain valid lower-level APIs.

@nest-langchain/demo-langgraph runs the same patterns through HTTP: command routing, Send fan-out, interrupt/resume, and explicit subgraph transforms.

Demo

pnpm --filter @nest-langchain/demo-langgraph start

curl "http://localhost:3000/graphs"
curl -X POST "http://localhost:3000/graphs/support-intake" \
  -H "content-type: application/json" \
  -d '{"message":"Checkout fails with a saved card error.","customerTier":"enterprise","channel":"web"}'

Use @nest-langchain/demo-visualization to inspect the same registry surface through hosted graph docs:

pnpm --filter @nest-langchain/demo-visualization start
open "http://localhost:3000/ai/graphs"