<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yohan Lasorsa</title>
    <description>The latest articles on DEV Community by Yohan Lasorsa (@sinedied).</description>
    <link>https://dev.to/sinedied</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F137508%2F3ffe451c-25a3-46d0-b347-b06d7fd118c8.jpg</url>
      <title>DEV Community: Yohan Lasorsa</title>
      <link>https://dev.to/sinedied</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sinedied"/>
    <language>en</language>
    <item>
      <title>Host Your Node.js MCP Server on Azure Functions in 1 Simple Step</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Tue, 09 Dec 2025 15:44:53 +0000</pubDate>
      <link>https://dev.to/azure/host-your-nodejs-mcp-server-on-azure-functions-in-3-simple-steps-3ao8</link>
      <guid>https://dev.to/azure/host-your-nodejs-mcp-server-on-azure-functions-in-3-simple-steps-3ao8</guid>
      <description>&lt;p&gt;Building AI agents with the Model Context Protocol (MCP) is powerful, but when it comes to hosting your MCP server in production, you need a solution that's reliable, scalable, and cost-effective. What if you could deploy your regular Node.js MCP server to a serverless platform that handles scaling automatically while you only pay for what you use?&lt;/p&gt;

&lt;p&gt;Let's explore how Azure Functions now supports hosting MCP servers built with the official Anthropic MCP SDK, giving you serverless scaling with almost no changes in your code.&lt;/p&gt;

&lt;p&gt;Grab your favorite hot beverage, and let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Azure Functions now supports hosting Node.js MCP servers using the official Anthropic SDK&lt;/li&gt;
&lt;li&gt;Only 1 simple configuration needed: adding &lt;code&gt;host.json&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Currently supports HTTP Streaming protocol with stateless servers&lt;/li&gt;
&lt;li&gt;Serverless hosting means automatic scaling and pay-per-use pricing&lt;/li&gt;
&lt;li&gt;Deploy with one command using Infrastructure as Code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What will you learn here?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Understand how MCP servers work on Azure Functions&lt;/li&gt;
&lt;li&gt;Configure a Node.js MCP server for Azure Functions hosting&lt;/li&gt;
&lt;li&gt;Test your MCP server locally and with real AI agents&lt;/li&gt;
&lt;li&gt;Deploy your MCP server with Infrastructure as Code and AZD&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reference links for everything we use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; - Official MCP documentation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/azure/azure-functions/functions-overview" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt; - Serverless compute platform&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/modelcontextprotocol/typescript-sdk" rel="noopener noreferrer"&gt;Anthropic MCP SDK&lt;/a&gt; - Official TypeScript SDK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/overview" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt; - One-command deployment tool&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs" rel="noopener noreferrer"&gt;Full sample project&lt;/a&gt; - Complete burger ordering system with MCP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure-Samples/mcp-sdk-functions-hosting-node" rel="noopener noreferrer"&gt;Simple example&lt;/a&gt; - Minimal MCP server starter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/anthonychu/create-functions-mcp-server" rel="noopener noreferrer"&gt;GitHub Copilot prompt helper&lt;/a&gt; - Automated setup by Anthony Chu&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 22 or higher&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://azure.microsoft.com/free" rel="noopener noreferrer"&gt;Azure account&lt;/a&gt; (free signup, or if you're a student, &lt;a href="https://azure.microsoft.com/free/students" rel="noopener noreferrer"&gt;get free credits here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aka.ms/azure-dev/install" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt; (for deployment)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/signup" rel="noopener noreferrer"&gt;GitHub account&lt;/a&gt; (optional, for using Codespaces)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is MCP and why does it matter?
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol is an open standard that enables AI models to securely interact with external tools and data sources. Instead of hardcoding tool integrations, you build an MCP server that exposes capabilities (like browsing a menu, placing orders, or querying a database) as tools that any MCP-compatible AI agent can discover and use. MCP is model-agnostic, meaning it can work with any LLM that supports the protocol, including models from Anthropic, OpenAI, and others. It's also worth noting that MCP supports more than just tool calls, though that's its most common use case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkujo718hq9m24ouev5zl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkujo718hq9m24ouev5zl.png" alt="Schema showing MCP interfacing with different tool servers" width="722" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The challenge? &lt;strong&gt;Running MCP servers in production requires infrastructure&lt;/strong&gt;. You need to handle scaling, monitoring, and costs. That's where Azure Functions comes in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🚨 Free course alert!&lt;/strong&gt; If you're new to MCP, check out the &lt;a href="https://github.com/microsoft/mcp-for-beginners" rel="noopener noreferrer"&gt;MCP for Beginners&lt;/a&gt; course to get up to speed quickly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Azure Functions for MCP servers?
&lt;/h2&gt;

&lt;p&gt;Azure Functions is a serverless compute platform that's perfect for MCP servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero infrastructure management&lt;/strong&gt;: No servers to maintain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic scaling&lt;/strong&gt;: Handles traffic spikes seamlessly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-effective&lt;/strong&gt;: Pay only for actual execution time (with generous free grant)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in monitoring&lt;/strong&gt;: Application Insights integration out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global distribution&lt;/strong&gt;: Deploy to regions worldwide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new Azure Functions support means you can take your existing Node.js MCP server and deploy it to a production-ready serverless environment with minimal changes. This comes up as an additional option for native Node.js MCP hosting, but you can still use the &lt;a href="https://learn.microsoft.com/azure/azure-functions/functions-bindings-mcp?pivots=programming-language-typescript" rel="noopener noreferrer"&gt;Azure Functions MCP bindings&lt;/a&gt; that were available before.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 simple step to enable Functions hosting
&lt;/h2&gt;

&lt;p&gt;Let's break down what you need to add to your existing Node.js MCP server to run it on Azure Functions. I'll use a &lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs/tree/main/packages/burger-mcp" rel="noopener noreferrer"&gt;real-world example&lt;/a&gt; from our burger ordering system.&lt;/p&gt;

&lt;p&gt;If you already have a working Node.js MCP server, you can just follow this to make it compatible with Azure Functions hosting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add the &lt;code&gt;host.json&lt;/code&gt; configuration
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;host.json&lt;/code&gt; file at the root of your Node.js project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurationProfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-custom-handler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customHandler"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"defaultExecutablePath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"arguments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"lib/server.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"DefaultAuthorizationLevel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"anonymous"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3000"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Adjust the &lt;code&gt;arguments&lt;/code&gt; array to point to your compiled server file (e.g., &lt;code&gt;lib/server.js&lt;/code&gt; or &lt;code&gt;dist/server.js&lt;/code&gt;), depending on your build setup. You can also change the port if needed to match your server configuration.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;hosts.json&lt;/code&gt; file holds &lt;a href="https://learn.microsoft.com/azure/azure-functions/functions-host-json" rel="noopener noreferrer"&gt;metadata configuration&lt;/a&gt; for the Functions runtime. The most important part here is the &lt;code&gt;customHandler&lt;/code&gt; section. It configures the Azure Functions runtime to run your Node.js MCP server as a &lt;em&gt;custom handler&lt;/em&gt;, which allows you to use any HTTP server framework (like Express, Fastify, etc.) without modification (&lt;strong&gt;tip: it can do more than MCP servers!&lt;/strong&gt; 😉).&lt;/p&gt;

&lt;p&gt;There's no step 2 or 3. That's it! 😎&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We're not covering the authentication and authorization aspects of Azure Functions here, but you can easily &lt;a href="https://learn.microsoft.com/azure/azure-functions/functions-mcp-tutorial?tabs=self-hosted&amp;amp;pivots=programming-language-typescript#enable-built-in-server-authorization-and-authentication" rel="noopener noreferrer"&gt;add those later if needed&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Real-world example: Burger MCP Server
&lt;/h2&gt;

&lt;p&gt;Let's look at how this works in practice with a &lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs/tree/main/packages/burger-mcp" rel="noopener noreferrer"&gt;burger ordering MCP server&lt;/a&gt;. This server exposes 9 tools for AI agents to interact with a burger API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_burgers&lt;/code&gt; - Browse the menu&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_burger_by_id&lt;/code&gt; - Get burger details&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;place_order&lt;/code&gt; - Place an order&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_orders&lt;/code&gt; - View order history&lt;/li&gt;
&lt;li&gt;And more...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the complete server implementation using Express and the MCP SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StreamableHTTPServerTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/streamableHttp.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getMcpServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mcp.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Handle all MCP Streamable HTTP requests&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/mcp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamableHTTPServerTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sessionIdGenerator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Connect the transport to the MCP server&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMcpServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Handle the request with the transport&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Clean up when the response is closed&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Note: error handling not shown for brevity&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// The port configuration&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Burger MCP server listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP tools are defined using the official SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;McpServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modelcontextprotocol/sdk/server/mcp.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;McpServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;burger-mcp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get_burgers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Get a list of all burgers in the menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;burgerApiUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/burgers`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;burgers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;burgers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ... more tools&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the actual implementation of the tool is forwarding an HTTP request to the burger API and returning the result in the MCP response format. This is a common pattern for MCP tools in enterprise contexts, that act as wrappers around one or more existing APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current limitations
&lt;/h3&gt;

&lt;p&gt;Note that this Azure Functions MCP hosting currently has some limitations: &lt;strong&gt;it only supports stateless servers using the HTTP Streaming protocol&lt;/strong&gt;. The legacy SSE protocol is not supported as it requires stateful connections, so you'll either have to migrate your client to use HTTP Streaming or use another hosting option, like using containers for example.&lt;/p&gt;

&lt;p&gt;For most use cases, HTTP Streaming is the recommended approach anyway as it's more scalable and doesn't require persistent connections. Stateful MCP servers comes with additional complexity challenges and have limited scalability if you need to handle many concurrent connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the MCP server locally
&lt;/h2&gt;

&lt;p&gt;First let's run the MCP server locally and play a bit with it.&lt;/p&gt;

&lt;p&gt;If you don't want to bother with setting up a local environment, you can use the following link or open it in a new tab to launch a GitHub Codespace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codespaces.new/Azure-Samples/mcp-agent-langchainjs?hide_repo_select=true&amp;amp;ref=main&amp;amp;quickstart=true" rel="noopener noreferrer"&gt;Create Codespace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will open a VS Code environment in your browser with the repo already cloned and all the tools installed and ready to go. Otherwise you can just &lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs" rel="noopener noreferrer"&gt;clone the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have the code ready, open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Start the burger MCP server and API&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start multiple services locally, including the Burger API and the MCP server, which will be available at &lt;code&gt;http://localhost:3000/mcp&lt;/code&gt;. This may take a few seconds, wait until you see this message in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 All services ready 🚀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're only interested in the MCP server for now, so let's focus on that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using MCP Inspector
&lt;/h3&gt;

&lt;p&gt;The easiest way to test the MCP server is with the MCP Inspector tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @modelcontextprotocol/inspector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the URL shown in the console in your browser, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set transport type to &lt;strong&gt;Streamable HTTP&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your local server URL: &lt;code&gt;http://localhost:3000/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Connect&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After you're connected, go to the &lt;strong&gt;Tools&lt;/strong&gt; tab to list available tools. You can then try the &lt;code&gt;get_burgers&lt;/code&gt; tool to see the burger menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo3lgw3v80we4v31p57u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuo3lgw3v80we4v31p57u.png" alt="MCP Inspector Screenshot" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using GitHub Copilot (with remote MCP)
&lt;/h3&gt;

&lt;p&gt;Configure GitHub Copilot to use your deployed MCP server by adding this to your project's &lt;code&gt;.vscode/mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"burger-mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click on "Start" button that will appear in the JSON file to activate the MCP server connection.&lt;/p&gt;

&lt;p&gt;Now you can use Copilot in agent mode and ask things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What spicy burgers do you have?"&lt;/li&gt;
&lt;li&gt;"Place an order for two cheeseburgers"&lt;/li&gt;
&lt;li&gt;"Show my recent orders"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copilot will automatically discover and use the MCP tools! 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If Copilot doesn't call the burger MCP tools, try checking if it's enabled by clicking on the tool icon in the chat input box and ensuring that "burger-mcp" is selected. You can also force tool usage by adding &lt;code&gt;#burger-mcp&lt;/code&gt; in your prompt.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  (Bonus) Deploying to Azure with Infrastructure as Code
&lt;/h2&gt;

&lt;p&gt;Deploying an application to Azure is usually not the fun part, especially when it involves multiple resources and configurations.&lt;br&gt;
With the &lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/overview" rel="noopener noreferrer"&gt;Azure Developer CLI (AZD)&lt;/a&gt;, you can define your entire application infrastructure and deployment process as code, and deploy everything with a single command.&lt;/p&gt;

&lt;p&gt;If you've used the automated setup with GitHub Copilot, you should already have the necessary files. Our burger example also comes with these files pre-configured. The MCP server is defined as a service in &lt;code&gt;azure.yaml&lt;/code&gt;, and the files under the &lt;code&gt;infra&lt;/code&gt; folder defines the Azure Functions app and related resources.&lt;/p&gt;

&lt;p&gt;Here's the relevant part of &lt;code&gt;azure.yaml&lt;/code&gt; that defines the burger MCP service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp-agent-langchainjs&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;burger-mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./packages/burger-mcp&lt;/span&gt;
    &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ts&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the infrastructure files can look intimidating at first, you don't need to understand all the details to get started. There are tons of templates and examples available to help you get going quickly, the important part is that everything is defined as code, so you can version control it and reuse it.&lt;/p&gt;

&lt;p&gt;Now let's deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Login to Azure&lt;/span&gt;
azd auth login

&lt;span class="c"&gt;# Provision resources and deploy&lt;/span&gt;
azd up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick your preferred Azure region when prompted (if you're not sure, choose &lt;strong&gt;East US2&lt;/strong&gt;), and voilà! In a few minutes, you'll have a fully deployed MCP server running on Azure Functions.&lt;/p&gt;

&lt;p&gt;Once the deployment is finished, the CLI will show you the URL of the deployed resources, including the MCP server endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95d7ypphekrsa5v8ecn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95d7ypphekrsa5v8ecn3.png" alt="AZD deployment output for the burger MCP example app" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example projects
&lt;/h2&gt;

&lt;p&gt;The burger MCP server is actually part of a larger example project that demonstrates building an AI agent with LangChain.js, that uses the burger MCP server to place orders. If you're interested in the next steps of building an AI agent on top of MCP, this is a great resource as it includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI agent web API using LangChain.js&lt;/li&gt;
&lt;li&gt;Web app interface built with Lit web components&lt;/li&gt;
&lt;li&gt;MCP server on Functions (the one we just saw)&lt;/li&gt;
&lt;li&gt;Burger ordering API (used by the MCP server)&lt;/li&gt;
&lt;li&gt;Live order visualization&lt;/li&gt;
&lt;li&gt;Complete Infrastructure as Code, to deploy everything with one command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if you're only interested in the MCP server part, then you might want to look at this simpler example that you can use as a starting point for your own MCP servers: &lt;a href="https://github.com/Azure-Samples/mcp-sdk-functions-hosting-node" rel="noopener noreferrer"&gt;mcp-sdk-functions-hosting-node&lt;/a&gt; is a server template for a Node.js MCP server using TypeScript and MCP SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about the cost?
&lt;/h2&gt;

&lt;p&gt;Azure Functions Flex Consumption pricing is attractive for MCP servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free grant&lt;/strong&gt;: 1 million requests and 400,000 GB-s execution time per month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After free grant&lt;/strong&gt;: Pay only for actual execution time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic scaling&lt;/strong&gt;: From zero to hundreds of instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The free grant is generous enough to allow running a typical MCP server with moderate usage, and all the experimentation you might need. It's easy to configure the scaling limits to control costs as needed, with an option to scale down to zero when idle. This flexibility is why Functions is my personal go-to choice for TypeScript projects on Azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Hosting MCP servers on Azure Functions gives you the best of both worlds: the simplicity of serverless infrastructure and the power of the official Anthropic SDK. With just &lt;strong&gt;one simple configuration step&lt;/strong&gt;, you can take your existing Node.js MCP server and deploy it to a production-ready, auto-scaling platform.&lt;/p&gt;

&lt;p&gt;The combination of MCP's standardized protocol and Azure's serverless platform means you can focus on building amazing AI experiences instead of managing infrastructure. Boom. 😎&lt;/p&gt;

&lt;p&gt;Star the repos ⭐️ if you found this helpful! Try deploying your own MCP server and share your experience in the comments. If you run into any issues or have questions, you can reach for help on the &lt;a href="https://aka.ms/foundry/discord" rel="noopener noreferrer"&gt;Azure AI community on Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
      <category>azure</category>
    </item>
    <item>
      <title>Serverless MCP Agent with LangChain.js v1 — Burgers, Tools, and Traces 🍔</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Tue, 21 Oct 2025 16:09:46 +0000</pubDate>
      <link>https://dev.to/azure/serverless-mcp-agent-with-langchainjs-v1-burgers-tools-and-traces-25oo</link>
      <guid>https://dev.to/azure/serverless-mcp-agent-with-langchainjs-v1-burgers-tools-and-traces-25oo</guid>
      <description>&lt;p&gt;AI agents that can actually do stuff (not just chat) are the fun part nowadays, but wiring them cleanly into real APIs, keeping things observable, and shipping them to the cloud can get... messy. So we built a fresh end‑to‑end sample to show how to do it right with the brand new &lt;strong&gt;LangChain.js v1&lt;/strong&gt; and &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;. In case you missed it, MCP is a recent open standard that makes it easy for LLM agents to consume tools and APIs, and LangChain.js, a great framework for building GenAI apps and agents, has first-class support for it.&lt;/p&gt;

&lt;p&gt;This new sample gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A LangChain.js v1 agent that streams its result, along reasoning + tool steps&lt;/li&gt;
&lt;li&gt;An MCP server exposing real tools (burger menu + ordering) from a business API&lt;/li&gt;
&lt;li&gt;A web interface with authentication, sessions history, and a debug panel (for developers)&lt;/li&gt;
&lt;li&gt;A production-ready multi-service architecture&lt;/li&gt;
&lt;li&gt;Serverless deployment on Azure in one command (&lt;code&gt;azd up&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyd3ybr83tyyogakr7ou.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyd3ybr83tyyogakr7ou.gif" alt="GIF animation of the agent in action" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, it’s a burger ordering system. Who doesn't like burgers? Grab your favorite beverage ☕, and let’s dive in for a quick tour!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;New sample: full-stack Node.js AI agent using LangChain.js v1 + MCP tools&lt;/li&gt;
&lt;li&gt;Architecture: web app → agent API → MCP server → burger API&lt;/li&gt;
&lt;li&gt;Runs locally with a single &lt;code&gt;npm start&lt;/code&gt;, deploys with &lt;code&gt;azd up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Uses streaming (NDJSON) with intermediate tool + LLM steps surfaced to the UI&lt;/li&gt;
&lt;li&gt;Ready to fork, extend, and plug into your own domain / tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What will you learn here?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What this sample is about and its high-level architecture&lt;/li&gt;
&lt;li&gt;What LangChain.js v1 brings to the table for agents&lt;/li&gt;
&lt;li&gt;How to deploy and run the sample&lt;/li&gt;
&lt;li&gt;How MCP tools can expose real-world APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reference links for everything we use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.langchain.com/oss/javascript/langchain/overview" rel="noopener noreferrer"&gt;LangChain.js docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@modelcontextprotocol/inspector" rel="noopener noreferrer"&gt;MCP Inspector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use case
&lt;/h2&gt;

&lt;p&gt;You want an AI assistant that can take a natural language request like “Order two spicy burgers and show me my pending orders” and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand intent (query menu, then place order)&lt;/li&gt;
&lt;li&gt;Call the right MCP tools in sequence, calling in turn the necessary APIs&lt;/li&gt;
&lt;li&gt;Stream progress (LLM tokens + tool steps)&lt;/li&gt;
&lt;li&gt;Return a clean final answer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Swap “burgers” for “inventory”, “bookings”, “support tickets”, or “IoT devices” and you’ve got a reusable pattern!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample overview
&lt;/h2&gt;

&lt;p&gt;Before we play a bit with the sample, let's have a look at the main services implemented here:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent Web App (&lt;code&gt;agent-webapp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Chat UI + streaming + session history&lt;/td&gt;
&lt;td&gt;Azure Static Web Apps, Lit web components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent API (&lt;code&gt;agent-api&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;LangChain.js v1 agent orchestration + auth + history&lt;/td&gt;
&lt;td&gt;Azure Functions, Node.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Burger MCP Server (&lt;code&gt;burger-mcp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Exposes burger API as tools over MCP (Streamable HTTP + SSE)&lt;/td&gt;
&lt;td&gt;Azure Functions, Express, MCP SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Burger API (&lt;code&gt;burger-api&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Business logic: burgers, toppings, orders lifecycle&lt;/td&gt;
&lt;td&gt;Azure Functions, Cosmos DB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's a simplified view of how they interact:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzs3kcdu4b537q2yx3hl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzs3kcdu4b537q2yx3hl.png" alt="Architecture Diagram" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are also other supporting components like databases and storage not shown here for clarity.&lt;/p&gt;

&lt;p&gt;For this quickstart we'll only interact with the &lt;strong&gt;Agent Web App&lt;/strong&gt; and the &lt;strong&gt;Burger MCP Server&lt;/strong&gt;, as they are the main stars of the show here.&lt;/p&gt;

&lt;h3&gt;
  
  
  LangChain.js v1 agent features
&lt;/h3&gt;

&lt;p&gt;The recent release of LangChain.js v1 is a huge milestone for the JavaScript AI community! It marks a significant shift from experimental tools to a production-ready framework. The new version doubles down on what’s needed to build robust AI applications, with a strong focus on &lt;strong&gt;agents&lt;/strong&gt;. This includes first-class support for streaming not just the final output, but also intermediate steps like tool calls and agent reasoning. This makes building transparent and interactive agent experiences (like the one in this sample) much more straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/signup" rel="noopener noreferrer"&gt;GitHub account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://azure.microsoft.com/free" rel="noopener noreferrer"&gt;Azure account&lt;/a&gt; (free signup, or if you're a student, &lt;a href="https://azure.microsoft.com/free/students" rel="noopener noreferrer"&gt;get free credits here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&amp;amp;pivots=os-windows" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploy and run the sample
&lt;/h3&gt;

&lt;p&gt;We'll use GitHub Codespaces for a quick zero-install setup here, but if you prefer to run it locally, check the &lt;a href="https://github.com/Azure-Samples/mcp-agent-langchainjs?tab=readme-ov-file#getting-started" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click on the following link or open it in a new tab to launch a Codespace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codespaces.new/Azure-Samples/mcp-agent-langchainjs?hide_repo_select=true&amp;amp;ref=main&amp;amp;quickstart=true" rel="noopener noreferrer"&gt;Create Codespace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will open a VS Code environment in your browser with the repo already cloned and all the tools installed and ready to go.&lt;/p&gt;

&lt;h4&gt;
  
  
  Provision and deploy to Azure
&lt;/h4&gt;

&lt;p&gt;Open a terminal and run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Login to Azure&lt;/span&gt;
azd auth login

&lt;span class="c"&gt;# Provision and deploy all resources&lt;/span&gt;
azd up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts to select your Azure subscription and region. If you're unsure of which one to pick, choose &lt;code&gt;East US 2&lt;/code&gt;.&lt;br&gt;
The deployment will take about 15 minutes the first time, to create all the necessary resources (Functions, Static Web Apps, Cosmos DB, AI Models).&lt;/p&gt;

&lt;p&gt;If you're curious about what happens under the hood, you can take a look at the &lt;code&gt;main.bicep&lt;/code&gt; file in the &lt;code&gt;infra&lt;/code&gt; folder, which defines the infrastructure as code for this sample.&lt;/p&gt;
&lt;h3&gt;
  
  
  Test the MCP server
&lt;/h3&gt;

&lt;p&gt;While the deployment is running, you can run the MCP server and API locally (even in Codespaces) to see how it works. Open another terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start all services locally, including the Burger API and the MCP server, which will be available at &lt;code&gt;http://localhost:3000/mcp&lt;/code&gt;. This may take a few seconds, wait until you see this message in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 All services ready 🚀
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When these services are running without Azure resources provisioned, they will use in-memory data instead of Cosmos DB so you can experiment freely with the API and MCP server, though the agent won't be functional as it requires a LLM resource.&lt;/p&gt;

&lt;h4&gt;
  
  
  MCP tools
&lt;/h4&gt;

&lt;p&gt;The MCP server exposes the following tools, which the agent can use to interact with the burger ordering system:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_burgers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a list of all burgers in the menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_burger_by_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a specific burger by its ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_toppings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a list of all toppings in the menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_topping_by_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a specific topping by its ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_topping_categories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a list of all topping categories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a list of all orders in the system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_order_by_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get a specific order by its ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;place_order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Place a new order with burgers (requires &lt;code&gt;userId&lt;/code&gt;, optional &lt;code&gt;nickname&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delete_order_by_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel an order if it has not yet been started (status must be &lt;code&gt;pending&lt;/code&gt;, requires &lt;code&gt;userId&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can test these tools using the MCP Inspector. Open another terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; @modelcontextprotocol/inspector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the URL printed in the terminal in your browser and connect using these settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transport&lt;/strong&gt;: Streamable HTTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href="http://localhost:3000/mcp" rel="noopener noreferrer"&gt;http://localhost:3000/mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection Type&lt;/strong&gt;: Via Proxy (should be default)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click on &lt;strong&gt;Connect&lt;/strong&gt;, then try listing the tools first, and run &lt;code&gt;get_burgers&lt;/code&gt; tool to get the menu info.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1x7jghzwa5rvdi32lfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1x7jghzwa5rvdi32lfa.png" alt="MCP Inspector Screenshot" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test the Agent Web App
&lt;/h3&gt;

&lt;p&gt;After the deployment is completed, you can run the command &lt;code&gt;npm run env&lt;/code&gt; to print the URLs of the deployed services. Open the Agent Web App URL in your browser (it should look like &lt;code&gt;https://&amp;lt;your-web-app&amp;gt;.azurestaticapps.net&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You'll first be greeted by an authentication page, you can sign in either with your GitHub or Microsoft account and then you should be able to access the chat interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3hhz6xv0g1djbendzrd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3hhz6xv0g1djbendzrd.png" alt="Agent chat interface screenshot" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, you can start asking any question or use one of the suggested prompts, for example try asking: &lt;code&gt;Recommend me an extra spicy burger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As the agent processes your request, you'll see the response streaming in real-time, along with the intermediate steps and tool calls. Once the response is complete, you can also unfold the debug panel to see the full reasoning chain and the tools that were invoked:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrk3nr22ypq4jm0qfaum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjrk3nr22ypq4jm0qfaum.png" alt="Intermediate steps debug panel screenshot" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Our agent service also sends detailed tracing data using OpenTelemetry. You can explore these either in Azure Monitor for the deployed service, or locally using an OpenTelemetry collector. We'll cover this in more detail in a future post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrap it up
&lt;/h2&gt;

&lt;p&gt;Congratulations, you just finished spinning up a full-stack serverless AI agent using LangChain.js v1, MCP tools, and Azure’s serverless platform. Now it's your turn to dive in the code and extend it for your use cases! 😎 And don't forget to &lt;code&gt;azd down&lt;/code&gt; once you're done to avoid any unwanted costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;This was just a quick introduction to this sample, and you can expect more in-depth posts and tutorials soon.&lt;/p&gt;

&lt;p&gt;Since we're in the era of AI agents, we've also made sure that this sample can be explored and extended easily with code agents like GitHub Copilot.&lt;br&gt;
We even built a custom chat mode to help you discover and understand the codebase faster! Check out the &lt;a href=".https://github.com/Azure-Samples/mcp-agent-langchainjs/blob/main/docs/copilot.md"&gt;Copilot setup guide&lt;/a&gt; in the repo to get started.&lt;/p&gt;

&lt;p&gt;If you like this sample, don't forget to star the repo ⭐️! You can also join us in the &lt;a href="https://aka.ms/foundry/discord" rel="noopener noreferrer"&gt;Azure AI community Discord&lt;/a&gt; to chat and ask any questions.&lt;/p&gt;

&lt;p&gt;Happy coding and burger ordering! 🍔&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>langchain</category>
      <category>azure</category>
    </item>
    <item>
      <title>Using DeepSeek-R1 on Azure with JavaScript</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Thu, 13 Feb 2025 13:20:00 +0000</pubDate>
      <link>https://dev.to/azure/using-deepseek-r1-on-azure-with-javascript-467i</link>
      <guid>https://dev.to/azure/using-deepseek-r1-on-azure-with-javascript-467i</guid>
      <description>&lt;h1&gt;
  
  
  Using DeepSeek-R1 on Azure with JavaScript
&lt;/h1&gt;

&lt;p&gt;The pace at which innovative AI models are being developed is outstanding! DeepSeek-R1 is one such model that focuses on complex reasoning tasks, providing a powerful tool for developers to build intelligent applications.&lt;/p&gt;

&lt;p&gt;We recently announced its availability on &lt;a href="https://github.blog/changelog/2025-01-29-deepseek-r1-is-now-available-in-github-models-public-preview/" rel="noopener noreferrer"&gt;GitHub Models&lt;/a&gt;&lt;br&gt;
as well as on &lt;a href="https://azure.microsoft.com/en-us/blog/deepseek-r1-is-now-available-on-azure-ai-foundry-and-github/" rel="noopener noreferrer"&gt;Azure AI Foundry&lt;/a&gt;. In this article, we’ll take a look at how you can deploy and use the DeepSeek-R1 models in your JavaScript applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek-R1 models focus on complex reasoning tasks, and is not designed for general conversation&lt;/li&gt;
&lt;li&gt;You can quickly switch your configuration to use Azure AI, GitHub Models, or even local models with Ollama.&lt;/li&gt;
&lt;li&gt;You can use &lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;OpenAI Node SDK&lt;/a&gt; or &lt;a href="https://js.langchain.com" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt; to interact with DeepSeek models.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What you'll learn here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deploying DeepSeek-R1 model on Azure.&lt;/li&gt;
&lt;li&gt;Switching between Azure, GitHub Models, or local (Ollama) usage.&lt;/li&gt;
&lt;li&gt;Code patterns to start using DeepSeek-R1 with various libraries in TypeScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Reference links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Azure-Samples/deepseek-azure-javascript" rel="noopener noreferrer"&gt;DeepSeek on Azure - JavaScript demos repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.azure.com/" rel="noopener noreferrer"&gt;Azure AI Foundry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/openai" rel="noopener noreferrer"&gt;OpenAI Node SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://js.langchain.com" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub account&lt;/strong&gt;. If you don't have one, you can &lt;a href="https://github.com/signup" rel="noopener noreferrer"&gt;create a free GitHub account&lt;/a&gt;. You can optionally use &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot Free&lt;/a&gt; to help you write code and ship your application even faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure account&lt;/strong&gt;. If you're new to Azure, &lt;a href="https://azure.microsoft.com/free" rel="noopener noreferrer"&gt;get an Azure account for free&lt;/a&gt; to get free Azure credits to get started. If you're a student, you can also get free credits with &lt;a href="https://aka.ms/azureforstudents" rel="noopener noreferrer"&gt;Azure for Students&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;We'll use GitHub Codespaces to get started quickly, as it provides a preconfigured Node.js environment for you. Alternatively, you can set up a local environment using the instructions found &lt;a href="https://github.com/Azure-Samples/deepseek-azure-javascript#use-your-local-environment" rel="noopener noreferrer"&gt;in the GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click on the button below to open our sample repository in a web-based VS Code, directly in your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codespaces.new/Azure-Samples/deepseek-azure-javascript?hide_repo_select=true&amp;amp;ref&amp;amp;quickstart=true" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fstatic%2Fv1%3Fstyle%3Dfor-the-badge%26label%3DGitHub%2BCodespaces%26message%3DOpen%26color%3Dblue%26logo%3Dgithub" alt="Open in GitHub Codespaces" width="234" height="28"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the project is open, wait a bit to ensure everything has loaded correctly. Open a terminal and run the following command to install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the samples
&lt;/h2&gt;

&lt;p&gt;The repository contains several TypeScript files under the &lt;code&gt;samples&lt;/code&gt; directory that demonstrate how to interact with DeepSeek-R1 models. You can run a sample using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx tsx samples/&amp;lt;sample&amp;gt;.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, let's start with the first one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx samples/01-chat.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a bit, and you should see the response from the model in your terminal.&lt;br&gt;
You'll notice that it may take longer than usual to respond, and see a weird response that starts with a &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; tag. This is because DeepSeek-R1 is designed to be used for task that need complex reasoning, like solving problems or answering math questions, and not for you usual chat interactions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Model configuration
&lt;/h2&gt;

&lt;p&gt;By default, the repository is configured to use GitHub Models, so you can run any example using Codespaces without any additional setup. While it's great for quick experimentation, GitHub models limit the number of requests you can make in a day and the amount of data you can send in a single request. If you want to use the model more extensively, you can switch to Azure AI or even use a local model with Ollama. You can take a look at the &lt;code&gt;samples/config.ts&lt;/code&gt; to see how the different configurations are set up.&lt;/p&gt;

&lt;p&gt;We'll not cover using Ollama models in this article, but you can find more information in the &lt;a href="https://github.com/Azure-Samples/deepseek-azure-javascript#using-ollama" rel="noopener noreferrer"&gt;repository documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying DeepSeek-R1 on Azure
&lt;/h2&gt;

&lt;p&gt;To experiment with the full capabilities of DeepSeek-R1, you can deploy it on Azure AI Foundry. Azure AI Foundry is a platform that allows you to deploy, manage and develop with AI models quickly. To use Azure AI Foundry, you need to have an &lt;a href="https://azure.com/free" rel="noopener noreferrer"&gt;Azure account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's start by deploying the model on Azure AI Foundry. First, follow this &lt;a href="https://learn.microsoft.com/azure/ai-studio/how-to/deploy-models-serverless?tabs=azure-ai-studio" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; to deploy a serverless endpoint with the model. When it's time to choose the model, make sure to select the &lt;code&gt;DeepSeek-R1&lt;/code&gt; model in the catalog.&lt;/p&gt;

&lt;p&gt;Once your endpoint is deployed, you should be able to see your endpoint details and retrieve the URL and API key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pcvvltvoftdt5ox8bhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5pcvvltvoftdt5ox8bhh.png" alt="Screenshot showing the endpoint details in Azure AI Foundry" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then create a &lt;code&gt;.env&lt;/code&gt; file in the root of the project and add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AZURE_AI_BASE_URL="https://&amp;lt;your-deployment-name&amp;gt;.&amp;lt;region&amp;gt;.models.ai.azure.com/v1"
AZURE_AI_API_KEY="&amp;lt;your-api-key&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: if you're copying the endpoint from the Azure AI Foundry portal, make sure to add the &lt;code&gt;/v1&lt;/code&gt; at the end of the URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open the &lt;code&gt;samples/config.ts&lt;/code&gt; file and update the default export to use Azure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;AZURE_AI_CONFIG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all samples will use the Azure configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore reasoning with DeepSeek-R1
&lt;/h2&gt;

&lt;p&gt;Now that you have the model deployed, you can start experimenting with it. Open the &lt;code&gt;samples/08-reasoning.ts&lt;/code&gt; file to see how the model handles more complex tasks, like helping us understand a well-known weird piece of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
float fast_inv_sqrt(float number) {
  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i  = *(long*)&amp;amp;y;
  i  = 0x5f3759df - ( i &amp;gt;&amp;gt; 1 );
  y  = *(float*)&amp;amp;i;
  y  = y * ( threehalfs - ( x2 * y * y ) );

  return y;
}

What is this code doing? Explain me the magic behind it.
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run this sample with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx samples/08-reasoning.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the model's response streaming piece by piece in the terminal, while describing its thought process before providing the actual answer to our question.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhe0nv7cbv15jbzvwmyr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhe0nv7cbv15jbzvwmyr.png" alt="Screenshot showing the model's response streaming in the terminal" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Brace yourself, as it might take a while to get the full response! At the end of the process, you should see the model's detailed explanation of the code, along with some context around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging frameworks
&lt;/h2&gt;

&lt;p&gt;Most examples in this repository are built with the OpenAI Node SDK, but you can also use LangChain.js to interact with the model. This might be especially interested if you need to integrate other sources of data or want to build a more complex application.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;samples/07-langchain.ts&lt;/code&gt; to have a look at the setup, and see how you can reuse the same configuration we used with the OpenAI SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;Now it's your turn to experiment and discover the full potential of DeepSeek-R1! You can try more advanced prompts, integrate it into your larger application, or even build agents to make the most out of the model. &lt;/p&gt;

&lt;p&gt;To continue your learning journey, you can check out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure-Samples/deepseek-js" rel="noopener noreferrer"&gt;DeepSeek-R1 JavaScript starter&lt;/a&gt; (GitHub): a complete starter project including to use DeepSeek-R1 models in JavaScript, including infrastructure as code for secure deployment on Azure.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/generative-ai-with-javascript" rel="noopener noreferrer"&gt;Generative AI with JavaScript&lt;/a&gt; (GitHub): code samples and resources to learn Generative AI with JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techcommunity.microsoft.com/t5/apps-on-azure-blog/build-a-serverless-ai-chat-with-rag-using-langchain-js/ba-p/4111041" rel="noopener noreferrer"&gt;Build a serverless AI chat with RAG using LangChain.js&lt;/a&gt; (GitHub): a next step code example to build an AI chatbot using Retrieval-Augmented Generation and LangChain.js.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
    <item>
      <title>Generative AI with JavaScript FREE course</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Wed, 16 Oct 2024 08:58:50 +0000</pubDate>
      <link>https://dev.to/azure/generative-ai-with-javascript-free-course-g04</link>
      <guid>https://dev.to/azure/generative-ai-with-javascript-free-course-g04</guid>
      <description>&lt;p&gt;Generative AI has come a long way in just two years, revolutionizing industries and unlocking new possibilities for developers across the globe. What initially started as a niche field primarily dominated by research and Python-based machine learning has quickly become a mainstream technology that’s reshaping how we approach creativity, automation, and interaction. But what’s even more exciting is how accessible this technology has become for developers from all backgrounds—especially JavaScript developers! 🎉&lt;/p&gt;

&lt;p&gt;Today, generative AI isn’t just about building complex models in Python. &lt;strong&gt;The real magic happens when these models are integrated into applications that users interact with every day&lt;/strong&gt;, and that’s where JavaScript comes in. As the web’s most widely used programming language, JavaScript plays a pivotal role in how developers build and scale generative AI-powered apps for the browser, mobile, and even server-side environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Generative AI with JavaScript
&lt;/h2&gt;

&lt;p&gt;We built our new content series, &lt;strong&gt;Generative AI with JavaScript&lt;/strong&gt;, to help you harness this power. Whether you’re a web developer curious about AI or an experienced coder looking to leverage generative AI in your projects, this series will walk you through everything you need to know: from connecting to APIs and working with large language models (LLMs), to building sophisticated, AI-driven applications using popular tools like LangChain.js and Azure.&lt;/p&gt;

&lt;p&gt;You can access the full series for free on our &lt;a href="https://aka.ms/genai-js" rel="noopener noreferrer"&gt;YouTube playlist&lt;/a&gt;, where you’ll find videos around 10 minutes long, each focusing on a specific topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://aka.ms/genai-js" rel="noopener noreferrer"&gt;📺 Generative AI with JavaScript&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GQ_2OjNZ9aA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We've made each video self-contained, so you can watch them in any order that suits your learning style, though we chose and ordered them to provide a smooth learning curve if you're new to generative AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn, share &amp;amp; teach
&lt;/h2&gt;

&lt;p&gt;The videos are &lt;em&gt;only one part&lt;/em&gt; of the learning experience! 😉&lt;/p&gt;

&lt;p&gt;We tried to pair each topic with interactive demos, code samples, and additional resources to help you dive deeper into the subject. You can find all these resources in the video descriptions but we also gathered everything in a dedicated GitHub repository: &lt;a href="https://github.com/microsoft/generative-ai-with-javascript" rel="noopener noreferrer"&gt;https://github.com/microsoft/generative-ai-with-javascript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this repo, you'll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📚 &lt;strong&gt;Links to all the videos&lt;/strong&gt; in the series&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Slides and transcripts&lt;/strong&gt; for all sessions, to use as foundation for your own talks&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Demo and code samples&lt;/strong&gt; to get hands-on experience&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Project examples and starter kits&lt;/strong&gt; to help you get use Gen AI in your projects&lt;/li&gt;
&lt;li&gt;🌟 &lt;strong&gt;Additional resources&lt;/strong&gt; to learn more about generative AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wanted to make sure you have everything you need to learn the concepts, share your knowledge with others, and even teach your own workshops or courses. Feel free to reuse any of the content in your own presentations, blog posts, or videos!&lt;/p&gt;

&lt;h2&gt;
  
  
  Want more?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsgtqo2vg1dclq5mttk0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgsgtqo2vg1dclq5mttk0.jpg" alt="funny folks shouting " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if I tell you this is just the beginning? 😏&lt;/p&gt;

&lt;p&gt;We have plently more content coming, this is just the first part of the series. We have more advanced topics, in-depth tutorials and even more use cases and tools to help you master generative AI with JavaScript. Stay tuned for more updates and new videos!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to keep the new content coming, don't forget to star the repo, like the videos, subscribe to the channel, yada yada... and share the series with your friends and colleagues! The more we see you're interested, the more we'll be able to create new content for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tell us your feedback!
&lt;/h2&gt;

&lt;p&gt;We're here to help you learn and grow, so we'd love to hear your feedback on the series. What did you like? What could be improved? What topics would you like to see covered next? Let us know by &lt;a href="https://github.com/microsoft/generative-ai-with-javascript/issues" rel="noopener noreferrer"&gt;opening an issue in the GitHub repository&lt;/a&gt; or by commenting on the videos. We're always listening, and we love to hear back from you! 🙌&lt;/p&gt;

&lt;p&gt;You can also join the discussion on the &lt;a href="https://discordapp.com/channels/1113626258182504448/1237357005555892225" rel="noopener noreferrer"&gt;AI Community #JavaScript channel on Discord&lt;/a&gt; to chat with other developers, ask questions, and share your own projects.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;THANK YOU&lt;/em&gt; for taking this journey with us, I'm excited to see what you'll build!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://aka.ms/genai-js" rel="noopener noreferrer"&gt;📺 Watch the video series&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/microsoft/generative-ai-with-javascript" rel="noopener noreferrer"&gt;📦 Get the code samples and all the Gen AI + JavaScript content&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Generative AI, from your local machine to Azure with LangChain.js</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Tue, 18 Jun 2024 14:58:00 +0000</pubDate>
      <link>https://dev.to/azure/generative-ai-from-your-local-machine-to-azure-with-langchainjs-5288</link>
      <guid>https://dev.to/azure/generative-ai-from-your-local-machine-to-azure-with-langchainjs-5288</guid>
      <description>&lt;p&gt;The generative AI landscape moves at a fast pace, and it can be challenging to keep up with the latest developments, even for seasoned developers. There are in particular two questions that often come up when starting a new AI project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How can I quickly validate an AI app idea, without investing too much time and resources?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;If I have a working prototype, how fast can I scale it to production?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't want to be outpaced by competitors or newer technologies, and you want to be able to quickly iterate on your ideas or pivot to new ones. This is where LangChain.js comes in. It's a framework that allows you to build AI applications very little adherence to the underlying AI technologies and tools. It abstracts some of the complexity of AI development, allowing you to focus on the business logic of your application.&lt;/p&gt;

&lt;p&gt;In this article, we'll take you through the development journey, starting from an idea and progressing towards production. We'll explore how LangChain framework together with Azure AI building blocks allows you to quickly build complex AI applications at the various stages of development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you prefer to watch a video version of this article, you can find it &lt;a href="https://www.youtube.com/watch?v=L4T4_Z1kyao" rel="noopener noreferrer"&gt;on YouTube here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI is not only reserved to Python developers: JavaScript developers also have everything they need to build AI applications.&lt;/li&gt;
&lt;li&gt;LangChain.js provides useful abstraction over AI models and APIs, allowing you to switch between them easily. This is particularly useful when you're experimenting with different models or when you want to scale your application, moving from a local SLM model to a cloud-based LLM.&lt;/li&gt;
&lt;li&gt;Ollama is allow you to experiment with AI models and embeddings locally, at no cost (if you have a powerful enough machine).&lt;/li&gt;
&lt;li&gt;Azure provides many AI building blocks and services that you can use to scale your application to production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs" rel="noopener noreferrer"&gt;source code on GitHub&lt;/a&gt; of the project we use as an example in this article.&lt;/p&gt;

&lt;p&gt;If you like the project, don't forget to give it a star ⭐️!&lt;/p&gt;

&lt;h2&gt;
  
  
  Working locally with Ollama
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; is a command-line tool that allows you to run AI models locally on your machine, making it great for prototyping. Running 7B/8B models on your machine requires at least 8GB of RAM, but works best with 16GB or more. You can install Ollama on Windows, macOS, and Linux from the official website: &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;https://ollama.com/download&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have Ollama installed, let's first download some models. You can find a list of available models on the &lt;a href="https://ollama.com/models" rel="noopener noreferrer"&gt;Ollama website&lt;/a&gt;. For this example, we'll use the &lt;a href="https://ollama.com/library/phi3:mini" rel="noopener noreferrer"&gt;Phi-3 Mini&lt;/a&gt; model. Open a terminal and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull phi3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This will download a few gigabytes of data, so make sure you have enough space on your machine and a good internet connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the model is downloaded, you can start interacting with the Ollama server. For example, you can use the &lt;code&gt;ollama run&lt;/code&gt; command to generate text based on a prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run phi3 &lt;span class="s2"&gt;"What is artificial intelligence? Explain it to a 5 years old child."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also have a minimal ChatGPT-like experience right from you terminal by just running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run phi3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwew42ku1bpqaxwye4hw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwew42ku1bpqaxwye4hw4.png" alt="Ollama run chat example" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then chat with the model interactively. Once you're done, you can stop the server by pressing &lt;code&gt;Ctrl+D&lt;/code&gt;.&lt;br&gt;
Ollama also provides a REST API that you can use to interact with the model. The API provides &lt;a href="https://github.com/ollama/ollama/blob/main/docs/api.md" rel="noopener noreferrer"&gt;many options&lt;/a&gt;, like streaming, JSON mode, and more. Here's an example of how you can use the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:11434/api/generate &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "model": "phi3",
  "prompt": "What is artificial intelligence? Explain it to a 5 years old child.",
  "stream": false
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this command you should see a JSON response from the model&lt;br&gt;
Ollama even provides an OpenAI compatible API, so you can use it as drop-in replacement for OpenAI models in your applications. And as Ollama runs entirely on your machine, it means you don't even need a network connection to use it.&lt;/p&gt;

&lt;p&gt;While Ollama is great for experimentation and prototyping, keep in mind that smaller models are not as powerful as the larger models available in the cloud. While it might be enough to validate your idea, you'll probably want to switch to cloud-based models for production to get better results.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prototyping with LangChain.js
&lt;/h2&gt;

&lt;p&gt;Now that we know how to run AI models locally, let's see how we can use LangChain.js to quickly prototype an AI application. &lt;a href="https://js.langchain.com/" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt; is a JavaScript framework that provides a high-level API to interact with AI models and APIs with many built-in tools to make complex AI applications easier to build.&lt;/p&gt;

&lt;p&gt;Let's start with a simple example project from scratch. Open a terminal and run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Creates a new folder and initializes a new Node.js project&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;langchain-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;langchain-demo
npm init es6 &lt;span class="nt"&gt;-y&lt;/span&gt;
npm i langchain @langchain/core @langchain/community pdf-parse faiss-node
&lt;span class="nb"&gt;touch &lt;/span&gt;index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the &lt;code&gt;index.js&lt;/code&gt; file in your favorite code editor and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SystemMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/core/messages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatOllama&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/community/chat_models/ollama&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phi3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You're a helpful assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Say hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the code with &lt;code&gt;node index.js&lt;/code&gt;. You should see the response from the model in the console. Congrats, you've just built the hello world of AI chatbots!&lt;/p&gt;

&lt;h3&gt;
  
  
  A more complex example
&lt;/h3&gt;

&lt;p&gt;What if I want to use &lt;a href="https://aka.ms/ws?src=gh%3Aazure-samples%2Fazure-openai-rag-workshop%2Fbase%2Fdocs%2F&amp;amp;step=1#what-is-retrievial-augmented-generation" rel="noopener noreferrer"&gt;RAG (Retrieval-Augmented Generation)&lt;/a&gt; to ground the answers using documents? Let's update our &lt;code&gt;index.js&lt;/code&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RecursiveCharacterTextSplitter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;langchain/text_splitter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createStuffDocumentsChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;langchain/chains/combine_documents&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRetrievalChain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;langchain/chains/retrieval&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatPromptTemplate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/core/prompts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ChatOllama&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/community/chat_models/ollama&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OllamaEmbeddings&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/community/embeddings/ollama&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FaissStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/community/vectorstores/faiss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFLoader&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@langchain/community/document_loaders/fs/pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Initialize the models&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phi3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OllamaEmbeddings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all-minilm:l6-v2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Load PDF document and split it into smaller chunks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDFLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terms-of-service.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;splitPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;chunkOverlap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfDocument&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Put the documents into a vector store and convert them to vectors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;FaissStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Create the RAG chain that retrieves and combines the prompt with the documents&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;combineDocsChain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createStuffDocumentsChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromMessages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You're a helpful assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Answer the question: {input}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;using the following documents:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;{context}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createRetrievalChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asRetriever&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;combineDocsChain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 5. Generate the result&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What's our mission?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will load a PDF document, split it into smaller chunks, convert them to vectors, and then use them in a multi-step workflow (chain) to perform a vector search and generate a response using the best results. Pheeew! This one is a more complex example, but it shows how LangChain.js can help you build more advanced AI scenarios in a few lines of code.&lt;/p&gt;

&lt;p&gt;Before running this code, you first need to download &lt;a href="https://raw.githubusercontent.com/Azure-Samples/serverless-chat-langchainjs/main/data/terms-of-service.pdf" rel="noopener noreferrer"&gt;this PDF document&lt;/a&gt; and put it in the &lt;code&gt;langchain-demo&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;We also need to download the embeddings model. You can do this by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull all-minilm:l6-v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one is very small (~50MB), and helps converting text to vectors. Now you can run your code with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv42cbi8kaa2ke2g8uzv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv42cbi8kaa2ke2g8uzv.png" alt="LangChain.js RAG example results" width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resulting answer directly comes from the PDF document, you can open it and look at the original mission statement at the beginning of the document to see how the model used it in its response.&lt;/p&gt;

&lt;p&gt;Using the same principles as this example, we've prototyped a chatbot for the &lt;em&gt;Contoso Real Estate company&lt;/em&gt;: we’ve built an experience that allows customers to ask support questions about the usage of its products. You can find the full source code of the project on &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The final results, with an added chat UI, look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrpmjyuf9itqgtfw3xyb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrpmjyuf9itqgtfw3xyb.gif" alt="Contoso Real Estate chatbot" width="952" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have a working prototype, let's see how we can deploy it to production using Azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating to Azure
&lt;/h2&gt;

&lt;p&gt;Azure provides many AI services that you can use for your applications, in our case we'll use &lt;a href="https://azure.microsoft.com/products/ai-services/openai-service" rel="noopener noreferrer"&gt;Azure OpenAI&lt;/a&gt; for the models and &lt;a href="https://azure.microsoft.com/products/ai-services/ai-search" rel="noopener noreferrer"&gt;Azure AI Search&lt;/a&gt; as our vector database. Thanks to LangChain.js abstraction, migrating your prototype to Azure for production is relatively straightforward, as you can swap the models and vector database without changing anything else in your code.&lt;/p&gt;

&lt;p&gt;If you look at &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/blob/main/packages/api/src/functions/chat-post.ts#L54-L75" rel="noopener noreferrer"&gt;the chat API code&lt;/a&gt;, this is what we use to run the code locally with Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OllamaEmbeddings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ollamaEmbeddingsModel&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ollamaChatModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;FaissStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faissStoreFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching to Azure OpenAI and Azure AI Search is as simple as changing the model and store initialization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureOpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureAISearchVectorStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use passworless authentication for increased security, so we don't need to store any secrets in our code. The implementation of the &lt;code&gt;getCredentials&lt;/code&gt; was omitted for simplicity, but you can &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/blob/main/packages/api/src/security.ts" rel="noopener noreferrer"&gt;find it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And that's it for the migration, at least for the code part, as to make it work you still have to create the necessary resources in Azure. We'll cover this in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure Developer CLI
&lt;/h3&gt;

&lt;p&gt;As developers, we know infrastructure is no fun, but it's a necessary part of deploying applications to the cloud. Azure provides a tool called &lt;a href="https://aka.ms/azure-dev/install" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt; that makes it easier to create and manage resources in Azure. It allows you to use &lt;a href="https://learn.microsoft.com/azure/cloud-adoption-framework/ready/considerations/infrastructure-as-code" rel="noopener noreferrer"&gt;Infrastructure as Code&lt;/a&gt; to define your resources in a declarative way, and then deploy them with a single command.&lt;/p&gt;

&lt;p&gt;We won't cover in details how to build the infrastructure templates, if you're curious though you can have a look at the &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/tree/main/infra" rel="noopener noreferrer"&gt;&lt;code&gt;infra&lt;/code&gt; folder&lt;/a&gt; and the &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/blob/main/azure.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;azure.yaml&lt;/code&gt; file&lt;/a&gt; used to configure and deploy the resources.&lt;/p&gt;

&lt;p&gt;The good news is that we have many samples that you can use as a starting point for your own project infrastructure. Once the configuration is done, you can create the resources and deploy the application with a few commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Authenticate to Azure&lt;/span&gt;
azd auth login
&lt;span class="c"&gt;# Provision and deploy the resources&lt;/span&gt;
azd up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try it with our &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs" rel="noopener noreferrer"&gt;Serverless AI Chat project&lt;/a&gt; we used as an example in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure building blocks
&lt;/h2&gt;

&lt;p&gt;We skipped a bit over the implementation details of our example project, but to build it quickly we used some of the already existing Azure AI building blocks. Here's a list of some components we used, that you can reuse in your own projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenAI Node.js SDK&lt;/strong&gt;: we announced at Build a new integration of Azure OpenAI with the official &lt;a href="https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai" rel="noopener noreferrer"&gt;OpenAI Node.js SDK&lt;/a&gt;, meaning it's now easier than ever to switch between OpenAI and Azure OpenAI models. The &lt;a href="https://js.langchain.com/docs/integrations/chat/azure/" rel="noopener noreferrer"&gt;LangChain.js Azure OpenAI integration&lt;/a&gt; has also been updated to use this new SDK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Azure integrations in LangChain.js&lt;/strong&gt;: we've contributed support for many Azure services in LangChain.js, to make it easier to build your AI applications on top of Azure. This includes Azure OpenAI, Azure AI Search, Azure CosmosDB, and more. You can find more information in the &lt;a href="https://js.langchain.com/docs/integrations/platforms/microsoft" rel="noopener noreferrer"&gt;LangChain.js documentation&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI Chat protocol&lt;/strong&gt;: we've defined an &lt;a href="https://github.com/microsoft/ai-chat-protocol/tree/main/spec#readme" rel="noopener noreferrer"&gt;API schema&lt;/a&gt; for AI chat applications, to make the frontend and backend components communicate. This schema is implemented in many of our AI samples, making them interoperable and easy to extend. We also provide an &lt;a href="https://www.npmjs.com/package/@microsoft/ai-chat-protocol" rel="noopener noreferrer"&gt;NPM package&lt;/a&gt; that includes the TypeScript types for the data objects and a client library to interact with the API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI Chat UI components&lt;/strong&gt;: if you want to focus on the backend part of your AI chat application, we provide &lt;a href="https://github.com/Azure-Samples/azure-openai-chat-frontend" rel="noopener noreferrer"&gt;a set of web components&lt;/a&gt; that implements the AI Chat protocol. You can use them to quickly build a chat UI for your application. And since most of our AI samples also implement the protocol, you can also reuse any of their frontend component if you prefer, like the &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/tree/main/packages/webapp" rel="noopener noreferrer"&gt;one we used in our example project&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've covered a lot of ground in this article, starting from running AI models locally with Ollama, to prototyping a chatbot with LangChain.js, and finally deploying it to production on Azure. In a fast-paced environment like AI development, using JavaScript with existing high-level frameworks like LangChain.js and building on top of off-the-shelf building blocks can help you iterate quickly on your ideas and eventually bring them to production.&lt;br&gt;
 you can quickly iterate on your ideas and bring them to production with the right tools and services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference links
&lt;/h3&gt;

&lt;p&gt;Here are some useful links to get you started with the tools and services we've mentioned in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://js.langchain.com" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/azure-dev/install" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai" rel="noopener noreferrer"&gt;Azure OpenAI Node.js SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/ai-chat-protocol/tree/main/spec#readme" rel="noopener noreferrer"&gt;AI Chat protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Azure-Samples/azure-openai-chat-frontend" rel="noopener noreferrer"&gt;AI Chat UI components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/" rel="noopener noreferrer"&gt;Serverless AI Chat sample&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
    <item>
      <title>Build a serverless AI Chat with RAG using LangChain.js</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Wed, 10 Apr 2024 14:55:38 +0000</pubDate>
      <link>https://dev.to/azure/build-a-serverless-chatgpt-with-rag-using-langchainjs-3487</link>
      <guid>https://dev.to/azure/build-a-serverless-chatgpt-with-rag-using-langchainjs-3487</guid>
      <description>&lt;p&gt;We're still at the beginning of exploring what we can do with generative AI, and the field is moving fast, very fast!&lt;/p&gt;

&lt;p&gt;And when things move fast, you need to be able to experiment and iterate quickly. First to validate your ideas with a prototype, then to scale up to production if it works.&lt;/p&gt;

&lt;p&gt;In this article, we'll show you how LangChain.js, Ollama with Mistral 7B model and Azure can be used together to build a serverless chatbot that can answer questions using a RAG (Retrieval-Augmented Generation) pipeline. We'll see first how you can work fully locally to develop and test your chatbot, and then deploy it to the cloud with state-of-the-art OpenAI models.&lt;/p&gt;

&lt;p&gt;Here's a preview of the final project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbypne7xil1m8jz3iqvfv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbypne7xil1m8jz3iqvfv.gif" alt="Animation showing a demo of the chatbot interface" width="952" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grab your favorite beverage ☕, and let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;LangChain.js provides abstraction over AI models and APIs, allowing you to switch between them easily. Built-in support for advanced chains components makes complex AI workflows like RAG easy to build.&lt;/li&gt;
&lt;li&gt;Ollama is a powerful tool to experiment with AI models and embeddings locally.&lt;/li&gt;
&lt;li&gt;Azure Cosmos DB for MongoDB vCore can be used as a vector database for AI workloads, in addition to your regular NoSQL storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the final project &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs" rel="noopener noreferrer"&gt;source code on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you like the project, don't forget to give it a star ⭐️!&lt;/p&gt;

&lt;h2&gt;
  
  
  What will you learn here?
&lt;/h2&gt;

&lt;p&gt;In this article, we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install required tools and set up the project&lt;/li&gt;
&lt;li&gt;Use Ollama to experiment with the Mistral 7B model on your local machine&lt;/li&gt;
&lt;li&gt;Run the project locally to test the chatbot&lt;/li&gt;
&lt;li&gt;Explain the RAG pipeline and how it can be used to build a chatbot&lt;/li&gt;
&lt;li&gt;Walk through LangChain.js building blocks to ingest the data and generate answers&lt;/li&gt;
&lt;li&gt;Deploy the chatbot to Azure Functions, using Azure Cosmos DB for MongoDB as a vector database (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, we have a lot to cover so feel free to take your time and experiment with the code as you go through the article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference links for everything we use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://js.langchain.com" rel="noopener noreferrer"&gt;LangChain.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/static-web-apps/" rel="noopener noreferrer"&gt;Azure Static Web Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/functions/" rel="noopener noreferrer"&gt;Azure Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/azure/cosmos-db/mongodb/vcore/" rel="noopener noreferrer"&gt;Azure Cosmos DB for MongoDB vCore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/azure-dev/install" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A working &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js v20+&lt;/a&gt; environment&lt;/li&gt;
&lt;li&gt;A machine with &lt;a href="https://github.com/ollama/ollama/blob/main/docs/gpu.md" rel="noopener noreferrer"&gt;a GPU supported by Ollama&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/join" rel="noopener noreferrer"&gt;A GitHub account&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;(Optional, for Azure deployment) An Azure account to create the resources and deploy the app. If you don't have an account, you can &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=javascript-0000-cxa" rel="noopener noreferrer"&gt;create one for free using this link&lt;/a&gt;. If you're a student, you can also get free credits with &lt;a href="https://aka.ms/azureforstudents" rel="noopener noreferrer"&gt;Azure for Students&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;First let's fork and clone the project repository on your machine. This will allow you to experiment with the code and make changes as you go through the article.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the following link, then select &lt;strong&gt;Create fork&lt;/strong&gt; button: &lt;a href="https://github.com/Azure-Samples/serverless-chat-langchainjs/fork" rel="noopener noreferrer"&gt;&lt;strong&gt;Fork on GitHub&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On your forked repository, select the &lt;strong&gt;Code&lt;/strong&gt; button, then the &lt;strong&gt;Local&lt;/strong&gt; tab, and copy the URL of your forked repository.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfdgo6vhl8z1k8minw3d.png" alt="Screenshot showing how to copy the repository URL" width="800" height="498"&gt;
&lt;/li&gt;
&lt;li&gt;Open a terminal and run this command to clone the repo: &lt;code&gt;git clone &amp;lt;your-repo-url&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a terminal, navigate to the project folder and install the dependencies with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's almost ready! Before running the app, we need first have to setup Ollama to have a local AI playground.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Ollama and local models
&lt;/h2&gt;

&lt;p&gt;Ollama is a CLI tool that allows you to experiment with AI models and embeddings locally. It's a great tool to test and validate your ideas before deploying them to the cloud.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;Ollama's website&lt;/a&gt; and download the latest version for your platform. Once installed, you can use the &lt;code&gt;ollama&lt;/code&gt; command in your terminal.&lt;/p&gt;

&lt;p&gt;We'll start by downloading the models we need for this project. Run the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mistral
ollama pull all-minilm:l6-v2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will pull the Mistral 7B model, a powerful language model that we'll use for the chatbot, and the All-MiniLM model, a small embedding model that we'll use to generate the vectors from the text.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;mistral&lt;/code&gt; model with download a few gigabytes of data, so it can take some time depending on your internet connection.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the models are downloaded, you can test that the Ollama server is working correctly by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run mistral
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get an invite in your terminal, where you can chat with the AI model directly, like a minimal chatbot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5v79vfyialg111umm3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5v79vfyialg111umm3d.png" alt="Screenshot of Ollama running the Mistral model" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try asking a few questions to the model, and see how it responds. This will give you a good idea of the capabilities of the model and how you can interact with it.&lt;/p&gt;

&lt;p&gt;Once you're done, you can stop the Ollama server by pressing &lt;code&gt;Ctrl+D&lt;/code&gt; in your terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the project locally
&lt;/h2&gt;

&lt;p&gt;Now that we have the models ready, we can run the project to test the chatbot. Open a terminal in the project root and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start a dev server for the frontend, and an Azure Functions runtime emulator for the backend. You can access and play with the chatbot at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The first time you run the project, it can take a few minutes to start the backend API as it will download the runtime dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should end up with a familiar chat interface, where you can ask questions and get answers from the chatbot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknjbf8ntopizepmtjigb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknjbf8ntopizepmtjigb.png" alt="Screenshot of the chatbot interface" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this demo, we use a fictitious company called &lt;em&gt;Contoso Real Estate&lt;/em&gt;, and the chatbot can answer support questions about the usage of its products. The sample data includes a set of documents that describes its terms of service, privacy policy and a support guide.&lt;/p&gt;

&lt;p&gt;You can try using the suggested questions, or ask your own questions to see how the chatbot responds. Try also asking something completely out of context, like "Who won the latest football world cup?" to see how the chatbot handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the RAG pipeline
&lt;/h2&gt;

&lt;p&gt;The chatbot we built uses a RAG (Retrieval-Augmented Generation) pipeline to answer questions. But what is RAG?&lt;/p&gt;

&lt;p&gt;RAG is a method used in artificial intelligence, particularly in natural language processing, to generate text responses that are both contextually relevant and rich in content using AI models.&lt;/p&gt;

&lt;p&gt;At its core, RAG involves two main components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Retriever&lt;/strong&gt;: Think "&lt;em&gt;like a search engine&lt;/em&gt;", finding relevant information from a knowledgebase, usually a vector database. In this sample, we're using Azure CosmosDB for MongoDB vCore as our vector database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generator&lt;/strong&gt;: Acts like a writer, taking the prompt and information retrieved to create a response. We're using here a Large Language Model (LLM) for this task.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3dx8urojb7wxdnox1jr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3dx8urojb7wxdnox1jr.png" alt="Diagram showing the RAG pipeline" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you ask a question to the chatbot, the RAG pipeline works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The question is sent to the retriever, which will find the most relevant documents in the knowledgebase.&lt;/li&gt;
&lt;li&gt;We used the top &lt;em&gt;N&lt;/em&gt; retrieved documents along with the question to generate a prompt for the generator.&lt;/li&gt;
&lt;li&gt;The generator (here the AI model) will then use the prompt to generate a response, which is sent back to the user.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Exploring the LangChain.js building blocks
&lt;/h2&gt;

&lt;p&gt;To build the RAG pipeline, we're using LangChain.js, a library that provides an abstraction layer over AI models but also comes with advanced chain components to build AI workflows, including RAG.&lt;/p&gt;

&lt;p&gt;We'll now dive into the code to understand the various parts of the pipeline, and the components we used to implement it.&lt;/p&gt;

&lt;p&gt;Our backend API is composed of two main endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/documents&lt;/code&gt;: This endpoint allows to upload a PDF documents in the database, performing text extraction and vectorization as part of the ingestion process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;/chat&lt;/code&gt;: This endpoint receives a list of messages, the last being the user query and returns a response generated by the AI model. It uses the documents stored in the database to generate the response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;/documents&lt;/code&gt; endpoint
&lt;/h3&gt;

&lt;p&gt;Here we use LangChain.js to extract the text from the PDF file, split it into smaller chunks, and generate vectors for each chunk. We store the text and the vectors in the database for later use in our RAG pipeline.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;packages/api/src/functions/documents-post.ts&lt;/code&gt; in your code editor.&lt;br&gt;
Let's analyse the main parts of &lt;code&gt;postDocuments()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get the uploaded file from the request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;parsedForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"file" field not found in form data.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsedForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by parsing the form data to get the uploaded PDF file name and content. We use the standard MIME type &lt;code&gt;multipart/form-data&lt;/code&gt; to handle file uploads here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Extract text from the PDF&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PDFLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;splitPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;rawDocument&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Split the text into smaller chunks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;chunkOverlap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splitDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawDocument&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we use LangChain.js components to perform the text extraction and splitting. We use the &lt;code&gt;PDFLoader&lt;/code&gt; to extract the text from the PDF file, and the &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt; to split the text into smaller chunks.&lt;/p&gt;

&lt;p&gt;Splitting the text into smaller chunks is important to improve the retrieval performance, as it allows the retriever to find more relevant information in the documents. It also helps with the AI models' limitations on the prompt input size, and ultimately reduces the usage cost when using hosted cloud models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generate embeddings and save in database&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;azureOpenAiEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AzureCosmosDBVectorStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureOpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createIndex&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If no environment variables are set, it means we are running locally&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Azure OpenAI endpoint set, using Ollama models and local DB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OllamaEmbeddings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ollamaEmbeddingsModel&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;FaissStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faissStoreFolder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now is the most important part: we generate the embeddings for each chunk of text and store them in the database. LangChain.js abstracts a lot of the complexity here, allowing us to switch between different embeddings models easily. Here we use the Azure OpenAI embeddings for the cloud deployment, and the Ollama embeddings for the local development. You can see that it's easy to switch between the two as LangChain.js provides a common interface for both.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Upload the PDF file to Azure Blob Storage&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Uploading file to blob storage: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blobServiceClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BlobServiceClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;containerClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blobServiceClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContainerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockBlobClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;containerClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBlockBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;blockBlobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;blobHTTPHeaders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;blobContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Azure Blob Storage connection string set, skipping upload.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we upload the original PDF file to Azure Blob Storage. This is useful to keep a copy of the documents, and also to provide a way to download them later if needed. But it's not mandatory for the chatbot to work, so we can skip it entirely if we're running locally.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;/chat&lt;/code&gt; endpoint
&lt;/h3&gt;

&lt;p&gt;In this endpoint we use LangChain.js components to connect to the database, load the documents and perform a vector search after vectorizing the user query. After that, the most relevant documents are injected into the prompt, and we generate the response. While this process seems complex, LangChain.js does all the heavy lifting for us.&lt;/p&gt;

&lt;p&gt;Open the file &lt;code&gt;packages/api/src/functions/chat-post.ts&lt;/code&gt; in your code editor.&lt;br&gt;
Let's skip to the most interesting part of the &lt;code&gt;postChat()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Embeddings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BaseChatModel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VectorStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;azureOpenAiEndpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Initialize models and vector database&lt;/span&gt;
  &lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureOpenAIEmbeddings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AzureCosmosDBVectorStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// If no environment variables are set, it means we are running locally&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No Azure OpenAI endpoint set, using Ollama models and local DB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OllamaEmbeddings&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ollamaEmbeddingsModel&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ollamaChatModel&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;FaissStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;faissStoreFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by initializing the AI models and the database. As you can see switching between the cloud and local models is straightforward, and the good news is that it's the only change needed!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create the chain that combines the prompt with the documents&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;combineDocsChain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createStuffDocumentsChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChatPromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromMessages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;human&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{input}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;documentPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{filename}: {page_content}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first part of our RAG chain is using the &lt;code&gt;createStuffDocumentsChain()&lt;/code&gt; function to combine the prompt with the documents. We use the templating features of LangChain.js to create the differents parts of the prompt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;system prompt&lt;/em&gt;, which is a fixed message that we inject at the beginning of the prompt. You can have a look at it to understand the different parts of what it does and what it contains. The most important part is the &lt;code&gt;{context}&lt;/code&gt; placeholder at the end, which will be replaced by the retrieved documents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;human prompt&lt;/em&gt;, which is the user query. We use the &lt;code&gt;{input}&lt;/code&gt; placeholder to inject the user query into the prompt.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;em&gt;document prompt&lt;/em&gt;, which is used to format how we inject the retrieved documents into the system prompt. Our format is simple here, we just prepend the document filename to the content of the page.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create the chain to retrieve the documents from the database&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createRetrievalChain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;retriever&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asRetriever&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;combineDocsChain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next part of our chain is the retrieval of the documents from the database. The &lt;code&gt;createRetrievalChain()&lt;/code&gt; abstracts the process entirely: we just need to provide the retriever and the combineDocsChain, and it will take care of the rest. It will do a few things behind the scenes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert the user query into a vector using the embeddings model.&lt;/li&gt;
&lt;li&gt;Performs a vector search in the database to find the most relevant documents.&lt;/li&gt;
&lt;li&gt;Injects the most relevant documents into the &lt;strong&gt;context&lt;/strong&gt; of the chain.&lt;/li&gt;
&lt;li&gt;Passes the context to our previous &lt;code&gt;combineDocsChain&lt;/code&gt; to generate the prompt.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generate the response&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastUserMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastUserMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseStream&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-ndjson&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transfer-Encoding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chunked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the last part of the chain is to generate the response using &lt;code&gt;chain.stream()&lt;/code&gt; and passing the last user message, containing the question, as input. The response is then streamed back to the client.&lt;/p&gt;

&lt;p&gt;We use a stream of newline-delimited JSON (NDJSON) for the response, following the &lt;a href="https://github.com/Azure-Samples/ai-chat-app-protocol" rel="noopener noreferrer"&gt;AI Chat Protocol&lt;/a&gt; as our API contract between the frontend and the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the chatbot to Azure (optional)
&lt;/h2&gt;

&lt;p&gt;Now that we have a working chatbot locally, we can deploy it to Azure to make it accessible to the world. You've noticed how we handle the changes needed in the two endpoints to make the code work both locally and in the cloud.&lt;/p&gt;

&lt;p&gt;When deployed, the project architecture will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenwoithyerrlzgsjp55c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenwoithyerrlzgsjp55c.png" alt="Diagram showing the architecture of the deployed project" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll use Azure Functions to host the backend API, and Azure Static Web Apps to host the frontend. The Blob Storage will be used to store a copy of the original PDF documents, and Azure Cosmos DB for MongoDB vCore will be used as the vector database.&lt;/p&gt;

&lt;p&gt;To deploy the application on Azure, you'll need a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An Azure account. If you don't have an account, you can &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=javascript-0000-cxa" rel="noopener noreferrer"&gt;create one for free using this link&lt;/a&gt;. If you're a student, you can also get free credits with &lt;a href="https://aka.ms/azureforstudents" rel="noopener noreferrer"&gt;Azure for Students&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An Azure subscription with access enabled for the Azure OpenAI service. You can request access with &lt;a href="https://aka.ms/oaiapply" rel="noopener noreferrer"&gt;this form&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/azure-dev/install" rel="noopener noreferrer"&gt;Azure Developer CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have everything ready, you can deploy the project with the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal at the root of the project.&lt;/li&gt;
&lt;li&gt;Authenticate with Azure by running &lt;code&gt;azd auth login&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;azd up&lt;/code&gt; to deploy the application to Azure. This will provision Azure resources, deploy this sample, and build the search index based on the files found in the &lt;code&gt;./data&lt;/code&gt; folder.

&lt;ul&gt;
&lt;li&gt;You will be prompted to select a base location for the resources. If you don't know which one to choose, you can select &lt;code&gt;eastus2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;By default, the OpenAI resource will be deployed to &lt;code&gt;eastus2&lt;/code&gt;. You can set a different location with &lt;code&gt;azd env set AZURE_OPENAI_RESOURCE_GROUP_LOCATION &amp;lt;location&amp;gt;&lt;/code&gt;. Currently only a short list of locations is accepted. That location list is based on the &lt;a href="https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-availability" rel="noopener noreferrer"&gt;OpenAI model availability table&lt;/a&gt; and may become outdated as availability changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The deployment process will take a few minutes. Once it's done, you'll see the URL of the web app in the terminal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6wrtni3iolyo0shbo32.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6wrtni3iolyo0shbo32.png" alt="Screenshot of the azd up command result" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now open the resulting link for the web app in your browser and start chatting with the bot.&lt;/p&gt;

&lt;p&gt;If you're curious about the resources created, you can check them in the &lt;a href="https://portal.azure.com" rel="noopener noreferrer"&gt;Azure portal&lt;/a&gt;. You'll find a resource group with the following resources:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jewc9vqlb4j4hz0ooy2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jewc9vqlb4j4hz0ooy2.png" alt="Screenshot of the created Azure resources" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We used &lt;em&gt;Infrastructure as Code&lt;/em&gt; to set up the resources, you can look into the &lt;code&gt;infra&lt;/code&gt; folder to see the templates used to deploy the resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning up the resources
&lt;/h3&gt;

&lt;p&gt;To clean up all the Azure resources created and stop incurring costs, you can run the following command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;azd down --purge&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When asked if you are sure you want to continue, enter &lt;code&gt;y&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resource group and all the resources will be deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;This was only an introduction to the possibilities of building AI-powered applications with LangChain.js and Azure (but still quite lengthy!). If you want to dig further into Generative AI or LangChain.js, here are some resources I recommend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://js.langchain.com" rel="noopener noreferrer"&gt;LangChain.js documentation&lt;/a&gt;: the official documentation for LangChain.js, with tutorials and examples to get you started.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/generative-ai-for-beginners" rel="noopener noreferrer"&gt;Generative AI For Beginners&lt;/a&gt;: a collection of resources to learn about Generative AI, including tutorials, code samples, and more.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/azure/ai-services/openai/overview" rel="noopener noreferrer"&gt;Azure OpenAI Service&lt;/a&gt;: the official documentation for the Azure OpenAI Service, which provides access to the latest AI models from OpenAI.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure-Samples/langchainjs-quickstart-demo" rel="noopener noreferrer"&gt;Ask YouTube: LangChain.js + Azure Quickstart sample&lt;/a&gt;: another LangChain.js sample project that uses RAG to answer questions from YouTube videos.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Azure-Samples/azure-search-openai-javascript" rel="noopener noreferrer"&gt;Chat + Enterprise data with Azure OpenAI and Azure AI Search&lt;/a&gt;: an enterprise implementation of the chatbot, using Azure AI Search&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aka.ms/entgptsearchblog" rel="noopener noreferrer"&gt;Revolutionize your Enterprise Data with Chat: Next-gen Apps w/ Azure OpenAI and AI Search&lt;/a&gt;: a blog post that explain the RAG approach using Azure OpenAI and Azure AI Search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also find &lt;a href="https://github.com/Azure-Samples/azureai-samples" rel="noopener noreferrer"&gt;more Azure AI samples here&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="http://twitter.com/sinedied" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, I would be happy to discuss and take your suggestions!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
    <item>
      <title>A simpler and smaller Angular starter with ngLite</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Fri, 29 Jul 2022 13:02:18 +0000</pubDate>
      <link>https://dev.to/angular/a-simpler-and-smaller-angular-starter-with-nglite-1ooh</link>
      <guid>https://dev.to/angular/a-simpler-and-smaller-angular-starter-with-nglite-1ooh</guid>
      <description>&lt;p&gt;A lot of complaints I heard when starting with Angular are about the sheer amount of files you get even on simple apps. When looking at the default starter template you get from Angular CLI's &lt;code&gt;ng new&lt;/code&gt; command, it's true that it can be a bit overwhelming if you're not used to it.&lt;/p&gt;

&lt;p&gt;But it doesn't have to &lt;em&gt;always&lt;/em&gt; be that way. In this article, we'll explore how we can create a smaller and simpler template that's also easier to grasp for beginners, following the &lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it" rel="noopener noreferrer"&gt;YAGNI principle&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; what we'll discuss here doesn't aim to be a new universal starter to replace the default one for &lt;em&gt;all use cases&lt;/em&gt;, but rather an experiment on how it's possible to make things simpler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting prepared
&lt;/h2&gt;

&lt;p&gt;Make sure you have a recent &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; runtime installed (at least v14), and let's start by installing the &lt;a href="https://cli.angular.io" rel="noopener noreferrer"&gt;Angular CLI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command-line tool is used to initialize new projects, among other things. After installing, you usually use it to create a new Angular project with the &lt;code&gt;ng new&lt;/code&gt; command, but hold off a bit!&lt;/p&gt;

&lt;p&gt;If you're old enough, maybe you remember using &lt;a href="https://en.wikipedia.org/wiki/Software_remastering#nLite" rel="noopener noreferrer"&gt;nLite&lt;/a&gt; to slim down your Windows install, back in the day? We'll take a similar approach here with the Angular starter to create a "ngLite" template, making sure to keep the number of files to a minimum..&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In the following sections, we'll take some time to understand the base application template and how it can be slimmed down for simpler projects. If you want to skip directly to the end result, you can use the command &lt;code&gt;npx degit sinedied/ng-lite-starter my-app&lt;/code&gt; that will use this pre-made &lt;a href="https://github.com/sinedied/ng-lite-starter" rel="noopener noreferrer"&gt;github template&lt;/a&gt;, but you'll miss all the fun 😉.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Slimming down the starter template
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74me80h7rli74ugaoi1o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74me80h7rli74ugaoi1o.gif" alt="Man removing everything on a table" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's create our app with this (long) command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ng new ng-lite &lt;span class="nt"&gt;--minimal&lt;/span&gt; &lt;span class="nt"&gt;--inline-template&lt;/span&gt; &lt;span class="nt"&gt;--inline-style&lt;/span&gt; &lt;span class="nt"&gt;--routing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain the options we used here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--minimal&lt;/code&gt;: creates a minimal project, without any test tools. When we'll get there, we'll probably want to use a better test framework than the default one anyways.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--inline-template&lt;/code&gt; and &lt;code&gt;--inline-style&lt;/code&gt;: enables single file components (SFC), including the HTML template and CSS styling directly into your TypeScript components. Instead of 3 files per component, you'll get only one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--routing=false&lt;/code&gt;: disables the default routing system. We can always add it back later if needed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--style=css&lt;/code&gt;: use standard CSS styling for our components. If you prefer other flavors like SCSS, you can adapt this option to suit your needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the files are generated and the dependencies installed, let's hop into the &lt;code&gt;ng-lite&lt;/code&gt; folder and start with some cleaning, removing dependencies we don't need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;ng-lite

&lt;span class="c"&gt;# Remove the dependencies we don't need&lt;/span&gt;
&lt;span class="c"&gt;# It's not because it's part of the framework that we have to use it :)&lt;/span&gt;
npm &lt;span class="nb"&gt;rm&lt;/span&gt; @angular/animations @angular/forms @angular/router @angular/platform-browser-dynamic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's have a look at all the files we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.vscode/
|- extensions.json
|- launch.json
|- tasks.json
src/
|- app/
| |- app.component.ts
| |- app.module.ts
|- assets/
| |- .gitkeep
|- environments/
| |- environment.prod.ts
| |- environment.ts
|- favicon.ico
|- index.html
|- main.ts
|- polyfills.ts
|- styles.css
.browserslistrc
.gitignore
angular.json
package.json
README.md
tsconfig.app.json
tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.vscode&lt;/code&gt; folder contains configurations related to the &lt;a href="https://aka.ms/vs/code" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; editor. It provides tasks to debug your app when pressing &lt;code&gt;F5&lt;/code&gt; key, and suggests you to install the must-have &lt;a href="https://marketplace.visualstudio.com/items?itemName=angular.ng-template&amp;amp;WT.mc_id=javascript-0000-yolasors" rel="noopener noreferrer"&gt;Angular language service extension&lt;/a&gt; when you open the project. If you don't use VS Code though, you can remove this folder.&lt;/p&gt;

&lt;p&gt;Next, we'll slim down a bit the number of files at the root of the project. You can see two &lt;code&gt;tsconfig.json&lt;/code&gt; files there, used for &lt;a href="https://www.typescriptlang.org" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; configuration. This separation may be useful when you want to use a different configuration for your tests, which is the case for the default setup created by &lt;code&gt;ng new&lt;/code&gt;, but we don't need it here. Add these lines coming from &lt;code&gt;tsconfig.app.json&lt;/code&gt; at the bottom of the &lt;code&gt;tsconfig.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/main.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And remove &lt;code&gt;tsconfig.app.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;tsconfig.app.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need to change a bit the &lt;code&gt;angular.json&lt;/code&gt; file before your app can build again, but let's keep that for later.&lt;/p&gt;

&lt;p&gt;Now, there's another file that we can get rid of: &lt;code&gt;.browserslistrc&lt;/code&gt;. It's a file that tells which browsers you want to support, and the Angular build system will adjust the CSS and JS output accordingly. Instead of having a separate file for that, you can add this entry at the end of the &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"browserslist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"last 1 Chrome version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"last 1 Firefox version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"last 2 Edge major versions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"last 2 Safari major versions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"last 2 iOS major versions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Firefox ESR"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can remove the &lt;code&gt;.browserslistrc&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; .browserslistrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reworking the &lt;code&gt;src/&lt;/code&gt; folder
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;src/&lt;/code&gt; folder, you'll find a file named &lt;code&gt;polyfills.ts&lt;/code&gt; that may contain polyfills, small pieces of code used to provide a compatibility layer for newer features. If you're targetting recent browsers, you can get rid of this file and just add the &lt;code&gt;zone.js&lt;/code&gt; import at the top of &lt;code&gt;main.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add this at the top of main.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zone.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, remove &lt;code&gt;polyfills.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;src/polyfills.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, there's a folder named &lt;code&gt;assets/&lt;/code&gt; that you can use to put any assets (images, fonts, JSON files...) you want to be copied to the &lt;code&gt;dist/&lt;/code&gt; folder. It contains an empty &lt;code&gt;.gitkeep&lt;/code&gt; to get the folder stored in the git repo, as git doesn't store empty folders. We can simplify this structure a bit, by also grouping our future assets with the base &lt;code&gt;index.html&lt;/code&gt; and favicon file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Removes assets/ folder&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; src/assets
&lt;span class="c"&gt;# Creates a public/ folder instead&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;src/public
&lt;span class="c"&gt;# Moves index.html and favicon.ico files into it&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;src/index.html src/favicon.ico src/public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't change much, but it's more in line with what you can find in almost all other web frameworks (React, Vue, Svelte...) and it means that you can easily add any new files to be placed at the root of the &lt;code&gt;dist/&lt;/code&gt; folder without having to edit &lt;code&gt;angular.json&lt;/code&gt; every time, something we'll have to do once a bit later.&lt;/p&gt;

&lt;p&gt;The next change we'll do here is get rid of &lt;code&gt;src/app/app.module.ts&lt;/code&gt;. Starting with Angular 14, we can create apps using &lt;a href="https://angular.io/guide/standalone-components" rel="noopener noreferrer"&gt;only components&lt;/a&gt; and that's perfect as it's all we need to get started.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Keep in mind that Angular Standalone components are still in preview, so the API may still change in the future.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Removes app.module.ts file&lt;/span&gt;
&lt;span class="nb"&gt;rm &lt;/span&gt;src/app/app.module.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the file &lt;code&gt;src/app/app.component.ts&lt;/code&gt;, our entry component, and add &lt;code&gt;standalone: true&lt;/code&gt; at the top of the component properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Add the line below&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll finally update the entry point of the application located at &lt;code&gt;src/main.ts&lt;/code&gt;, to bootstrap our app using our component. Replace the content of the file with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zone.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;enableProdMode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bootstrapApplication&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app/app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./environments/environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;production&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;enableProdMode&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll keep the rest of the files as-is as they will be useful for our app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;src/styles.css&lt;/code&gt; is the global stylesheet for the app. You can use it to import any CSS lib you want to use, and put your global styling here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/environments/environment*.ts&lt;/code&gt; these files contain the environment configuration for the app. &lt;code&gt;environment.ts&lt;/code&gt; will be used during development, and &lt;code&gt;environment.prod.ts&lt;/code&gt; will replace it during production builds so it's an easy way to define any environment-specific settings, like your API URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Editing &lt;code&gt;angular.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;angular.json&lt;/code&gt; file is the (rather verbose) configuration file that tells the Angular CLI how to run your project's tasks, such as building your app. Since we changed a few things from the default app structure, our final step it to update this file to reflect our changes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Replace the line &lt;code&gt;"tsConfig": "tsconfig.app.json",&lt;/code&gt; with &lt;code&gt;"tsConfig": "tsconfig.json",&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove the line &lt;code&gt;"polyfills": "src/polyfills.ts",&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replace the line &lt;code&gt;"index": "src/index.html",&lt;/code&gt; with &lt;code&gt;"index": "src/public/index.html",&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"src/favicon.ico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"src/assets"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"assets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"glob"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, add the line &lt;code&gt;"standalone": true,&lt;/code&gt; under the &lt;code&gt;"@schematics/angular:component"&lt;/code&gt; key, as we'll use standalone components in our app:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"@schematics/angular:component"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wheew! That's a lot of changes, but we managed to simplify our starter template by quite a lot while still retaining essential Angular features. Look at this screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1adhi51bvy6qvdviap0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1adhi51bvy6qvdviap0.png" alt="Comparison between default starter and our" width="800" height="898"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, a nice side effect of this work is the reduction of the initial bundle size, from a &lt;strong&gt;150.91 kB total (46.20 kB gzipped)&lt;/strong&gt; with the default &lt;code&gt;ng new&lt;/code&gt; template to a &lt;strong&gt;116.01 kB total (36.15 kB gzipped)&lt;/strong&gt; with our starter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcswp6g5cagwdsz62an4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcswp6g5cagwdsz62an4w.png" alt="Bar graph showing bundle size reduction" width="540" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, that's a bit tedious and not something you want to do every time you start a new project! You can use this &lt;a href="https://github.com/sinedied/ng-lite-starter" rel="noopener noreferrer"&gt;github template&lt;/a&gt; if you'd like to use this starter again in the future, or you can make your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about testing?
&lt;/h2&gt;

&lt;p&gt;You may have noticed that this template doesn't include any testing tools. This is fine for learning and personal projects, but it's not a good idea for a production app to skip unit testing.&lt;/p&gt;

&lt;p&gt;While the default Angular starter includes unit tests, it makes use of the older and clunky &lt;a href="https://karma-runner.github.io" rel="noopener noreferrer"&gt;Karma&lt;/a&gt;/&lt;a href="https://jasmine.github.io" rel="noopener noreferrer"&gt;Jasmine&lt;/a&gt; combo for unit testing.&lt;/p&gt;

&lt;p&gt;When you need unit testing for your project, you can use the much better and faster testing framework &lt;a href="https://jestjs.io" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; with our &lt;code&gt;ngLite&lt;/code&gt; template by adding a few extra steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Run the command &lt;code&gt;npm install --save-dev jest @angular-builders/jest @types/jest&lt;/code&gt; to install the dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a &lt;code&gt;jest.config.js&lt;/code&gt; file to your project root with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;clearMocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;collectCoverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;coverageDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;coverage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a &lt;code&gt;tsconfig.spec.json&lt;/code&gt; file to your project root with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./tsconfig.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./out-tsc/spec"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.spec.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"src/**/*.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In your &lt;code&gt;angular.json&lt;/code&gt; file, add this after your &lt;code&gt;serve&lt;/code&gt; configuration (under the &lt;code&gt;architect&lt;/code&gt; key):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@angular-builders/jest:run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tsConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsconfig.spec.json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If you want to have tests generated by default when using the &lt;code&gt;ng generate&lt;/code&gt; command, you can also remove all the &lt;code&gt;"skipTests": true&lt;/code&gt; occurrences in this file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create your first test in &lt;code&gt;src/app/app.component.spec.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;compileComponents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should create the component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can now run your tests with &lt;code&gt;ng test&lt;/code&gt; or &lt;code&gt;ng test --watch&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;If you followed closely, you've seen that we didn't even include some of Angular's core libraries like &lt;code&gt;@angular/forms&lt;/code&gt; or &lt;code&gt;@angular/router&lt;/code&gt;. Well, that's not because you're writing an Angular app that you have to use &lt;em&gt;all&lt;/em&gt; of the provided libraries! For sure they're convenient, and you can always add them back later if you need them, but you can also build your entire app &lt;em&gt;without them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That's what we'll explore in a further article. Now that we have a simpler starter template, why not try building a complete app, and keeping the code as simple as it can?&lt;/p&gt;

&lt;p&gt;In the meantime, you can have a look at &lt;a href="https://github.com/sinedied/ng-lite-todo" rel="noopener noreferrer"&gt;this todo list app&lt;/a&gt; built from expanding on that idea.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="http://twitter.com/sinedied" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, I would be happy to discuss and take your suggestions!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>angular</category>
    </item>
    <item>
      <title>New tips &amp; tricks for your Azure Static Web Apps</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Mon, 23 May 2022 16:13:53 +0000</pubDate>
      <link>https://dev.to/azure/new-tips-tricks-for-your-azure-static-web-apps-56pl</link>
      <guid>https://dev.to/azure/new-tips-tricks-for-your-azure-static-web-apps-56pl</guid>
      <description>&lt;p&gt;About a year ago, we published &lt;a href="https://aka.ms/StaticWebAppsTips" rel="noopener noreferrer"&gt;a series of videos&lt;/a&gt; to help you get the most out of Azure Static Web Apps. It's time to update it with fresh tips, as we got new exciting tools to improve your dev experience!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you've never heard of Static Web Apps (or SWA for short), it's a web app hosting service provided by Azure, offering streamlined full-stack development with many built-in features like authentication, customizable routing, serverless functions and more. It also has a great &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=javascript-0000-yolasors" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tip #18: Run, build and deploy your apps with the new SWA CLI
&lt;/h2&gt;

&lt;p&gt;With the release of &lt;a href="https://github.com/Azure/static-web-apps-cli" rel="noopener noreferrer"&gt;the new SWA CLI&lt;/a&gt;, it's never been easier to get started with Azure Static Web Apps! With the new commands you can configure your project to test it locally with the emulator, and deploy it directly from the CLI in a few minutes. It also support multiple configurations, and it's compatible with monorepos. Watch the video to learn how to get started with the new commands.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/zchTNKmay6M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip #19: End-to-end browser debugging of your Static Web Apps with VS Code
&lt;/h2&gt;

&lt;p&gt;I love web development, but when you're trying to debug your app, whether it's the HTML, CSS or JavaScript, it always involves a lot of painful back and forth between your code editor and the browser. But thanks to &lt;a href="https://aka.ms/devtools-for-code" rel="noopener noreferrer"&gt;this new VS Code extension&lt;/a&gt;, you can now preview and debug your apps without having to leave your editor, all from a single window! And it even comes with built-in hints and support for accessibility testing, so you won't have any excuses to not make your app accessible.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/l39cjaosxKw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell us your feedback!
&lt;/h2&gt;

&lt;p&gt;We're also here on dev.to to listen and help as much as we can! 🙂&lt;/p&gt;

&lt;p&gt;Please tell us your experience, your difficulties and questions, and what videos you would like to see next in this series!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>staticwebapps</category>
    </item>
    <item>
      <title>Deploy your Static Web Apps to Azure directly from the command line</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Fri, 20 May 2022 13:09:10 +0000</pubDate>
      <link>https://dev.to/azure/deploy-your-static-web-apps-to-azure-directly-from-the-command-line-2ip8</link>
      <guid>https://dev.to/azure/deploy-your-static-web-apps-to-azure-directly-from-the-command-line-2ip8</guid>
      <description>&lt;p&gt;For this second article about the SWA CLI, we'll talk about the new deployment options. And yes, you can now deploy your apps straight from the command line! Who's better than the person who implemented it to talk about it? I'll let my friend &lt;a href="https://twitter.com/manekinekko" rel="noopener noreferrer"&gt;Wassim&lt;/a&gt; explain it all to you.&lt;/p&gt;


&lt;div class="ltag__user ltag__user__id__13081"&gt;
    &lt;a href="/wassimchegham" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F13081%2F0a029e3e-206b-41b5-83b3-52e39a29c4cb.jpg" alt="wassimchegham image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/wassimchegham"&gt;Wassim Chegham&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/wassimchegham"&gt;Senior Developer Advocate at Microsoft ★ GDE for Google ★ Angular and Node.js contributor ★ BytecodeAlliance contributor &amp;amp; WebAssembly enthusiast&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;If you've never heard of Static Web Apps (or SWA for short), it's a web app hosting service provided by Azure, offering streamlined full-stack development with many built-in features like authentication, customizable routing, serverless functions and more. It also has a great &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=javascript-0000-yolasors" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Logging in to Azure
&lt;/h2&gt;

&lt;p&gt;In the new SWA CLI for Azure Static Web Apps, we introduced a new &lt;code&gt;swa login&lt;/code&gt; command allowing you to log in to your Azure account, straight from the command line. This command will encrypt and store your credentials in your system's key Chain, so you don't have to enter them again.&lt;/p&gt;

&lt;p&gt;In most common cases, the &lt;code&gt;swa login&lt;/code&gt; will pick your current active Azure credentials from the following locations (in this order):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your environment variables, if you have set one or all of following &lt;a href="https://azure.github.io/static-web-apps-cli/docs/cli/env-vars#azure-identity" rel="noopener noreferrer"&gt;variables&lt;/a&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AZURE_TENANT_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AZURE_CLIENT_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AZURE_CLIENT_SECRET&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Your current Visual Studio Code, if you're using VS Code and is connected to Azure.&lt;/li&gt;
&lt;li&gt;Interactive prompt using your browser.&lt;/li&gt;
&lt;li&gt;The last fallback is the device code flow, which will prompt you to log in to your Azure account using a generated device code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, if you need, you can also provide any of these credentials from the command line, using the available flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Usage: swa login &lt;span class="o"&gt;[&lt;/span&gt;options]

login into Azure

Options:
  &lt;span class="nt"&gt;-S&lt;/span&gt;, &lt;span class="nt"&gt;--subscription-id&lt;/span&gt; &amp;lt;subscriptionId&amp;gt;    Azure subscription ID used by this project
                                            &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"process.env.AZURE_SUBSCRIPTION_ID"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-R&lt;/span&gt;, &lt;span class="nt"&gt;--resource-group&lt;/span&gt; &amp;lt;resourceGroupName&amp;gt;  Azure resource group used by this project
  &lt;span class="nt"&gt;-T&lt;/span&gt;, &lt;span class="nt"&gt;--tenant-id&lt;/span&gt; &amp;lt;tenantId&amp;gt;                Azure tenant ID &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"process.env.AZURE_TENANT_ID"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt;, &lt;span class="nt"&gt;--client-id&lt;/span&gt; &amp;lt;clientId&amp;gt;                Azure client ID
  &lt;span class="nt"&gt;-CS&lt;/span&gt;, &lt;span class="nt"&gt;--client-secret&lt;/span&gt; &amp;lt;clientSecret&amp;gt;       Azure client secret
  &lt;span class="nt"&gt;-n&lt;/span&gt;, &lt;span class="nt"&gt;--app-name&lt;/span&gt; &amp;lt;appName&amp;gt;                  Azure Static Web App application name
  &lt;span class="nt"&gt;-cc&lt;/span&gt;, &lt;span class="nt"&gt;--clear-credentials&lt;/span&gt;                  clear persisted credentials before login &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt;, &lt;span class="nt"&gt;--use-keychain&lt;/span&gt;                        &lt;span class="nb"&gt;enable &lt;/span&gt;using the operating system native keychain
                                            &lt;span class="k"&gt;for &lt;/span&gt;persistent credentials &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-nu&lt;/span&gt;, &lt;span class="nt"&gt;--no-use-keychain&lt;/span&gt;                    disable using the operating system native keychain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once logged in, the SWA CLI will store your active Azure subscription ID and tenant ID in a local file called &lt;code&gt;.env&lt;/code&gt; at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ swa login

Welcome to Azure Static Web Apps CLI &lt;span class="o"&gt;(&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;

Checking Azure session...
✔ Successfully logged into Azure!
✔ Saved project credentials &lt;span class="k"&gt;in&lt;/span&gt; .env file.
✔ Successfully setup project!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will be used when deploying your project to Azure using the &lt;code&gt;swa deploy&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying your app
&lt;/h2&gt;

&lt;p&gt;The new SWA CLI makes it even more easier to deploy your apps to Azure. You can now deploy your app directly from the command line, using one single &lt;code&gt;swa deploy&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;swa deploy&lt;/code&gt; command is smarter enough to figure out if you already have an Azure Static Web Apps instance available, and if so, it will use it. Otherwise, it will create a new one on the fly - or prompt you to select one from a list of already available ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ swa deploy

Welcome to Azure Static Web Apps CLI &lt;span class="o"&gt;(&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;

Using configuration &lt;span class="s2"&gt;"thundr"&lt;/span&gt; from file:
  /home/wassimchegham/oss/@thundr-dev/thundr/swa-cli.config.json

Deploying front-end files from folder:
  /home/wassimchegham/oss/@thundr-dev/thundr/app/dist/thundr-ui

Deploying API from folder:
  /home/wassimchegham/oss/@thundr-dev/thundr/api

Checking Azure session...
✔ Successfully logged into Azure!

Checking project settings...
? Choose your Static Web App › - Use arrow-keys. Return to submit.
❯   &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Create a new application
    swa_samples/angular-web-bluetooth
    swa_samples/catsify
    swa_samples/hexa
    swa_samples/ngxtools
    swa_samples/nitrooo
  ↓ swa_samples/photobooth-teams

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;swa deploy&lt;/code&gt; command is also able to detect if your existing Static Web Apps instance has already been deployed from a CI/CD pipeline and inform you about it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying from a CI/CD pipeline
&lt;/h2&gt;

&lt;p&gt;Deploying from CI/CD pipelines is a common practice in many projects. You can use the &lt;code&gt;swa deploy&lt;/code&gt; command to deploy your app to Azure from your CI/CD pipeline. You can accomplish this by setting a deployment token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ swa deploy &lt;span class="nt"&gt;--print-token&lt;/span&gt; &lt;span class="nt"&gt;--app-name&lt;/span&gt; thundr &lt;span class="nt"&gt;--resource-group&lt;/span&gt; swa_samples

Welcome to Azure Static Web Apps CLI &lt;span class="o"&gt;(&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;

Checking project &lt;span class="s2"&gt;"thundr"&lt;/span&gt; settings...
✔ Successfully setup project!

Deploying to environment: preview

Deployment token:
7c3fc44b858164b677-truncated-8c80ace9-39a8-416c-ae22-864745c0470b003
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swa deploy &lt;span class="nt"&gt;--deployment-token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7c3fc44b858164b677-truncated-8c80ace9-39a8-416c-ae22-864745c0470b003b003
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set the deployment token as an &lt;a href="https://azure.github.io/static-web-apps-cli/docs/cli/env-vars#deploy-settings" rel="noopener noreferrer"&gt;environment variable&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SWA_CLI_DEPLOYMENT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;7c3fc44b858164b677-truncated-8c80ace9-39a8-416c-ae22-864745c0470b003b003
swa deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment variables
&lt;/h2&gt;

&lt;p&gt;The new SWA CLI introduced a new set of environment variables to make it easier for you to configure your experience. You can customize the behavior of the SWA CLI by setting the following environment variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General settings&lt;/li&gt;
&lt;li&gt;Emulator settings&lt;/li&gt;
&lt;li&gt;Deploy settings&lt;/li&gt;
&lt;li&gt;Runtime settings&lt;/li&gt;
&lt;li&gt;Login experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read more about these environment variables in the &lt;a href="https://azure.github.io/static-web-apps-cli/docs/cli/env-vars" rel="noopener noreferrer"&gt;Environment Variables section&lt;/a&gt; in our new documentation website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;From the start of your project to its deployment to Azure, with all the needed local testing in-between, you should now be equipped to take care of your complete development process all from the command line. Now the main challenge remaining for you is to deploy and grow your app 😉.&lt;/p&gt;

&lt;p&gt;If you'd like to learn some tips you can use when developing your app, you can check out the &lt;a href="https://aka.ms/StaticWebAppsTips" rel="noopener noreferrer"&gt;Static Web Apps - Tips &amp;amp; Tricks&lt;/a&gt; video series.&lt;/p&gt;

&lt;p&gt;You can also learn more about all the new features of the SWA CLI and how to use them by looking at the &lt;a href="https://azure.github.io/static-web-apps-cli/" rel="noopener noreferrer"&gt;new docs website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell us your feedback!
&lt;/h2&gt;

&lt;p&gt;We're also here on dev.to to listen and help as much as we can! 🙂&lt;/p&gt;

&lt;p&gt;Please tell us your experience, your difficulties, your questions, and what features you would like to see in the next versions of the SWA CLI.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>staticwebapps</category>
    </item>
    <item>
      <title>Get started with the new Azure Static Web Apps CLI</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Thu, 19 May 2022 14:46:06 +0000</pubDate>
      <link>https://dev.to/azure/get-started-with-the-new-azure-static-web-apps-cli-mm3</link>
      <guid>https://dev.to/azure/get-started-with-the-new-azure-static-web-apps-cli-mm3</guid>
      <description>&lt;p&gt;You may have missed it, but the &lt;a href="https://github.com/Azure/static-web-apps-cli" rel="noopener noreferrer"&gt;new Azure Static Web Apps CLI&lt;/a&gt; is now available!&lt;/p&gt;

&lt;p&gt;It would take a whole post to describe all the changes and new features, but here are some of the highlights. With the new &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;deploy&lt;/code&gt; commands, the complete developement cycle of your app is now available &lt;em&gt;in a single tool&lt;/em&gt;, directly from the command line. To get started, make sur you have a recent &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; version installed (&amp;gt;14) and type this command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @azure/static-web-apps-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll now have a look at the new commands and the most important features.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you've never heard of Static Web Apps (or SWA for short), it's a web app hosting service provided by Azure, offering streamlined full-stack development with many built-in features like authentication, customizable routing, serverless functions and more. It also has a great &lt;a href="https://azure.microsoft.com/free/?WT.mc_id=javascript-0000-yolasors" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;swa&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;As the new CLI introduces a gazillion new options, it must quite a steep learning curve to get started? Quite the opposite!&lt;/p&gt;

&lt;p&gt;Every command starts with &lt;code&gt;swa&lt;/code&gt;, and now it might also be the only thing you'll need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# yes, that's all you need to type&lt;/span&gt;
swa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command is a macro shorcut for &lt;code&gt;swa init&lt;/code&gt;, &lt;code&gt;swa build&lt;/code&gt;, &lt;code&gt;swa login&lt;/code&gt; and &lt;code&gt;swa deploy&lt;/code&gt;. We'll get to the details of all these commands in a bit, the most important thing here is that &lt;code&gt;swa&lt;/code&gt; is the only command you need to know to configure a new project for use with Static Web Apps, build it and deploy it. You don't have to worry about any options or flags, just type &lt;code&gt;swa&lt;/code&gt; and you'll be on your way. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the configuration
&lt;/h2&gt;

&lt;p&gt;The first of the new commands is &lt;code&gt;swa init&lt;/code&gt;. This commands with detect your project configuration and the frameworks you're using to suggest a configuration for you, so you won't have to read through &lt;a href="https://azure.github.io/static-web-apps-cli/" rel="noopener noreferrer"&gt;the shiny new docs&lt;/a&gt; to figure out what options you need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ig0bi0yi4rkz8b7rm96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ig0bi0yi4rkz8b7rm96.png" alt="swa init command screenshot" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After asking a name for your project configuration, you can validate or edit the detected project settings. Once finished, it will create a &lt;code&gt;swa-cli.config.json&lt;/code&gt; file in the current folder with your settings. You can commit this file in your project repository so everyone from your team can use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building your project
&lt;/h2&gt;

&lt;p&gt;After the configuration is created, you're all set to use other commands without having to bother with any options or flags.&lt;/p&gt;

&lt;p&gt;Whether you need to build your front-end app, your API, or both, the &lt;code&gt;swa build&lt;/code&gt; command will do the job. It also takes care of installing your NPM dependencies (if needed) detecting if you're using npm, Yarn or PNPM with your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running your app locally
&lt;/h2&gt;

&lt;p&gt;Historically, being able to run your app locally was the first feature of the SWA CLI. Of course it's still there, and &lt;code&gt;swa start&lt;/code&gt; allows you to emulate the SWA runtime environment, along with the routing, authentication features and API. A few changes were made to improve the developer experience, and of course bugs were fixed to more closely match the real SWA runtime.&lt;/p&gt;

&lt;p&gt;Previously, if you were using an API, you had to install &lt;a href="https://docs.microsoft.com/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash&amp;amp;WT.mc_id=javascript-0000-yolasors#publish" rel="noopener noreferrer"&gt;Functions Core Tools&lt;/a&gt; and make sure to match the tools version with the Node.js version you're using, but now the whole process is automated for you. If you don't have the tools installed or if the wrong version is installed, you don't have to worry about it: it will be automatically installed for you when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;With these new commands, you can already setup your project, build it and test it locally. Using &lt;code&gt;swa start&lt;/code&gt; is also great for debugging your app and API in a live running environment. Once you're happy with the result, you can deploy it to Azure, and that's what we'll explore in the next post.&lt;/p&gt;

&lt;p&gt;In the meantime, you can also head to &lt;a href="https://azure.github.io/static-web-apps-cli/" rel="noopener noreferrer"&gt;the new docs website&lt;/a&gt; to learn more about the new features and how to use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell us your feedback!
&lt;/h2&gt;

&lt;p&gt;We're also here on dev.to to listen and help as much as we can! 🙂&lt;/p&gt;

&lt;p&gt;Please tell us your experience, your difficulties, your questions, and what features you would like to see in the next versions of the SWA CLI.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>staticwebapps</category>
    </item>
    <item>
      <title>One simple command to improve your JavaScript workflow</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Tue, 15 Mar 2022 14:37:09 +0000</pubDate>
      <link>https://dev.to/sinedied/a-simple-command-to-improve-your-javascript-workflow-3mp4</link>
      <guid>https://dev.to/sinedied/a-simple-command-to-improve-your-javascript-workflow-3mp4</guid>
      <description>&lt;p&gt;I don't know about you, but as a JavaScript developer, there's something I do hundreds of times a day: running &lt;strong&gt;NPM scripts&lt;/strong&gt; to &lt;em&gt;test&lt;/em&gt;, &lt;em&gt;lint&lt;/em&gt;, &lt;em&gt;build&lt;/em&gt;, &lt;em&gt;serve&lt;/em&gt;, &lt;em&gt;release&lt;/em&gt; and more.&lt;/p&gt;

&lt;p&gt;And because I'm a CLI guy, that involves a lot of typing, which as a result involves a lot of typing mistakes. &lt;code&gt;npm start&lt;/code&gt; or &lt;code&gt;npm run build&lt;/code&gt; may seems short enough, but typing it hundreds of times is not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ui0x9w80acxfeddyr29.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ui0x9w80acxfeddyr29.gif" alt="bored dog typing" width="400" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's why almost 4 years ago I've decided to find a better workflow. Ultimately, my goal have been to type the minimum amount of characters to run my scripts.&lt;/p&gt;

&lt;p&gt;I've found a way to do that with &lt;strong&gt;fuzzy matching&lt;/strong&gt;. I also found two existing tools that explored the same idea, &lt;a href="https://www.npmjs.com/package/fuzzy-npm-run" rel="noopener noreferrer"&gt;fuzzy-npm-run&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/fuzzy-run" rel="noopener noreferrer"&gt;fuzzy-run&lt;/a&gt;. Both relied on the same library &lt;a href="http://fusejs.io" rel="noopener noreferrer"&gt;fuse.js&lt;/a&gt; which is not great for matching commands, as it doesn't weight properly specific features like subcommands separation (using characters like &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;) or first character of words. This led me to run the wrong scripts too many times to count...&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing &lt;code&gt;fuzz-run&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;That's about when I've decided to spin my own runner tool. I benchmarked many fuzzy matching libraries, and kept the best one I found suited for the job, &lt;a href="https://www.npmjs.com/package/fuzzysort" rel="noopener noreferrer"&gt;fuzzysort&lt;/a&gt;, as it solved all the issues I had with existing runners.&lt;/p&gt;

&lt;p&gt;And this is what I've been using every day for the last years, running all my scripts with only a few characters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3bsly9xppno8odtgknu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3bsly9xppno8odtgknu.gif" alt="demo gif" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install it with &lt;code&gt;npm install -g fuzz-run&lt;/code&gt;, then its usage is dead simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If no arguments are provided, it will list all available scripts (no more digging in &lt;code&gt;package.json&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type &lt;code&gt;nr &amp;lt;fuzzy_script_name&amp;gt;&lt;/code&gt; to run a script. You can even add options to the script, like &lt;code&gt;nr b --watch&lt;/code&gt;. And no need for silly extra &lt;code&gt;--&lt;/code&gt; like with &lt;code&gt;npm&lt;/code&gt; to pass those options.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The name of the script is fuzzy matched, this means that you either type only some letters of the script name, regardless of their position (first letters weights more), like &lt;code&gt;t&lt;/code&gt; for the &lt;code&gt;test&lt;/code&gt; script. For compound script names like &lt;code&gt;test:ci&lt;/code&gt;, you can just type the first letters of each word, like &lt;code&gt;tc&lt;/code&gt;. It will even match if you do simple typos, like &lt;code&gt;ets&lt;/code&gt; for &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that it will autodetect which package manager you're using in a project, and will run the script with the right commands whether you're using &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;yarn&lt;/code&gt; or &lt;code&gt;pnpm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's it, you can now run your scripts with only a few characters, and you'll be as happy as I was when I started using it.&lt;/p&gt;

&lt;p&gt;To be honest, I almost forgot about this tool, because well, it quickly become part of my dev habits, and I only think about it when I'm on a new setup and it's not there. That's also why I'm only writing this post now, almost 4 years later 😅&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpbq96w90cwd8u7r5yk9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpbq96w90cwd8u7r5yk9.gif" alt="happy" width="460" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;My most frequent task being taken care of, I recently decided to think about how I could improve some other very common (and boring) tasks I do in almost every project I work on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updates, updates, updates
&lt;/h3&gt;

&lt;p&gt;One of these tasks is &lt;strong&gt;updating dependencies&lt;/strong&gt;. It often involves running &lt;code&gt;npm outdated&lt;/code&gt; to check if there are any outdated dependencies, and then running &lt;code&gt;npm update&lt;/code&gt; to update them within the allowed ranges in my &lt;code&gt;package.json&lt;/code&gt;. Then most of the time, running &lt;code&gt;npm outdated --long&lt;/code&gt; again to see the remaining ones with a link to their repository, so I can look at changelogs for possible breaking changes and decide if I want to update them, one &lt;code&gt;npm install &amp;lt;package&amp;gt;@latest&lt;/code&gt; at a time.&lt;/p&gt;

&lt;p&gt;When I remember the command, sometimes I use &lt;a href="https://www.npmjs.com/package/npm-check" rel="noopener noreferrer"&gt;npm-check&lt;/a&gt; or &lt;a href="https://www.npmjs.com/package/npm-check-updates" rel="noopener noreferrer"&gt;npm-check-updates&lt;/a&gt; to save some time, but I most often don't remember which is the one I usually use and what's the syntax. Yarn has definitely an edge here with its &lt;code&gt;yarn upgrade-interactive&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;So here comes a scripted action to tackle this task in a more automated way: &lt;code&gt;nr --update&lt;/code&gt; or simply &lt;code&gt;nr -u&lt;/code&gt; (because you know, the less characters to type the better 😉).&lt;/p&gt;

&lt;p&gt;It will run this sequence of actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm outdated&lt;/code&gt; to check for any outdated dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If there are any, ask if you want to run &lt;code&gt;npm update&lt;/code&gt; to update them within the allowed ranges in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, perform an interactive upgrade for packages outside your allowed ranges, with &lt;code&gt;npx npm-check -u&lt;/code&gt;. You'll be able to choose which packages to upgrade, with a link to their repository to see the changelogs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, if you're within a Yarn or PNPM project, the commands will change accordingly.&lt;/p&gt;

&lt;p&gt;With that, I won't have to remember the exact syntax of the commands, and I'll be able to run this task whatever package manager used for the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing those install issues...
&lt;/h3&gt;

&lt;p&gt;Another task I've been doing a lot more that I would like, is trying to fix those install issues that sometimes occur with &lt;code&gt;npm install&lt;/code&gt; (and that includes Yarn, too).&lt;/p&gt;

&lt;p&gt;You know, when the install fails or breaks for no apparent reason, and most often it's because the dependency tree could not be resolved properly after adding or updating a package. Which often results in &lt;code&gt;rm -rf node_modules package-lock.json&lt;/code&gt; and &lt;code&gt;npm install&lt;/code&gt; to try to fix the issue.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;nr --refresh&lt;/code&gt; or &lt;code&gt;nr -r&lt;/code&gt; scripted action comes to the rescue, well doing exactly just that for you, with included adaptations for Yarn and PNPM. Again, less typing for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;This is a very small tool that I've been using for a long time, and I hope it's useful to you even though I initially created it to scratch my own itch.&lt;/p&gt;

&lt;p&gt;It's also completly &lt;a href="https://github.com/sinedied/fuzz-run" rel="noopener noreferrer"&gt;open source&lt;/a&gt;, so you can contribute to it or fork it to make it your own.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="http://twitter.com/sinedied" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; for more content, I would be happy to discuss about it and take your suggestions!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Instantly set up a new dev environment using containers and VS Code</title>
      <dc:creator>Yohan Lasorsa</dc:creator>
      <pubDate>Thu, 30 Sep 2021 15:00:11 +0000</pubDate>
      <link>https://dev.to/itnext/instantly-set-up-a-new-dev-environment-using-containers-and-vs-code-51g8</link>
      <guid>https://dev.to/itnext/instantly-set-up-a-new-dev-environment-using-containers-and-vs-code-51g8</guid>
      <description>&lt;p&gt;There are 3 things that make me waste a lot of time as a developer, and that can become a big source of frustration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a new development machine&lt;/li&gt;
&lt;li&gt;Manage and update my development environment to work on a project&lt;/li&gt;
&lt;li&gt;Reinstall everything again, because after many times installing and updating my dev tools everything broke :(&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if I told you that we can avoid these issues, and that it's possible to perfectly isolate each of your projects' environment? And to be able to share and update it easily, to have nothing to do when a newcomer joins your project?&lt;/p&gt;

&lt;p&gt;It's now possible thanks to the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack&amp;amp;WT.mc_id=javascript-14373-yolasors" rel="noopener noreferrer"&gt;Remote Development extension&lt;/a&gt; for VS Code and containers. In this post, I'll show you how to set up a complete dev environment inside a container and use it with your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can quickly onboard a new developer in a project using dev containers, share it with your teammates, and update it with no hassle.&lt;/li&gt;
&lt;li&gt;While nothing prevents you from using Docker and any IDE to do the same thing, the Remote Development extension provides seamless integration with VS Code to bootstrap and work with dev containers.&lt;/li&gt;
&lt;li&gt;Dev containers also allow you to take your dev environment everywhere with online dev tools like &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;GitHub Codespaces&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why developing &lt;em&gt;inside&lt;/em&gt; a container
&lt;/h2&gt;

&lt;p&gt;In every software project, the developer story is (almost) always the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Hey, welcome to the team! We're all excited to have you on board!" 🎉&lt;/li&gt;
&lt;li&gt;"Here's the doc to set up your environment for the project. Let me know when you're ready!" (&lt;em&gt;spoiler alert:&lt;/em&gt; sometimes there's &lt;strong&gt;no doc&lt;/strong&gt; at all, and you'll have to ask your teammates to know what to install)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most likely, a few hours/days later, the outcome is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Please help, I can't figure out why it's still not building/running properly" 😞&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp3z7fvcqqq0fnvdzj9e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp3z7fvcqqq0fnvdzj9e.gif" alt="Animation showing developer saying " width="500" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been that developer. A few times already.&lt;/p&gt;

&lt;p&gt;After a while of spending time reviewing everything you installed, you'll eventually discover common issues usually are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The environment setup doc is &lt;em&gt;outdated&lt;/em&gt; (I bet you didn't see that one coming, right? Isn't that the newcomer's job to update it anyway?).&lt;/li&gt;
&lt;li&gt;You installed the correct tools, but not the right &lt;em&gt;version&lt;/em&gt; ("Sorry, We forgot to tell you'll have to use this &lt;em&gt;specific version&lt;/em&gt;, as we have a few issues in our codebase").&lt;/li&gt;
&lt;li&gt;You have some &lt;em&gt;conflicts&lt;/em&gt; with the environment set up of other projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing is, we already know how to solve these problems, because that's the &lt;em&gt;exact same issues&lt;/em&gt; we had with production environments when we ship our applications.&lt;/p&gt;

&lt;p&gt;We solved all these problems by packaging the runtime environment of our applications into &lt;strong&gt;containers&lt;/strong&gt;. Then, why not also use containers for our &lt;em&gt;dev environment&lt;/em&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;You need to have these tools installed on your machine to get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com?WT.mc_id=javascript-14373-yolasors" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; (or any Docker-compatible client)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack&amp;amp;WT.mc_id=javascript-14373-yolasors" rel="noopener noreferrer"&gt;Remote Development Extension for VS Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Open VS Code on a new or existing project, then click on the bottom left&lt;br&gt;
of the status bar, on the "opposing chevron" icon (let me know if you have a better name):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg0523a0hkhejmhp1ir7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg0523a0hkhejmhp1ir7.png" alt="Screenshot showing remote extension button in VS Code status bar" width="800" height="58"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you do that, select &lt;strong&gt;Add Development Container Configuration Files...&lt;/strong&gt;, and it will open a huge list of environments starters. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu7w71xvmgg8zuku3no6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu7w71xvmgg8zuku3no6.png" alt="Screenshot showing remote extension" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll find pre-made configurations for Node.js, Java, .Net, Go, Python, and more, most likely there is one that you can use as a starting point. Once you've made your choice, you'll notice that a new &lt;code&gt;.devcontainer&lt;/code&gt; folder was created, containing 2 files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnhama6a2npzkkf3lguk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsnhama6a2npzkkf3lguk.png" alt="Screenshot of the new folder in VS Code" width="344" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll dig into these files just after, for now, let's reload the project to work inside a container. &lt;/p&gt;

&lt;p&gt;Click again on that "opposing chevron" icon in the status bar, and select &lt;strong&gt;Reopen in container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;VS Code will reload the project's window and start building the container. Depending on your internet connection speed, this may take a while but don't worry, it's only needed the first time.&lt;/p&gt;

&lt;p&gt;Once it's finished, you'll see that VS Code is connected to your dev container in the status bar. You can also open a terminal and play with your new dev environment (in my case, I've set up a new Node.js v14 environment).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6da23ph8fi9t1es78ii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6da23ph8fi9t1es78ii.png" alt="Screenshot of VS Code connected to a dev container with a terminal open" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Customizing the dev setup
&lt;/h2&gt;

&lt;p&gt;Now that you have a working environment, you'll probably want to customize it for your project's needs. If you unfold the &lt;code&gt;.devcontainer&lt;/code&gt; folder in the explorer, you'll see these 2 files that will allow you to do that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;: this file defines your container configuration, and is used to build the container image that will be used for your environment. If you need to install additional tools or configure scripts and environment variables for your dev environment, this is where you should look at.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;devcontainer.json&lt;/code&gt;: this file allows to customize VS Code when the project is connected to a development container. In particular, you can specify VS Code settings, extensions, and port forwarding that will be enabled only in this project dev container.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a deeper look at how you can fine-tune your VS Code dev environment by editing your &lt;code&gt;devcontainer.json&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;
  
  
  VS Code settings and overrides
&lt;/h3&gt;

&lt;p&gt;You'll see something like this under the &lt;code&gt;settings&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*default*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;specific&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;settings.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;values&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;create.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.shell.linux"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/bin/bash"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This key allows you to override any VS Code settings when it's connected to this container environment. For example, if you work on Windows and your default terminal is PowerShell, by connecting to the dev container the setting above with switch your default terminal to Bash instead, which will be run inside the Linux system of the dev container. With these settings, you can set a common code formatter for your team, or force GPG signing for your commits for example.&lt;/p&gt;

&lt;p&gt;Another useful option is located under the &lt;code&gt;extensions&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;IDs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;extension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;want&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;when&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;created.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumeur.vscode-eslint"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one allows specifying extensions that will be automatically installed &lt;em&gt;within&lt;/em&gt; your dev container when it's run for the first time. Yes, you've read that well: these extensions won't pollute your global VS Code configuration, as they'll be available only when your dev container is started and connected to your project. No need to toggle your extensions anymore depending on your active project, no more having a useless C# extension active on your Java project!&lt;/p&gt;

&lt;p&gt;One last essential option, &lt;code&gt;forwardPorts&lt;/code&gt;, allows you to expose ports available from within your dev container and make them accessible by default from your local machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'forwardPorts'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;inside&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;container&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;locally&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"forwardPorts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if you run a web server within your dev container, without port forwarding you won't be able to access it in your browser. Note that it's also possible to add ports to forward on the fly, by clicking on the antenna icon in the status bar, then selecting &lt;code&gt;Add Port&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwotdqj8cyrrok1h7p89v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwotdqj8cyrrok1h7p89v.png" alt="Screenshot of port forwarding panel in VS Code" width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing and updating the environment
&lt;/h2&gt;

&lt;p&gt;Once your container and VS Code configuration is finished, you only have to push these two files in your repository to make them available for your team. And it's one of the big benefits of this approach! To welcome a new developer to your project, you now only have to clone the project, reload VS Code to use the container config and that's it. Take a coffee (or many) while the container is downloading and installing tools on its first run, and your new developer will have a dev environment ready for work, without any effort.&lt;/p&gt;

&lt;p&gt;Even better, if anyone updates the environment configuration, for example by upgrading a tool's version, everyone in the team will see this notification when pulling the changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2bylpy6ldb0zpdvmt6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2bylpy6ldb0zpdvmt6g.png" alt="Screen showing the configuration changed dialog in VS Code" width="752" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You then only have to select &lt;code&gt;Rebuild&lt;/code&gt; to update your environment to the latest version. No more difficulties building a project when coming back from vacation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Where's the catch?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It almost seems too good to be true, so where's the catch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been asking that to myself since I started using it from the first beta, and so far it's been a happy ride. If you wonder about the performance, yes doing everything inside a container is a bit slower than doing it on the host machine, but nothing to worry about. In some cases, you may face disk performance issues especially with lots of small files (looking at you, &lt;code&gt;npm install&lt;/code&gt;) but there are tips to &lt;a href="https://code.visualstudio.com/remote/advancedcontainers/improve-performance?WT.mc_id=javascript-14373-yolasors" rel="noopener noreferrer"&gt;improve your filesystem performance with Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The only issue I had at the beginning was with my git credentials as I'm using SSH keys, but once I've followed &lt;a href="https://code.visualstudio.com/docs/remote/containers?WT.mc_id=javascript-14373-yolasors#_sharing-git-credentials-with-your-container" rel="noopener noreferrer"&gt;the guide to set up an SSH agent&lt;/a&gt; everything went smoothly.&lt;/p&gt;

&lt;p&gt;You can have a look at the &lt;a href="https://code.visualstudio.com/docs/remote/containers?WT.mc_id=javascript-14373-yolasors#_known-limitations" rel="noopener noreferrer"&gt;known limitations&lt;/a&gt;, but so far I've yet to have any issue with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;This article is only a brief overview of what's possible with these tools, and of course, you can push it way further if needed, like creating a dev environment using multiple containers for example. You can have a look at &lt;a href="https://code.visualstudio.com/remote/advancedcontainers/overview?WT.mc_id=javascript-14373-yolasors" rel="noopener noreferrer"&gt;the documentation for more advanced scenarios&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Going further, you could still argue that you have to install VS Code with the remote extension and a Docker-compatible runtime to make this work. It's also possible to get rid of that, using for example &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;GitHub Codespace&lt;/a&gt; which makes use of the exact same tools and configuration to provide a cloud-based dev environment, accessible from any browser, without having to install anything (but a browser) on your machine.&lt;/p&gt;




&lt;p&gt;Follow me on &lt;a href="http://twitter.com/sinedied" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, I would be happy to discuss and take your suggestions!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>tips</category>
    </item>
  </channel>
</rss>
