Getting Started with LangChain.js: Building Practical LLM Applications
By Jake Morrison LangChain.js is one such tool. It provides a structured way to build applications powered by large language models (LLMs) using JavaScript or TypeScript. If you’re working with LLMs in the JavaScript ecosystem, understanding LangChain.js is a must. This guide will walk you through practical, actionable steps to get started and build real applications.
What is LangChain.js and Why Use It?
LangChain.js is a library designed to help developers create LLM-powered applications. It doesn’t replace the LLMs themselves, but rather provides an abstraction layer and a set of tools to chain different LLM calls, integrate with other data sources, and manage complex interactions.
Think of it this way: directly calling an LLM API is like using a raw database query. LangChain.js provides an ORM (Object-Relational Mapper) for LLMs. It handles common patterns like:
* **Chains:** Combining multiple LLM calls or other steps into a sequence.
* **Prompts:** Managing and formatting input for LLMs.
* **Agents:** Allowing LLMs to make decisions and use tools.
* **Retrieval:** Integrating external data (your documents, databases) with LLMs.
* **Memory:** Giving LLMs a way to remember past interactions.
The main benefit of using LangChain.js is increased productivity and maintainability. You write less boilerplate code, your application logic becomes clearer, and it’s easier to swap out LLM providers or add new features. For anyone building with LLMs in JavaScript, LangChain.js offers significant advantages.
Setting Up Your LangChain.js Project
Before we write any code, let’s get our environment ready. You’ll need Node.js installed.
1. **Create a new project directory:**
“`bash
mkdir langchain-js-project
cd langchain-js-project
“`
2. **Initialize a Node.js project:**
“`bash
npm init -y
“`
3. **Install LangChain.js and an LLM provider:**
We’ll start with OpenAI for our examples, but LangChain.js supports many others (Hugging Face, Google, etc.).
“`bash
npm install langchain @openai/openai
“`
If you prefer TypeScript:
“`bash
npm install langchain @openai/openai typescript @types/node ts-node
npx tsc –init
“`
Then, update `tsconfig.json` to include `”moduleResolution”: “node”` and `”esModuleInterop”: true`.
4. **Set up your API key:**
You’ll need an OpenAI API key. Never hardcode API keys in your code. Use environment variables.
Create a `.env` file in your project root:
“`
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
“`
To load environment variables, install `dotenv`:
“`bash
npm install dotenv
“`
Then, at the very top of your main script file (e.g., `index.js` or `src/index.ts`):
“`javascript
require(‘dotenv’).config();
“`
or for TypeScript/ESM:
“`typescript
import ‘dotenv/config’;
“`
Now you’re ready to start building with LangChain.js.
Your First LLM Call with LangChain.js
Let’s make a simple call to an LLM.
“`javascript
// index.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
async function simpleLLMCall() {
const model = new OpenAI({ temperature: 0.7 }); // temperature controls randomness
const prompt = “What is the capital of France?”;
const result = await model.call(prompt);
console.log(result);
}
simpleLLMCall();
“`
To run this: `node index.js`.
You should see “Paris.” or similar output. This is the most basic interaction. LangChain.js wraps the OpenAI API call, making it consistent with other LLM providers.
Working with Prompts: Templates and Variables
Directly embedding prompts in your code can become messy. LangChain.js provides `PromptTemplate` to manage prompts effectively.
“`javascript
// promptExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
async function templatedPrompt() {
const model = new OpenAI({ temperature: 0.7 });
const template = “What is a good name for a company that makes {product}?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“product”],
});
const formattedPrompt = await prompt.format({ product: “colorful socks” });
console.log(“Formatted Prompt:”, formattedPrompt);
const result = await model.call(formattedPrompt);
console.log(“LLM Result:”, result);
}
templatedPrompt();
“`
Run this with `node promptExample.js`.
This demonstrates how `PromptTemplate` allows you to define prompts with placeholders (`{product}`) that you can fill dynamically. This is crucial for building flexible applications.
Chains: Connecting LLM Calls and Logic
Chains are where LangChain.js really shines. They allow you to combine multiple steps, including LLM calls, into a single, coherent workflow.
Simple LLM Chain
The `LLMChain` is the most basic chain. It takes a `PromptTemplate` and an LLM, then formats the prompt and passes it to the LLM.
“`javascript
// llmChainExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
const { LLMChain } = require(‘langchain/chains’);
async function simpleLLMChain() {
const model = new OpenAI({ temperature: 0.7 });
const template = “What is the best {activity} to do in {city}?”;
const prompt = new PromptTemplate({
template: template,
inputVariables: [“activity”, “city”],
});
const chain = new LLMChain({ llm: model, prompt: prompt });
const result = await chain.call({ activity: “food”, city: “Rome” });
console.log(“Chain Result:”, result);
// The result object will contain an ‘text’ property with the LLM’s output.
}
simpleLLMChain();
“`
Run with `node llmChainExample.js`.
Notice how `chain.call()` takes an object with the input variables, and it handles formatting the prompt and calling the LLM. This is a cleaner way to interact with your LLM.
Sequential Chains: Multi-Step Workflows
Sometimes you need to perform multiple LLM calls in sequence, where the output of one call becomes the input for the next. This is where `SimpleSequentialChain` comes in handy.
“`javascript
// sequentialChainExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { PromptTemplate } = require(‘langchain/prompts’);
const { LLMChain, SimpleSequentialChain } = require(‘langchain/chains’);
async function multiStepChain() {
const model = new OpenAI({ temperature: 0.7 });
// Chain 1: Generate a company name
const nameTemplate = “What is a good name for a company that makes {product}?”;
const namePrompt = new PromptTemplate({
template: nameTemplate,
inputVariables: [“product”],
});
const nameChain = new LLMChain({ llm: model, prompt: namePrompt });
// Chain 2: Describe the company
const descriptionTemplate = “Write a short, engaging marketing slogan for a company named {companyName}.”;
const descriptionPrompt = new PromptTemplate({
template: descriptionTemplate,
inputVariables: [“companyName”],
});
const descriptionChain = new LLMChain({ llm: model, prompt: descriptionPrompt });
// Combine them into a sequential chain
const overallChain = new SimpleSequentialChain({
chains: [nameChain, descriptionChain],
verbose: true, // Set to true to see intermediate steps
});
const result = await overallChain.run(“eco-friendly shoes”);
console.log(“Overall Result:”, result);
}
multiStepChain();
“`
Run with `node sequentialChainExample.js`.
The `SimpleSequentialChain` takes the output of `nameChain` and automatically uses it as the input (`companyName`) for `descriptionChain`. This pattern is extremely powerful for breaking down complex tasks. LangChain.js makes this easy.
Retrieval Augmented Generation (RAG): Using Your Own Data
LLMs are powerful, but their knowledge is limited to their training data. For applications requiring current information or specific domain knowledge (your company’s documents, for example), you need to augment the LLM’s knowledge with external data. This is called Retrieval Augmented Generation (RAG).
The core idea of RAG is:
1. **Load Data:** Get your documents (PDFs, text files, web pages).
2. **Split Data:** Break large documents into smaller, manageable chunks.
3. **Embed Data:** Convert these chunks into numerical representations (embeddings) using an embedding model.
4. **Store Data:** Store these embeddings in a vector database.
5. **Retrieve:** When a user asks a question, embed the question, search the vector database for similar document chunks.
6. **Augment Prompt:** Add these retrieved chunks to the LLM prompt.
7. **Generate:** The LLM uses this augmented prompt to generate a more informed answer.
LangChain.js provides components for each of these steps.
Step 1: Loading Documents
LangChain.js has various document loaders. Let’s use a `TextLoader`.
“`javascript
// ragExample.js – Part 1: Document Loading
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
async function loadDocuments() {
// Create a dummy text file for demonstration
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
The quick brown fox jumps over the lazy dog.
This document talks about various animals and their behaviors.
Dogs are known for their loyalty and playfulness.
Foxes are cunning and solitary creatures.
Cats enjoy napping and chasing mice.
`);
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
console.log(“Loaded Documents:”, docs);
// Each doc will have pageContent and metadata
}
loadDocuments();
“`
Step 2: Splitting Documents
Large documents need to be split into smaller chunks so that they fit within the LLM’s context window and to improve retrieval relevance.
“`javascript
// ragExample.js – Part 2: Splitting
require(‘dotenv’).config();
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
const { RecursiveCharacterTextSplitter } = require(‘langchain/text_splitter’);
async function splitDocuments() {
// (Assume my_document.txt is already created from Part 1)
const loader = new TextLoader(“my_document.txt”);
const docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100, // Max characters per chunk
chunkOverlap: 20, // Overlap between chunks to maintain context
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(“Split Documents:”, splitDocs);
}
splitDocuments();
“`
Step 3 & 4: Embedding and Storing Documents (Vector Store)
This is where embeddings and vector databases come in. We’ll use OpenAI embeddings and `HNSWLib` (a local, in-memory vector store) for simplicity. For production, you’d use a dedicated vector database like Pinecone, Chroma, Weaviate, etc.
“`javascript
// ragExample.js – Part 3: Embeddings and Vector Store
require(‘dotenv’).config();
const { OpenAIEmbeddings } = require(‘langchain/embeddings/openai’);
const { HNSWLib } = require(‘langchain/vectorstores/hnswlib’);
const { RecursiveCharacterTextSplitter } = require(‘langchain/text_splitter’);
const { TextLoader } = require(‘langchain/document_loaders/fs/text’);
const { OpenAI } = require(‘langchain/llms/openai’);
const { RetrievalQAChain } = require(‘langchain/chains’);
async function ragApplication() {
// 1. Load and Split Documents
const fs = require(‘fs’);
fs.writeFileSync(‘my_document.txt’, `
The quick brown fox jumps over the lazy dog.
This document talks about various animals and their behaviors.
Dogs are known for their loyalty and playfulness.
Foxes are cunning and solitary creatures.
Cats enjoy napping and chasing mice.
A group of cats is called a clowder.
`);
const loader = new TextLoader(“my_document.txt”);
const rawDocs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 100,
chunkOverlap: 20,
});
const docs = await splitter.splitDocuments(rawDocs);
// 2. Create Embeddings and Store in Vector Store
const embeddings = new OpenAIEmbeddings();
const vectorStore = await HNSWLib.fromDocuments(docs, embeddings);
// 3. Create a Retriever
const retriever = vectorStore.asRetriever();
// 4. Create the Retrieval QA Chain
const model = new OpenAI({ temperature: 0 });
const chain = RetrievalQAChain.fromLLM(model, retriever);
// 5. Ask a question
const question = “What is a group of cats called?”;
const result = await chain.call({ query: question });
console.log(“RAG Answer:”, result.text);
const anotherQuestion = “Tell me about foxes.”;
const anotherResult = await chain.call({ query: anotherQuestion });
console.log(“Another RAG Answer:”, anotherResult.text);
}
ragApplication();
“`
Run with `node ragExample.js`.
This example puts all the RAG pieces together. The `RetrievalQAChain` abstracts the process of retrieving relevant documents and using them to answer a question. This is a practical way to build chatbots or question-answering systems over your specific data using LangChain.js.
Agents: LLMs Using Tools
Agents allow LLMs to make decisions about which tools to use and in what order, based on the user’s input. This enables more dynamic and complex interactions. Tools can be anything: a search engine, a calculator, an API call to your internal systems, or even another LLM chain.
Let’s create a simple agent that can perform calculations using a `Calculator` tool.
“`javascript
// agentExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { initializeAgentExecutorWithOptions } = require(‘langchain/agents’);
const { Calculator } = require(‘langchain/tools/calculator’);
async function runAgent() {
const model = new OpenAI({ temperature: 0 });
const tools = [new Calculator()];
// Initialize the agent executor
const executor = await initializeAgentExecutorWithOptions(tools, model, {
agentType: “openai-functions”, // Use OpenAI’s function calling feature
verbose: true, // See the agent’s thought process
});
console.log(“Calling agent with a simple math question…”);
const result1 = await executor.call({ input: “What is 123 plus 456?” });
console.log(“Agent Result 1:”, result1.output);
console.log(“\nCalling agent with a more complex question…”);
const result2 = await executor.call({ input: “What is the square root of 625 multiplied by 3?” });
console.log(“Agent Result 2:”, result2.output);
}
runAgent();
“`
Run with `node agentExample.js`.
With `verbose: true`, you’ll see the agent’s “thoughts”: it identifies that a calculation is needed, uses the `Calculator` tool, and then provides the answer. This is a powerful pattern for building applications that go beyond simple text generation. LangChain.js simplifies the setup of these agents.
Memory: Maintaining Conversation Context
By default, LLMs are stateless. Each API call is independent. For conversational applications, the LLM needs to remember past interactions. LangChain.js provides various memory types to achieve this.
`ConversationBufferMemory` is a common choice, storing the raw conversation history.
“`javascript
// memoryExample.js
require(‘dotenv’).config();
const { OpenAI } = require(‘langchain/llms/openai’);
const { ConversationChain } = require(‘langchain/chains’);
const { ConversationBufferMemory } = require(‘langchain/memory’);
async function conversationalApp() {
const model = new OpenAI({ temperature: 0.7 });
const memory = new ConversationBufferMemory();
const chain = new ConversationChain({ llm: model, memory: memory });
console.log(“Starting conversation…”);
let result1 = await chain.call({ input: “Hi, my name is Jake.” });
console.log(“User: Hi, my name is Jake.”);
console.log(“AI:”, result1.response);
let result2 = await chain.call({ input: “What is my name?” });
console.log(“User: What is my name?”);
console.log(“AI:”, result2.response);
let result3 = await chain.call({ input: “Tell me a fun fact about JavaScript.” });
console.log(“User: Tell me a fun fact about JavaScript.”);
console.log(“AI:”, result3.response);
}
conversationalApp();
“`
Run with `node memoryExample.js`.
The `ConversationBufferMemory` maintains the chat history, allowing the LLM to answer “What is my name?” correctly based on the previous turn. LangChain.js offers other memory types for more advanced use cases, like summarizing conversations or storing only specific parts.
Practical Tips for LangChain.js Development
1. **Start Simple:** Don’t try to build a complex agent with multiple tools and memory right away. Begin with simple LLM calls and `LLMChain`.
2. **Use `verbose: true`:** When debugging chains or agents, setting `verbose: true` is invaluable. It shows you the intermediate steps, the prompts being sent, and the responses received, helping you understand why your chain might not be behaving as expected.
3. **Manage API Keys Securely:** Always use environment variables for your API keys. Never commit them to version control.
4. **Understand Context Windows:** Be mindful of the token limits of your chosen LLM. Long prompts, extensive conversation history, or many retrieved documents can quickly exceed these limits. LangChain.js has tools to help manage this, but it’s important to be aware.
5. **Experiment with `temperature`:** The `temperature` parameter controls the randomness of the LLM’s output. Higher values (e.g., 0.7-1.0) lead to more creative, less deterministic responses. Lower values (e.g., 0.0-0.3) are more factual and consistent. Adjust this based on your application’s needs.
6. **Explore Documentation:** The LangChain.js documentation is thorough. If you’re looking for a specific loader, chain, or tool, chances are it’s covered there.
7. **Consider Production Vector Stores:** For real-world RAG applications, replace `HNSWLib` with a persistent vector database like Pinecone, Chroma, or Weaviate. This will allow you to scale and store large amounts of data.
8. **Error Handling:** Implement solid error handling for API calls, especially in production environments. Network issues, rate limits, or invalid inputs can cause failures.
Frequently Asked Questions about LangChain.js
Q1: Is LangChain.js only for OpenAI models?
No, LangChain.js supports a wide range of LLM providers, including OpenAI, Hugging Face models (via `HuggingFaceHub` or `HuggingFaceInference`), Google (e.g., `GoogleGenerativeAI`), Anthropic, and many more. The core LangChain.js abstractions allow you to swap out LLM providers with minimal code changes.
Q2: What’s the difference between `model.call()` and `chain.call()` or `chain.run()`?
`model.call()` is the most direct way to interact with a single LLM instance, passing a raw string prompt. `chain.call()` is used for chains and takes an object containing input variables (which are then formatted by a `PromptTemplate` if used within the chain). `chain.run()` is a convenience method for chains that only have a single input variable and directly returns the output text. For more complex chains with multiple inputs/outputs, `chain.call()` is preferred.
Q3: How can I integrate LangChain.js with my existing database or API?
You can integrate LangChain.js with your existing systems by creating custom tools for agents. A tool is essentially a function that takes an input string and returns an output string. You can define a tool that queries your database, calls an internal API, or performs any other custom logic, and then make this tool available to your LangChain.js agent. For RAG, you can create custom `DocumentLoader` implementations to load data from your specific data sources.
Q4: When should I use LangChain.js over directly calling LLM APIs?
Use LangChain.js when you need to build applications that involve more than a single, isolated LLM call. If your application requires:
* Multi-step reasoning (chains).
* Integrating external data (RAG).
* Giving the LLM access to external tools (agents).
* Managing conversation history (memory).
* Easily swapping LLM providers.
* More structured and maintainable code.
If you’re just making a one-off prompt for testing, direct API calls might be simpler, but for any practical application, LangChain.js quickly becomes indispensable.
Conclusion
LangChain.js is a powerful framework for building sophisticated LLM applications in JavaScript and TypeScript. By understanding its core components—LLMs, Prompts, Chains, Retrieval, Agents, and Memory—you can construct intelligent systems that go beyond simple text generation. This guide has provided a practical, actionable starting point. From simple LLM calls to complex RAG pipelines and agents, LangChain.js offers the tools to bring your AI automation ideas to life. Start experimenting, build small projects, and you’ll quickly discover the potential of LangChain.js in your development workflow.
🕒 Last updated: · Originally published: March 15, 2026