<?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: Dilum Darshana</title>
    <description>The latest articles on DEV Community by Dilum Darshana (@dilumdarshana).</description>
    <link>https://dev.to/dilumdarshana</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%2F1082764%2F3e4ae257-d005-4a8f-9535-9e42ab577320.jpg</url>
      <title>DEV Community: Dilum Darshana</title>
      <link>https://dev.to/dilumdarshana</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dilumdarshana"/>
    <language>en</language>
    <item>
      <title>Building an AI-Powered FAQ Assistant with Redis 8 and OpenAI</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Sat, 02 Aug 2025 14:32:36 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/building-an-ai-faq-assistant-with-redis-8-and-openai-5o9</link>
      <guid>https://dev.to/dilumdarshana/building-an-ai-faq-assistant-with-redis-8-and-openai-5o9</guid>
      <description>&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built an AI-powered FAQ assistant that delivers instant, context-aware answers to user questions using Redis 8 and OpenAI. It supports semantic search with Redis Vector similarity and intelligently caches responses using RedisJSON to speed up repeated queries. Users can interact with a friendly chat interface that retrieves and returns answers in real time.&lt;/p&gt;

&lt;p&gt;The goal of this project is to demonstrate how Redis can be used as a real-time AI data layer to power conversational interfaces, specifically for use cases like FAQs, support bots, and knowledge assistants.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Note that the cache duration is set to 15 mins due to the limited storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Live App: &lt;a href="https://master.d15ripqxac0b7u.amplifyapp.com" rel="noopener noreferrer"&gt;https://master.d15ripqxac0b7u.amplifyapp.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Initial page load:&lt;/em&gt;&lt;br&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%2Fp79628gfzs4n681zk0xa.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%2Fp79628gfzs4n681zk0xa.png" alt="QnA initial load" width="791" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Response from app:&lt;/em&gt;&lt;br&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%2Fta88er52qur3jrgixwy9.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%2Fta88er52qur3jrgixwy9.png" alt="Ask Question" width="693" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With cached data:&lt;/em&gt;&lt;br&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%2F0p8phpw64kcvdtyh4cit.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%2F0p8phpw64kcvdtyh4cit.png" alt="QnA with Cached data" width="764" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;From Redis Insight&lt;/em&gt;&lt;br&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%2Fhmrc6v73m7969wg7tfty.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%2Fhmrc6v73m7969wg7tfty.png" alt="Redis Insight" width="670" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/dilumdarshana/ai-faq-memory-assistant" rel="noopener noreferrer"&gt;https://github.com/dilumdarshana/ai-faq-memory-assistant&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Redis 8
&lt;/h2&gt;

&lt;p&gt;Redis 8 was the core engine that powered both performance and intelligence in this project. Here’s how I used its features:&lt;/p&gt;

&lt;p&gt;Redis Stack (RedisJSON + RediSearch + Vector Similarity Search):&lt;br&gt;
I stored FAQ documents in Redis using the JSON data type. Each FAQ includes fields like question, answer, source, and a createdAt timestamp. The documents are indexed for full-text and semantic search using FT.CREATE.&lt;/p&gt;

&lt;p&gt;Vector Search for Semantic Understanding:&lt;br&gt;
Each question was embedded into a 1536-dimensional vector using OpenAI's embedding model. These vectors were stored in Redis and indexed using the HNSW algorithm. When a user asks a question, the assistant searches Redis using vector similarity to retrieve the most relevant context.&lt;/p&gt;

&lt;p&gt;Intelligent Caching with RedisJSON:&lt;br&gt;
Before sending a user query to the LLM, I check if a cached answer already exists for a similar query using Redis. This reduces token usage and cost while improving response time.&lt;/p&gt;

&lt;p&gt;FT.SEARCH Sorting by Date:&lt;br&gt;
For the "Recently Asked Questions" section, I used FT.SEARCH with sorting on the createdAt field to show the latest activity. (15 mins cache duration)&lt;/p&gt;

&lt;p&gt;Redis not only made the solution real-time and scalable, but it also gave me a flexible and powerful way to combine structured, vector, and text search in one place.&lt;/p&gt;

&lt;p&gt;Design:&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%2Fy9s3y5ugvhai2yko4j7n.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%2Fy9s3y5ugvhai2yko4j7n.png" alt="System Design" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This project shows how Redis 8 can be a game-changer for real-time AI applications. Whether you're building FAQ bots, search interfaces, or RAG-based assistants, Redis offers a powerful and developer-friendly solution.&lt;/p&gt;

&lt;p&gt;Tech Stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis 8 (Vector Store, RediSearch, RedisJSON)&lt;/li&gt;
&lt;li&gt;OpenAI (gpt-4o, embeddings)&lt;/li&gt;
&lt;li&gt;Next.js (LangChain, TypeScript)&lt;/li&gt;
&lt;li&gt;pnpm&lt;/li&gt;
&lt;li&gt;AWS Amplify&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redischallenge</category>
      <category>devchallenge</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Self-Host n8n and Access It from Anywhere</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Mon, 28 Jul 2025 18:17:12 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/how-to-self-host-n8n-and-access-it-from-anywhere-2og5</link>
      <guid>https://dev.to/dilumdarshana/how-to-self-host-n8n-and-access-it-from-anywhere-2og5</guid>
      <description>&lt;p&gt;Hello devs,&lt;/p&gt;

&lt;p&gt;If you are building workflows and automations using n8n, you have probably thought:&lt;/p&gt;

&lt;p&gt;“Can I run this on my own server and access it from anywhere?”&lt;/p&gt;

&lt;p&gt;The answer is yes — and in this guide, I'll show you how to self-host n8n with Docker, securely expose it using ngrok, and run your automations from anywhere in the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing and Running n8n Locally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running n8n with Docker is the easiest and most consistent way to set it up locally. It keeps your environment isolated, reproducible, and production-ready. No need to worry about system dependencies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a directory for n8n:
&lt;/li&gt;
&lt;/ol&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;&lt;span class="nb"&gt;mkdir &lt;/span&gt;n8n-local &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;n8n-local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a docker-compose.yml file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.n8n.io/n8nio/n8n&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-n8n-workflow&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5678:5678"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# - DOMAIN_NAME=example.com&lt;/span&gt;
      &lt;span class="c1"&gt;# - SUBDOMAIN=n8n&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_HOST=n8n.example.com&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_PROTOCOL=https&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_PORT=5678&lt;/span&gt;
      &lt;span class="c1"&gt;# - NODE_ENV=production&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GENERIC_TIMEZONE=${GENERIC_TIMEZONE}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_SECURE_COOKIE=false&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_EDITOR_BASE_URL=&amp;lt;ngrok url&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true&lt;/span&gt;
      &lt;span class="c1"&gt;# - WEBHOOK_URL=&amp;lt;ngrok url&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_DEFAULT_BINARY_DATA_MODE=filesystem&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./n8n_data:/home/node/.n8n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create .env file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GENERIC_TIMEZONE=Asia/Colombo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Start container&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: There can be a user ownership issue in the n8n_data folder. Run the following command from the folder where container running,&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;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 ./n8n_data
&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;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test from local&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Open your browser and navigate to:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:5678" rel="noopener noreferrer"&gt;http://localhost:5678&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Complete the registration and survey (if prompted), and you’ll land on the n8n dashboard:&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%2Fqboj3tiuk0zmf0q34564.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%2Fqboj3tiuk0zmf0q34564.png" alt="n8n dashbaord" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up ngrok&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let’s expose your local n8n instance to the internet using ngrok.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Signup at ngrok&lt;br&gt;
&lt;a href="https://dashboard.ngrok.com/signup" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/signup&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install ngrok&lt;br&gt;
Follow the instructions for your OS:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://dashboard.ngrok.com/get-started/setup/macos" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/get-started/setup/macos&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add auth token
&lt;/li&gt;
&lt;/ol&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;ngrok config add-authtoken xxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your auth token can be found at: &lt;a href="https://dashboard.ngrok.com/get-started/your-authtoken" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/get-started/your-authtoken&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start a tunnel to port 5678
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http &lt;span class="nt"&gt;--url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xxxxx.ngrok-free.app 5678
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ngrok will provide a public HTTPS domain like &lt;a href="https://xxxxx.ngrok-free.app" rel="noopener noreferrer"&gt;https://xxxxx.ngrok-free.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Binding everything together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now it’s time to update your Docker setup with the ngrok URL.&lt;/p&gt;

&lt;p&gt;We need to stop the existing docker container, if it is running,&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;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the environment variables with the ngrok domain:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;n8n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.n8n.io/n8nio/n8n&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-n8n-workflow&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5678:5678"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# - DOMAIN_NAME=example.com&lt;/span&gt;
      &lt;span class="c1"&gt;# - SUBDOMAIN=n8n&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_HOST=n8n.example.com&lt;/span&gt;
      &lt;span class="c1"&gt;# - N8N_PROTOCOL=https&lt;/span&gt;
      &lt;span class="c1"&gt;# - NODE_ENV=production&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_PORT=5678&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GENERIC_TIMEZONE=${GENERIC_TIMEZONE}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_SECURE_COOKIE=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_EDITOR_BASE_URL=xxxxx.ngrok-free.app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WEBHOOK_URL=xxxxx.ngrok-free.app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;N8N_DEFAULT_BINARY_DATA_MODE=filesystem&lt;/span&gt;

    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./n8n_data:/home/node/.n8n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start container again:&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;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Visit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xxxxx.ngrok-free.app" rel="noopener noreferrer"&gt;https://xxxxx.ngrok-free.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have full access to your local n8n instance from anywhere in the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Self-hosting n8n with Docker and exposing it using ngrok is a powerful way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rapidly test webhooks and integrations&lt;/li&gt;
&lt;li&gt;Run automations privately and securely&lt;/li&gt;
&lt;li&gt;Avoid deploying to cloud platforms for quick experiments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup gives you the flexibility to develop and demo from anywhere — perfect for both learning and production prototypes. You can always enhance it later with a custom domain, HTTPS via Let's Encrypt, or move to a VPS if needed.&lt;/p&gt;

&lt;p&gt;If you're exploring automation workflows or integrating n8n into your stack, give this setup a try — and let me know how it goes!&lt;/p&gt;

&lt;p&gt;Cheers...&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>docker</category>
      <category>automation</category>
      <category>ngrok</category>
    </item>
    <item>
      <title>Build Visual Workflows with n8n and Automate Everything</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Mon, 14 Jul 2025 06:00:45 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/build-visual-workflows-with-n8n-and-automate-everything-2hk3</link>
      <guid>https://dev.to/dilumdarshana/build-visual-workflows-with-n8n-and-automate-everything-2hk3</guid>
      <description>&lt;p&gt;Hello devs,&lt;/p&gt;

&lt;p&gt;As a full-stack developer working mostly with JavaScript, I often build internal tools and data pipelines. I found myself writing a lot of boilerplate Node.js code to glue together APIs and databases — until I discovered n8n.&lt;/p&gt;

&lt;p&gt;This blog is my personal reference and a shared guide for anyone looking to get serious about workflow automation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing and Running n8n Locally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Running n8n with Docker is the easiest and most consistent way to set it up locally. It keeps your environment isolated, reproducible, and production-ready. No need to worry about system dependencies. But, it is available in Saas as well. &lt;a href="https://app.n8n.cloud/login" rel="noopener noreferrer"&gt;https://app.n8n.cloud/login&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;docker-compose.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: my-n8n-workflow
    restart: always
    ports:
      - "5678:5678"
    environment:
      # - DOMAIN_NAME=example.com
      &lt;span class="c"&gt;# - SUBDOMAIN=n8n&lt;/span&gt;
      &lt;span class="c"&gt;# - N8N_HOST=n8n.example.com&lt;/span&gt;
      &lt;span class="c"&gt;# - N8N_PROTOCOL=https&lt;/span&gt;
      &lt;span class="c"&gt;# - N8N_PORT=5678&lt;/span&gt;
      &lt;span class="c"&gt;# - WEBHOOK_URL=https://n8n.example.com&lt;/span&gt;
      &lt;span class="c"&gt;# - NODE_ENV=production&lt;/span&gt;
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - N8N_SECURE_COOKIE=false
    volumes:
      - ./n8n_data:/home/node/.n8n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.env&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;GENERIC_TIMEZONE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Colombo

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: There can be a user ownership issue in the n8n_data folder. Run the following command from the folder where container running,&lt;/em&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 ./n8n_data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, do the composer up,&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;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well, n8n should be runs on &lt;a href="http://localhost:5678" rel="noopener noreferrer"&gt;http://localhost:5678&lt;/a&gt;. Fill the user registration to create an account, then fill the survey (as of now) to go into the dashboard.&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%2Fshckskehdbmijtjua5pk.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%2Fshckskehdbmijtjua5pk.png" alt="n8n dashboard" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;n8n UI and Core Concepts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workflow Canvas: The drag-and-drop editor where you build automation flows step by step.&lt;/li&gt;
&lt;li&gt;Nodes: The building blocks of workflows—each node performs a task like sending an email, calling an API, or transforming data.&lt;/li&gt;
&lt;li&gt;Triggers: Nodes that start workflows (e.g., Cron, Webhook, Manual).&lt;/li&gt;
&lt;li&gt;Actions: Nodes that perform operations, like HTTP requests, database inserts, or sending notifications.&lt;/li&gt;
&lt;li&gt;Data Flow: Data moves between nodes in JSON format, and you can map, modify, or branch the flow at each step.&lt;/li&gt;
&lt;li&gt;Function Node: Use JavaScript for advanced logic and data manipulation directly inside n8n.&lt;/li&gt;
&lt;li&gt;Set Node: Quickly add or modify fields without writing code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Building a Real-World Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To understand how triggers, API calls, data transformation, and external services come together in n8n, let’s build a simple real-world workflow.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use Case:&lt;/em&gt; Send a random joke to your email every 5 minutes using Resend&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Workflow:&lt;/em&gt; [Cron Node] → [Fetch Joke (HTTP Request)] → [Send Email (Resend HTTP Request)]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Create a New Workflow&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the n8n editor.&lt;/li&gt;
&lt;li&gt;Click “New Workflow” and give it a name, for example:
Send Joke to Email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Add a Cron Trigger&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click “+” Add Node.&lt;/li&gt;
&lt;li&gt;Search for Schedule Trigger and select the Schedule Trigger Node.&lt;/li&gt;
&lt;li&gt;Configure it to run every 5 minutes, continuously.&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%2F0nisaamhaa9d1ajo91ax.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%2F0nisaamhaa9d1ajo91ax.png" alt="Add a cron trigger" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Fetch a Joke via HTTP Request&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click “+” Add Node.&lt;/li&gt;
&lt;li&gt;Search for HTTP Request.&lt;/li&gt;
&lt;li&gt;Configure it with response format as JSON. URL: &lt;a href="https://icanhazdadjoke.com/" rel="noopener noreferrer"&gt;https://icanhazdadjoke.com/&lt;/a&gt;, method: GET &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%2F85ey8vwou6s0nz9x90r2.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%2F85ey8vwou6s0nz9x90r2.png" alt="Configure http request" width="505" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This should return a json something like this:&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;"joke"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I once lost a banana at court but then I appealed."&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;&lt;strong&gt;Step 4:&lt;/strong&gt; Send the Joke via Email Using Resend&lt;/p&gt;

&lt;p&gt;Now send the joke to email using Resend's email API. To do this, need to create a Resend account and API key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://resend.com" rel="noopener noreferrer"&gt;https://resend.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the Email Node,&lt;/p&gt;

&lt;p&gt;Since n8n doesn’t have a native Resend node, use HTTP Request again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click “+” Add Node.&lt;/li&gt;
&lt;li&gt;Select HTTP Request.&lt;/li&gt;
&lt;li&gt;Configure it,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;URL: &lt;a href="https://api.resend.com/emails" rel="noopener noreferrer"&gt;https://api.resend.com/emails&lt;/a&gt;&lt;br&gt;
Method: POST&lt;br&gt;
Headers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization: Bearer RESEND_API_KEY&lt;/li&gt;
&lt;li&gt;Content-Type: application/json&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%2Frvjebiswikgkxtx14aav.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%2Frvjebiswikgkxtx14aav.png" alt="Config HTTP node" width="476" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send body:
&lt;/li&gt;
&lt;/ul&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;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jokes &amp;lt;onboarding@resend.dev&amp;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;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dilum.dar@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Here's Your Joke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{$json.joke}}"&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;&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%2F1b03i8z0tu76a3r9b2y8.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%2F1b03i8z0tu76a3r9b2y8.png" alt="JSON Body" width="483" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, everything ready for test. The whole workflow looks like this,&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%2Frlngqh7743xgjiifnikd.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%2Frlngqh7743xgjiifnikd.png" alt="Whole workflow" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the workflow manually by clicking “Execute Workflow”. Should receive an email with a joke :)&lt;/p&gt;

&lt;p&gt;If it works, click “Activate” to make it run automatically every 5 minutes.&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%2Fqp0xr4m8blfke8dzkgyv.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%2Fqp0xr4m8blfke8dzkgyv.png" alt="Activate workflow" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
Whether you're automating your daily tasks, integrating multiple services, or building your own AI pipelines—n8n gives you the flexibility of code with the simplicity of a visual editor.&lt;/p&gt;

&lt;p&gt;This was just the beginning.&lt;/p&gt;

&lt;p&gt;Cheers....&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>ai</category>
      <category>workflow</category>
      <category>automation</category>
    </item>
    <item>
      <title>Building My First MCP Server with TypeScript: A Beginner’s Journey</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Mon, 07 Jul 2025 06:16:52 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/building-my-first-mcp-server-with-typescript-a-beginners-journey-1nhb</link>
      <guid>https://dev.to/dilumdarshana/building-my-first-mcp-server-with-typescript-a-beginners-journey-1nhb</guid>
      <description>&lt;p&gt;Hello backend devs,&lt;/p&gt;

&lt;p&gt;As someone deeply interested in LLM-based Agentic workflows, I recently took into a new challenge: building my first Model Context Protocol (MCP) server using TypeScript. This post walks through the process—from zero to a working MCP server—and what I learned along the way. There are many articles in the internet which is explained in Python. Here, I wanted to use my most familiar JavaScript as a programming language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why we need MCP?&lt;/strong&gt;&lt;br&gt;
Extending the capabilities of large language models (LLMs) has become increasingly important. While there are many ways to integrate external tools or services with LLMs, these methods are often ad hoc and lack consistency. When building scalable and reusable solutions, it's crucial to have a standardised way to define and expose tools that LLMs can interact with.&lt;/p&gt;

&lt;p&gt;This is where Model Context Protocol (MCP) comes into the picture. MCP provides a protocol-driven approach to tool integration, making it easier to define, discover, and use external tools in a consistent and structured manner—regardless of the environment or framework.&lt;/p&gt;

&lt;p&gt;If you’re familiar with RAG (Retrieval-Augmented Generation), you can think of MCP as a more secure and controllable alternative for tool execution. With RAG, the model often has access to unstructured or semi-structured data sources. In contrast, with MCP, tools are invoked by an MCP client, and only the results are passed back to the LLM. This provides a more secure and sandboxed execution flow—keeping tool logic and sensitive operations isolated from the LLM.&lt;/p&gt;

&lt;p&gt;In short, MCP helps extend the power of LLMs through a common standard, promoting reusability, interoperability, and better developer experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technical approach&lt;/strong&gt;&lt;br&gt;
To implement my first MCP server, I chose the official @modelcontextprotocol/sdk npm package. This SDK provides a clean and TypeScript-friendly interface to build MCP-compliant servers and tools. It includes everything needed to handle standard MCP request types, define tools with schema validation, and manage communication over stdin/stdout (which is how many LLM runtimes expect to interact with tools).&lt;/p&gt;

&lt;p&gt;The SDK abstracts away many of the low-level details, letting you focus on defining what your tools do, rather than how they are wired into the protocol. This made it a great choice for a first-time MCP implementation.&lt;/p&gt;

&lt;p&gt;While I went with the official SDK for simplicity and clarity, there are other approaches available too.&lt;/p&gt;

&lt;p&gt;[Documentation]&lt;a href="https://github.com/modelcontextprotocol/typescript-sdk" rel="noopener noreferrer"&gt;https://github.com/modelcontextprotocol/typescript-sdk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up the project&lt;/strong&gt;&lt;br&gt;
To keep things simple and focused, I used a minimal folder structure and pnpm as my package manager. Since this was a TypeScript-based CLI-style MCP server, I didn’t need a complex build system or runtime framework.&lt;/p&gt;

&lt;p&gt;Here's the basic folder structure I followed:&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%2F0e5cnghs2gxadpkkr3fs.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%2F0e5cnghs2gxadpkkr3fs.png" alt="Folder structure" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;package.json&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;"name"&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-currency-converter"&lt;/span&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;"1.0.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;"description"&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;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bin"&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;"mcp-currency-converter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.js"&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;"scripts"&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;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc &amp;amp;&amp;amp; node -e &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;require('fs').chmodSync('dist/index.js', '755')&lt;/span&gt;&lt;span class="se"&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;"inspector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpx @modelcontextprotocol/inspector node ./dist/index.js"&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;"keywords"&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;"author"&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;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packageManager"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm@10.7.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;"dependencies"&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;"@modelcontextprotocol/sdk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.15.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;"zod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.25.67"&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;"devDependencies"&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;"@types/node"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.8.3"&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;tsconfig.json&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;"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;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CommonJS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&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;"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;"./dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&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"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&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="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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&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="nl"&gt;"forceConsistentCasingInFileNames"&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="s2"&gt;"src/**/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&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;"node_modules"&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;&lt;strong&gt;Tool&lt;/strong&gt;&lt;br&gt;
Tools are the heart of an MCP server. Each tool exposes a specific piece of functionality that the LLM can invoke via the MCP client. You can register multiple tools in the same MCP server, each serving different—but often related—business purposes.&lt;/p&gt;

&lt;p&gt;In my case, I implemented two tools to demonstrate a basic currency conversion use case:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Currency Converter – Converts a given amount from one currency to another.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;List Available Currencies – Returns a list of supported currency codes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both tools are defined using the @modelcontextprotocol/sdk along with zod for schema validation.&lt;/p&gt;

&lt;p&gt;Here’s how they are implemented:&lt;/p&gt;

&lt;p&gt;types.ts&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;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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MCPTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZodTypeAny&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&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;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TInput&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;tools/convertCurrency.ts&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="cm"&gt;/**
 * Convert given amount from one currency to another using the Free Currency API.
 */&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MCPTool&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;../types&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fromCurrency&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="nf"&gt;string&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;The currency to convert from (e.g., USD, EUR)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;toCurrency&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="nf"&gt;string&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;The currency to convert to (e.g., USD, EUR)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;amount&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="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;positive&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;The amount to convert&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertCurrencyTool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MCPTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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;convert-currency&lt;/span&gt;&lt;span class="dl"&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;Converts an amount from one currency to another&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&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;fromCurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toCurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;any&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;currencyFinderKey&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;FREE_CURRENCY_KEY&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;currencyFinderKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing FREE_CURRENCY_KEY&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="s2"&gt;`Converting &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&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;fromCurrency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toCurrency&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;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;`https://api.freecurrencyapi.com/v1/latest?apikey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currencyFinderKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;base_currency=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fromCurrency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;currencies=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toCurrency&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;data&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exchangeRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="nx"&gt;toCurrency&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;exchangeRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid exchange rate&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;convertedAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exchangeRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;amount&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="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="s2"&gt;`Converted &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&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;fromCurrency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;toCurrency&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;convertedAmount&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;toCurrency&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tools/listCurrencies.ts&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="cm"&gt;/**
 * List avaible currencies tool. Available currencies are fetched from freecurrencyapi.com
 */&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MCPTool&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;../types&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Schema&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;

&lt;span class="c1"&gt;// List currencies tool&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listCurrenciesTool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MCPTool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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;list-currencies&lt;/span&gt;&lt;span class="dl"&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;Lists all supported currencies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&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;currencyFinderKey&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;FREE_CURRENCY_KEY&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;currencyFinderKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing FREE_CURRENCY_KEY&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Listing supported currencies&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`https://api.freecurrencyapi.com/v1/currencies?apikey=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currencyFinderKey&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;data&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="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="s2"&gt;`Supported currencies: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Wiring Up the MCP Server&lt;/strong&gt;&lt;br&gt;
Now that the tools are ready, the next step is to create the MCP server and integrate those tools using the @modelcontextprotocol/sdk.&lt;/p&gt;

&lt;p&gt;In this setup, I defined a simple MCP server using the StdioServerTransport, which allows tools to be called locally through stdin and stdout—ideal for testing or local agent interactions.&lt;/p&gt;

&lt;p&gt;After initialising the server, I registered both tools so they can be listed and executed via standard MCP requests.&lt;/p&gt;

&lt;p&gt;Here is the implementation:&lt;/p&gt;

&lt;p&gt;index.ts&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="cp"&gt;#!/usr/bin/env node
&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;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;StdioServerTransport&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/stdio.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;convertCurrencyTool&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;./tools/convertCurrency.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;listCurrenciesTool&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;./tools/listCurrencies.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;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;currencyConverter&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="c1"&gt;// Tool no: 1&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;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;convertCurrencyTool&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;span class="nx"&gt;convertCurrencyTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;convertCurrencyTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;convertCurrencyTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Tool no: 2&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;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;listCurrenciesTool&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;span class="nx"&gt;listCurrenciesTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;listCurrenciesTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;listCurrenciesTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&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;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;StdioServerTransport&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing it&lt;/strong&gt;&lt;br&gt;
That is it! Now it’s time to test whether everything works as expected.&lt;/p&gt;

&lt;p&gt;For this, I used the official modelcontextprotocol/inspector tool. It provides a simple way to inspect and interact with your local MCP server via a CLI interface.&lt;/p&gt;

&lt;p&gt;I added it as a dependency and defined a script in package.json for convenience. To run the test, navigate into your project directory and execute 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;&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm inspector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will launch the inspector (See the console output for the URL), which connects to your MCP server and lists the available tools. You can then invoke each tool, provide input, and view the structured output—all directly from your terminal.&lt;/p&gt;

&lt;p&gt;It’s a great way to quickly validate your server’s setup and ensure tool execution is working correctly.&lt;/p&gt;

&lt;p&gt;In here, we can type different inputs to see how it works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Connect the Inspector&lt;br&gt;
Click the "Connect" button in the bottom left corner of the UI.&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%2F5ux8becik0ih3a6sa1o8.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%2F5ux8becik0ih3a6sa1o8.png" alt="Inspector connect" width="384" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Load and Test Tools&lt;br&gt;
After connecting, go to the “Tools” tab.&lt;/p&gt;

&lt;p&gt;Click on “List Tools” to load the available tools from your MCP server.&lt;/p&gt;

&lt;p&gt;You can now select a tool (e.g., convertCurrency or listCurrencies), enter the required parameters, and run it.&lt;/p&gt;

&lt;p&gt;If everything is working correctly, you should receive a valid and expected response from your tool.&lt;/p&gt;

&lt;p&gt;💡 Note: If your MCP server relies on any environment variables (e.g., API keys), make sure to set them inside the Inspector UI before running the tools.&lt;br&gt;
In my case, I had to set FREE_CURRENCY_KEY for the currency conversion API to work properly.&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%2Fg334nuxc5h8xlojloz5d.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%2Fg334nuxc5h8xlojloz5d.png" alt="Inspector response" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MCP encourages clean, reusable, and structured tool development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The @modelcontextprotocol/sdk abstracts a lot, but understanding what's happening under the hood helps when debugging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Inspector tool is incredibly helpful—but only if the server and tools are defined correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Type safety and schema validation are your best friends—don’t skip on them!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
Building my first MCP server in TypeScript was a rewarding experience. It not only helped me understand how to expose tools to LLMs in a structured way, but also gave me a glimpse into the future of AI integrations—where standards like MCP can bring consistency, security, and reusability to the developer ecosystem.&lt;/p&gt;

&lt;p&gt;The @modelcontextprotocol/sdk made it approachable, and using tools like the Inspector helped validate everything with confidence.&lt;/p&gt;

&lt;p&gt;If you're working with LLMs and looking for a clean way to plug in external capabilities, I highly recommend exploring MCP. Start small, build a simple tool, and see how easily it can scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's Next?&lt;/strong&gt;&lt;br&gt;
In my next post, I'll walk through how to use the MCP server from code using an MCP client—so you can see how to invoke tools programmatically and integrate them into your own AI workflows.&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>typescript</category>
    </item>
    <item>
      <title>API Documentation for NestJS</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Mon, 26 May 2025 03:06:20 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/api-documentation-for-nestjs-3cnk</link>
      <guid>https://dev.to/dilumdarshana/api-documentation-for-nestjs-3cnk</guid>
      <description>&lt;p&gt;This time we are going to learn about how to create API documentation for NestJS application using Swagger. There are many ways in the world, let's focus on Swagger which is NestJS official preference.&lt;/p&gt;

&lt;p&gt;First, install the dependency,&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;pnpm add @nestjs/swagger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, initialise the Swagger in bootstrap which is main.ts file,&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;SwaggerModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DocumentBuilder&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;@nestjs/swagger&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Set up Swagger documentation for the API&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swaggerConfig&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;DocumentBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Taskify API documentation&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="nf"&gt;setDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Taskify API documentation&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="nf"&gt;setVersion&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&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="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&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="nf"&gt;build&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;documentFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SwaggerModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDocument&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="nx"&gt;swaggerConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;SwaggerModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api-docs&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;documentFactory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start the application and listen on the specified port&lt;/span&gt;
  &lt;span class="k"&gt;await&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="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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, that's it. &lt;a href="http://localhost:3000/api-docs" rel="noopener noreferrer"&gt;http://localhost:3000/api-docs&lt;/a&gt; will be showing the elegant documentation structure with separate sections. &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%2F3i01dtvkwpa79uh6z2gi.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%2F3i01dtvkwpa79uh6z2gi.png" alt="Swagger sections" width="506" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sections are automatically split based on the controller name. It is possible to give a custom name though. Use &lt;code&gt;ApiTags&lt;/code&gt; decorator&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;ApiTags&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;@nestjs/swagger&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="nd"&gt;ApiTags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task APIs&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="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;task&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;class&lt;/span&gt; &lt;span class="nc"&gt;TaskController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;taskService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskService&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;/div&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%2F71r4djpr4iradnbo5aun.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%2F71r4djpr4iradnbo5aun.png" alt="Custom section name" width="277" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, the API definitions are empty. We need to improve further. Let's do it.&lt;/p&gt;

&lt;p&gt;The schema and example section take from the DTO file. Im my example, create task API and createTaskDTO which is validate the task create inputs. We can use this file to improve the documentation using ApiProperty decorator.&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="c1"&gt;// create-task.dto.ts file&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;Priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TaskStatus&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;@prisma/client&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;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IsString&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;class-validator&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;ApiProperty&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;@nestjs/swagger&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;class&lt;/span&gt; &lt;span class="nc"&gt;CreateTaskDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&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;Title of the task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Complete the project report&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="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&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;Description of the task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Write a detailed report on the project progress&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="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&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;Priority level of the task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Priority&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="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&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;Status of the task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TODO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskStatus&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="nd"&gt;IsString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;IsNotEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskStatus&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;&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%2F17mqarmu35jd1pnm84vd.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%2F17mqarmu35jd1pnm84vd.png" alt="Example and schema" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, the response section in the API can be filled with ApiResponse decorator from the controller file. Note that the CreateTaskResponseDto type. This should be a DTO for the response. Let's modify the create-task.dto file for that,&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="c1"&gt;// create-task.dto.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateTaskResponseDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Priority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiProperty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TaskStatus&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, need to call the ApiResponse decorator from the controller&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="c1"&gt;// task.controller.ts file&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATED&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;Task created successfully&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;CreateTaskResponseDto&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger response documentation.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HttpCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Sets the HTTP status code to 201 (Created).&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ResponseInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Intercepts the response to format it consistently.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Handles POST requests to the /task endpoint.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CurrentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&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;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task created successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&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;/div&gt;



&lt;p&gt;Additionally, if we want to add description for each API, can be used ApiOperation decorator from the controller. Example,&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiOperation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some description about this API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger documentation for the endpoint.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATED&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;Task created successfully&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;CreateTaskResponseDto&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger response documentation.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HttpCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Sets the HTTP status code to 201 (Created).&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ResponseInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Intercepts the response to format it consistently.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Handles POST requests to the /task endpoint.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CurrentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&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;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task created successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&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;/div&gt;



&lt;p&gt;It is possible to add multiple response types, example error responses. Just use the different decorators as bellow,&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiOperation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create a new task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger documentation for the endpoint.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiCreatedResponse&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;Task created successfully&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;CreateTaskResponseDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger response documentation.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiBadRequestResponse&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;Bad Request&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger error response documentation.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ApiUnauthorizedResponse&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;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// Swagger error response documentation.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HttpCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CREATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Sets the HTTP status code to 201 (Created).&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;UseInterceptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ResponseInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Intercepts the response to format it consistently.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Handles POST requests to the /task endpoint.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createTask&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;CurrentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&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;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;taskService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTaskDto&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task created successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;task&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;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From my perspective, this approach allow us to create useful API documentation with minimal effort. The big advantage of having such a documentation is improve team collaboration specially for front-end teams.&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>swagger</category>
      <category>api</category>
      <category>documentation</category>
    </item>
    <item>
      <title>How to Use Redis with Replication and Automatic Failover</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Wed, 09 Apr 2025 17:44:24 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/how-to-use-redis-with-replication-and-automatic-failover-1991</link>
      <guid>https://dev.to/dilumdarshana/how-to-use-redis-with-replication-and-automatic-failover-1991</guid>
      <description>&lt;p&gt;Redis is an in memory storing giant in the industry. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why so fast?
&lt;/h2&gt;

&lt;p&gt;Redis store data in the memory. In general, reading and writing data from memory faster than reading/writing data from the disk. Redis store everything as a key/value pairs in a hash map.&lt;/p&gt;

&lt;p&gt;Most Redis commands are operate in O(1) or O(log N) time complexity regardless of the size of the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redis architecture
&lt;/h2&gt;

&lt;p&gt;Redis uses Single-Threaded Event Loop architecture. In general, Redis can handle 10,000 concurrent requests. This can be change using redis.conf &lt;em&gt;maxclients&lt;/em&gt; directive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistence of data?
&lt;/h2&gt;

&lt;p&gt;Since, Redis uses memory to store data, data will be lost upon the server crash. Redis provides two approaches to persistence the data&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;RDB - Redis Database&lt;br&gt;
In this way, snapshot will be written to the disk periodically. Can be restore data form them. But, it has few cons as well. Until data restore complete, there will be a significant latency. This would not ideal in the production. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AOF (Append Only File)&lt;br&gt;
In this way, AOF persistence logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset. There is a cons here, recovery time will be higher, when the file become larger and larger over the time&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The recommended approach is use the combination of RDB and AOF&lt;/p&gt;

&lt;h2&gt;
  
  
  Read Replicas (High availability)
&lt;/h2&gt;

&lt;p&gt;Use read replicas would be ideal in this case. It has primary node and multiple read replicas. Once the primary goes down, one of the replica start acting as new primary node (Redis Sentinel).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/latest/operate/oss_and_stack/management/replication" rel="noopener noreferrer"&gt;Read More...&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Redis use cases
&lt;/h2&gt;

&lt;p&gt;Redis can use in 3 different main purposes,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use as a Cache&lt;/li&gt;
&lt;li&gt;Use as a Database&lt;/li&gt;
&lt;li&gt;Use as a Message Broker&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Redis core data types
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Strings – Simple key-value pairs.&lt;/li&gt;
&lt;li&gt;Lists – Doubly linked lists.&lt;/li&gt;
&lt;li&gt;Hashes – Key-value maps.&lt;/li&gt;
&lt;li&gt;Sets – Unordered unique elements.&lt;/li&gt;
&lt;li&gt;Sorted Sets – Ordered sets with scores.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Spine Redis instance
&lt;/h2&gt;

&lt;p&gt;Can use Docker to spine up a Redis server in a minute with different features.&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;# start redis server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-server &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis
&lt;span class="c"&gt;# -d: Detached mode&lt;/span&gt;
&lt;span class="c"&gt;# -p: port mapping&lt;/span&gt;

&lt;span class="c"&gt;# restart and stop redis server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker restart redis-server
&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop redis-server

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Most needed command
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# go inside docker container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container_id&amp;gt; bash

&lt;span class="c"&gt;# go inside redis&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; &amp;lt;host&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;port&amp;gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &amp;lt;password&amp;gt;
&lt;span class="c"&gt;# eg. redis-cli -h 192.168.1.8 -p 6379 -a xxxx&lt;/span&gt;

&lt;span class="c"&gt;# OR without go inside,&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-server redis-cli GET username

&lt;span class="c"&gt;# set string&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;SET username &lt;span class="s2"&gt;"Apache"&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;GET username
&lt;span class="nv"&gt;$ &lt;/span&gt;DEL username
&lt;span class="nv"&gt;$ &lt;/span&gt;EXPIRE username 60 &lt;span class="c"&gt;# expire item in 60 seconds&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;TTL username &lt;span class="c"&gt;# check TTL of key. xx seconds left&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;FLUSHDB &lt;span class="c"&gt;# delete all keys in current DB&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;FLUSHALL &lt;span class="c"&gt;# delete all keys in all DB&lt;/span&gt;

&lt;span class="c"&gt;# Hashes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;HSET user:1000 name &lt;span class="s2"&gt;"Bob"&lt;/span&gt; age 23
&lt;span class="nv"&gt;$ &lt;/span&gt;HGET user:1000 name &lt;span class="c"&gt;# Bob&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;HGET user:1000 age &lt;span class="c"&gt;# 23&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;HGETALL user:1000

&lt;span class="c"&gt;# Lists (ordered items)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;LPUSH tasks &lt;span class="s2"&gt;"task1"&lt;/span&gt; &lt;span class="s2"&gt;"task2"&lt;/span&gt; &lt;span class="c"&gt;# push item to left&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;LPOP tasks &lt;span class="c"&gt;# remove item from left&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;RPUSH tasks &lt;span class="s2"&gt;"task3"&lt;/span&gt; &lt;span class="c"&gt;# push item to right&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;LRANGE tasks 0 &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="c"&gt;# list all items&lt;/span&gt;

&lt;span class="c"&gt;# Sets (unordered items)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;SADD colors &lt;span class="s2"&gt;"red"&lt;/span&gt; &lt;span class="c"&gt;# add item&lt;/span&gt;

&lt;span class="c"&gt;# monitor redis activities&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;monitor

&lt;span class="c"&gt;# docker create new network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create redis-net

&lt;span class="c"&gt;# inspect docker network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect redis-net

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Persistence of data (RDB/AOF). How to do?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# enable snapshots (RDB)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-rdb &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/redis-data:/data redis redis-server &lt;span class="nt"&gt;--save&lt;/span&gt; 60 1

&lt;span class="c"&gt;# -v:  mount redis volume to host /redis-data folder.&lt;/span&gt;
&lt;span class="c"&gt;# redis-server: this runs the redis-server executable inside the container. we use this to pass additional options&lt;/span&gt;
&lt;span class="c"&gt;# -save 60 1: saves to disk if 1+ key changes in 60 seconds.&lt;/span&gt;

&lt;span class="c"&gt;# enable append only (AOF)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-rdb &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/redis-data:/data redis redis-server &lt;span class="nt"&gt;--appendonly&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redis replicas. How to do?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create docker network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create redis-net

&lt;span class="c"&gt;# make sure network is there&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network list

&lt;span class="c"&gt;# create master node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-master &lt;span class="nt"&gt;--network&lt;/span&gt; redis-net &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis redis-server &lt;span class="nt"&gt;--appendonly&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# create replica 01&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-replica1 &lt;span class="nt"&gt;--network&lt;/span&gt; redis-net &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6380:6379 redis redis-server &lt;span class="nt"&gt;--replicaof&lt;/span&gt; redis-master 6379 &lt;span class="nt"&gt;--appendonly&lt;/span&gt; &lt;span class="nb"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Note: both redis nodes must be in the same docker network, in order to communicate each other&lt;/span&gt;

&lt;span class="c"&gt;# test everything works:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-master redis-cli &lt;span class="nb"&gt;set &lt;/span&gt;username &lt;span class="s2"&gt;"Bob"&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-replica1 redis-cli get username

&lt;span class="c"&gt;# check information about the node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-master redis-cli info replication

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

&lt;/div&gt;



&lt;p&gt;Can be used docker compose file to spine all in one command (docker-compose.yml)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  redis-master:
    image: redis
    container_name: redis-master
    restart: always
    volumes:
      - redis_master:/data
    ports:
      - 6379:6379

  redis-replica1:
    image: redis
    container_name: redis-replica1
    restart: always
    volumes:
      - redis_slave:/data
    ports:
      - 6479:6379
    command: redis-server --slaveof redis-master 6379
volumes:
  redis_master:
  redis_replica1:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run from command line&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;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redis automatic failover (Sentinel). How to do?
&lt;/h2&gt;

&lt;p&gt;Redis does not handle failover by default. We need to config for it. Sentinel is the Redis concept we can use to handle failover along with replicas created above.&lt;/p&gt;

&lt;p&gt;Create sentinel config file (sentinel.conf) in the current working directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;port 26379
sentinel monitor mymaster redis-master 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;mymaster → Name of the monitored master.&lt;br&gt;
redis-master 6379 → IP and port of the master.&lt;br&gt;
2 → The number of Sentinels that must agree before a failover occurs.&lt;br&gt;
down-after-milliseconds 5000 → Time (5 sec) before Sentinel marks the master as down.&lt;br&gt;
failover-timeout 10000 → Time (10 sec) before failing over.&lt;br&gt;
parallel-syncs 1 → Number of replicas syncing simultaneously.&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;# create sentinel conatiner&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-sentinel &lt;span class="nt"&gt;--network&lt;/span&gt; redis-net &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 26379:26379 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:/etc/redis redis redis-server /etc/redis/sentinel.conf &lt;span class="nt"&gt;--sentinel&lt;/span&gt;

&lt;span class="c"&gt;# verify sentinel uses the correct master node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; redis-sentinel redis-cli &lt;span class="nt"&gt;-p&lt;/span&gt; 26379 SENTINEL get-master-addr-by-name mymaster
&lt;span class="c"&gt;# this should return either redis-master or ip of the master container&lt;/span&gt;

&lt;span class="c"&gt;# check the actual role of the node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-master redis-cli info replication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to test the failover&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;# stop the master node&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop redis-master

&lt;span class="c"&gt;# check the role of replica&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-replica1 redis-cli info replication
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redis use cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;As a rate limiter&lt;br&gt;
Leaky bucket algorithm&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As a distribution lock&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rank/Leaderbord&lt;br&gt;
Using sorted list data type&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Now a days, we have plenty of managed services available out there, rather manually setting up everything. Example, AWS ElastiCache. But, I think it is better to know what is going on behind the scenes.&lt;/p&gt;

&lt;p&gt;Add anything I missed here....&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>redis</category>
      <category>cache</category>
      <category>failover</category>
    </item>
    <item>
      <title>Connecting Database with NestJS Using Prisma ORM</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Sun, 06 Apr 2025 07:18:48 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/connecting-database-with-nestjs-using-prisma-orm-5f7h</link>
      <guid>https://dev.to/dilumdarshana/connecting-database-with-nestjs-using-prisma-orm-5f7h</guid>
      <description>&lt;p&gt;Hello Backend devs...&lt;/p&gt;

&lt;p&gt;Time to connect NestJS application with a database. There are many databases can be connected with NestJS, including Sql or NoSql types. Choosing a right database is another crucial decision in the application's architecture. Today, let's focus on connect with the PostgreSQL database - a powerful popular relational database.&lt;/p&gt;

&lt;p&gt;Obviously, we can use native drivers to connect with any databases from NestJS app. But, most of time, it is better to use an ORM, since, it is more time efficient and maintainable.&lt;/p&gt;

&lt;p&gt;Today, let's focus on Prisma ORM, one of the most modern and developer-friendly ORMs, to connect PostgreSQL with a NestJS application.&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;Install dependencies&lt;/strong&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="c"&gt;# adding prisma cli&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; prisma

&lt;span class="c"&gt;# prisma client for development&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm add @prisma/client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Initialise Prisma&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This will create prisma folder and .env file in the root&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;pnpx prisma init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fclbj3buh47fs0lvhwvsq.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%2Fclbj3buh47fs0lvhwvsq.png" alt="Prisma Folder Structure" width="223" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;Configurations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Need to update .env file for the database connection.&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="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Migrations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Schema file located at &lt;code&gt;prisma/schema.prisma&lt;/code&gt; file. There are two ways to run migrations here,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Migrations&lt;br&gt;
Mostly use this approach. We create a schema on code, then push the changes to the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Introspection&lt;br&gt;
In here, When already have the database, we can update the schema by pulling it. Command in bellow,&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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;pnpx prisma db pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through the most common approach Migrations. In the first stage, we don't have anything in the database. We need to create it from the sketch. Assume that we need to create a user table. What we have to do is update the schema.prisma file as needed,&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="nx"&gt;generator&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prisma-client-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;datasource&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_URL&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="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;UserType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ADMIN&lt;/span&gt;
  &lt;span class="nx"&gt;USER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;User&lt;/span&gt;       &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;   &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;role_id&lt;/span&gt;    &lt;span class="nx"&gt;Int&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;       &lt;span class="nx"&gt;UserType&lt;/span&gt; &lt;span class="c1"&gt;// custom type with enum&lt;/span&gt;
  &lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;Role&lt;/span&gt;       &lt;span class="nx"&gt;Role&lt;/span&gt;     &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;role_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&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, need to create the first migration 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;# pnpx prisma migrate dev --name &amp;lt;description_of_change&amp;gt;&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will do two actions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create new migration file inside prisma/migrations folder&lt;br&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%2Fop5m3ulu1seghjhxchbi.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%2Fop5m3ulu1seghjhxchbi.png" alt="Migration folder" width="219" height="170"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create tables in the connected database provided in the schema file&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Anyway, we have a option to only create the migration file without execute it,&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;pnpx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; &amp;lt;description_of_change&amp;gt; &lt;span class="nt"&gt;--create-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can execute this any time by,&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;pnpx prisma migrate dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Update table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What if we want to modify existing table or add a new table to the database?&lt;/p&gt;

&lt;p&gt;Modify the prisma.schema file for the new change.&lt;/p&gt;

&lt;p&gt;Best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single logical change/s per migration&lt;/li&gt;
&lt;li&gt;Always, create a migration file for change using &lt;code&gt;prisma migrate dev&lt;/code&gt;. This will re generate the prisma client and create a migration file.&lt;/li&gt;
&lt;li&gt;Review migration without execute it using &lt;code&gt;npx prisma migrate dev --create-only&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Descriptive naming on migration file&lt;/li&gt;
&lt;li&gt;Never edit executed migration files. Create a new one for change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's assume that we want to add optional age filed to the user table. Modify the user model.&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="nx"&gt;generator&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prisma-client-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;datasource&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postgresql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DATABASE_URL&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;model&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;User&lt;/span&gt;       &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;         &lt;span class="nx"&gt;Int&lt;/span&gt;      &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;id&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;autoincrement&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;email&lt;/span&gt;      &lt;span class="nb"&gt;String&lt;/span&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;unique&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;   &lt;span class="nb"&gt;String&lt;/span&gt;
  &lt;span class="nx"&gt;age&lt;/span&gt;        &lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="c1"&gt;// ? mark indicate that optional&lt;/span&gt;
  &lt;span class="nx"&gt;role_id&lt;/span&gt;    &lt;span class="nx"&gt;Int&lt;/span&gt;
  &lt;span class="nx"&gt;created_at&lt;/span&gt; &lt;span class="nx"&gt;DateTime&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nx"&gt;Role&lt;/span&gt;       &lt;span class="nx"&gt;Role&lt;/span&gt;     &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;role_id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;references&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&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;Generate migration,&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;pnpx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; add-age-field-to-user &lt;span class="nt"&gt;--create-only&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpx prisma migrate dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Deploy in production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we want to apply migrations on production by manually or using CI/CD, then much safe to use,&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;pnpx prisma migrate deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it safe for production? This does not generate any migrations. Only apply the changes (diff) to the database and does not re generate the prisma client&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;General tips&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When delete node_modules folder, prisma client also will be deleted. Prisma client usually located at node_modules/@prisma folder. Need to re generate it before use in the code. Better option would be add the following to the package.json
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&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;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prisma generate"&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;OR run the command manually,&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;pnpx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Connect from NestJS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, we have done the setup for database infrastructure. Let's focus on how do we connect NestJS with the created database.&lt;/p&gt;

&lt;p&gt;First step is create a prisma module. (It is work just with a prisma service without having a prisma module. But, with the NestJS module philosophy, it is recommended to have a prisma module)&lt;/p&gt;

&lt;p&gt;Then, prisma service,&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;nest generate module prisma
&lt;span class="nv"&gt;$ &lt;/span&gt;nest generate service prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once module generated, module import automatically add to the imports list in the app.module. (this is not necessary. without import prisma module from here, it works as usual) &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%2Fojsrt0vpkh8lon5ujj0w.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%2Fojsrt0vpkh8lon5ujj0w.png" alt="Prisma import to app module" width="413" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update src/prisma/prisma.service.ts file as bellow,&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;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnModuleInit&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;@nestjs/common&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;PrismaClient&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;@prisma/client&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="nd"&gt;Injectable&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;class&lt;/span&gt; &lt;span class="nc"&gt;PrismaService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnModuleInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onModuleInit&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="k"&gt;this&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="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, Prisma will be globally available for dependency injection from any services. Let's try that as well,&lt;/p&gt;

&lt;p&gt;Let's say we have a user module. Then when we want to access prisma inside a user module, first, need to import prisma service in the user module:&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;Module&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;@nestjs/common&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;UserService&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;./user.service&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;UserController&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;./user.controller&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;PrismaService&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;src/prisma/prisma.service&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="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt;&lt;span class="p"&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;class&lt;/span&gt; &lt;span class="nc"&gt;UserModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, can inject prisma service to the user service and use it within the user service,&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;Injectable&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;@nestjs/common&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;PrismaService&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;../prisma/prisma.service&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="nd"&gt;Injectable&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;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// injecting prisma service to user service&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PrismaService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&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;async&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CreateUserDto&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;data&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;async&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UpdateUserDto&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;data&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;async&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it 🏆&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;GUI for Prisma&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prisma has awesome tool to visualise the data.&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;pnpx prisma studio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open the GUI on &lt;code&gt;http://localhost:5555&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are many ORM supports with NestJS. Drizzle and TypeORM are the other most popular candidates. I hear that Drizzle is much performant than others. All candidates have tradeoffs each other and I use Prisma for small and medium scale projects. It is easy to use when compare with TypeORM.   &lt;/p&gt;

&lt;p&gt;May be you can share your thoughts on this? &lt;/p&gt;

&lt;p&gt;🤝 Let's meet again with another important topic&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>prisma</category>
      <category>postgres</category>
    </item>
    <item>
      <title>My Mostly Used Docker Commands</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Thu, 03 Apr 2025 12:17:41 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/my-mostly-used-docker-commands-39mh</link>
      <guid>https://dev.to/dilumdarshana/my-mostly-used-docker-commands-39mh</guid>
      <description>&lt;p&gt;Mostly used Docker commands collection by me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;generic&lt;/strong&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="c"&gt;# remove all stopped containers&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker prune

&lt;span class="c"&gt;# docker full cleanup&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;

&lt;span class="c"&gt;# check disk usage by docker&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;

&lt;span class="c"&gt;# check the service version&lt;/span&gt;
&lt;span class="c"&gt;# --rm: this will remove the container after stop&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; redis redis-server &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; node node &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;images&lt;/strong&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="c"&gt;# pull docker image&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker pull redis

&lt;span class="c"&gt;# list pulled images&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker images

&lt;span class="c"&gt;# remove image&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker rmi redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;container&lt;/strong&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="c"&gt;# create and run docker container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-master &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis

&lt;span class="c"&gt;# run docker in specific network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; redis-master &lt;span class="nt"&gt;--network&lt;/span&gt; redis-net &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379:6379 redis

&lt;span class="c"&gt;# stop container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop redis-master

&lt;span class="c"&gt;# start container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker start redis-master

&lt;span class="c"&gt;# check running containers&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps

&lt;span class="c"&gt;# check all containers&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# restart container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker restart redis-master

&lt;span class="c"&gt;# check logs&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker logs redis-master

&lt;span class="c"&gt;# go inside container&lt;/span&gt;
&lt;span class="c"&gt;# docker exec -it &amp;lt;container_name|container_id&amp;gt; bash&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; redis-master bash

&lt;span class="c"&gt;# execute command without go inside container&lt;/span&gt;
&lt;span class="c"&gt;# docker exec &amp;lt;container_name&amp;gt; &amp;lt;command_to_run_inside_container&amp;gt;&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;redis-master redis-cli GET username

&lt;span class="c"&gt;# remove container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;rm &lt;/span&gt;redis-master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;volums&lt;/strong&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="c"&gt;# list all volumes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume list

&lt;span class="c"&gt;# create a new volume&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume create redis-volume

&lt;span class="c"&gt;# remove volume&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume &lt;span class="nb"&gt;rm &lt;/span&gt;redis-volume

&lt;span class="c"&gt;# inspect volume in a container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker volume inspect redis_master

&lt;span class="c"&gt;# mount volume to container&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-v&lt;/span&gt; redis-volume:/data redis-master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;network&lt;/strong&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="c"&gt;# create new network&lt;/span&gt;
&lt;span class="c"&gt;# docker network create &amp;lt;options&amp;gt; &amp;lt;network_name&amp;gt; &lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create redis-net
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network create &lt;span class="nt"&gt;--subnet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;192.168.1.0/24 redis-net

&lt;span class="c"&gt;# list all networks&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network list

&lt;span class="c"&gt;# check who is in the network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect redis-net

&lt;span class="c"&gt;# check what is inside network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect redis-net

&lt;span class="c"&gt;# connect to specific container&lt;/span&gt;
&lt;span class="c"&gt;# docker network connect &amp;lt;network&amp;gt; &amp;lt;container&amp;gt;&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network connect redis-net redis-master

&lt;span class="c"&gt;# disconnect from network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network disconnect redis-net redis-master

&lt;span class="c"&gt;# remove network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;rm &lt;/span&gt;redis-net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;docker-compose&lt;/strong&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="c"&gt;# compose start&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;docker-compose.yml&amp;gt; up

&lt;span class="c"&gt;# compose stop. This will remove all containers as well&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;docker-compose.yml&amp;gt; down

&lt;span class="c"&gt;# compose stop + delete volumes&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;docker-compose.yml&amp;gt; down &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;debug&lt;/strong&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="c"&gt;# inspect container&lt;/span&gt;
&lt;span class="c"&gt;# docker inspect &amp;lt;container_name&amp;gt;&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect redis-master

&lt;span class="c"&gt;# inspect image&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect redis

&lt;span class="c"&gt;# inspect docker network&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect redis-net
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

&lt;p&gt;Note: This is a living document.&lt;/p&gt;

</description>
      <category>docker</category>
    </item>
    <item>
      <title>Next.js Deployment to AWS Amplify: Environment Variable Issue &amp; Fix</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Sat, 29 Mar 2025 09:00:38 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/nextjs-deployment-to-aws-amplify-environment-variable-issue-fix-333k</link>
      <guid>https://dev.to/dilumdarshana/nextjs-deployment-to-aws-amplify-environment-variable-issue-fix-333k</guid>
      <description>&lt;p&gt;Recently, I had to spent hours to figure it out environment variable issue on NextJS deployment to AWS Amplify. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Application has text search from MongoDB where user do the search from UI. API provided by the NextJS itself located at,&lt;/p&gt;

&lt;p&gt;api/search/route.ts&lt;/p&gt;

&lt;p&gt;Route handler connect to the Mongodb using mongoose.connect function. Function has an environment variable call &lt;code&gt;MONGDOB_URI&lt;/code&gt;. This is coming from an environment variable. Application has some public environment variables as well (obviously start with NEXT_PUBLIC_XXXX). All variables set via Amplify Environment Variable section,&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%2F8wsc7uwgq6swsdrr570w.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%2F8wsc7uwgq6swsdrr570w.png" alt="Amplify env variables" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When application deployed to the Amplify, &lt;code&gt;MONGDOB_URI&lt;/code&gt; read as 'undefined' while other public environment variables are reading without any issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I check the build log, all environment variables are reading well,&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%2F6jigu697r8fmbts02ac8.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%2F6jigu697r8fmbts02ac8.png" alt="Amplify build log" width="796" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, when I run the application, CloudWatch logs says it is not found &lt;code&gt;MONGDOB_URI&lt;/code&gt;&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%2Fzxokg800zeo9bnycmpru.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%2Fzxokg800zeo9bnycmpru.png" alt="CloudWatch log" width="800" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Public variables are working fine,&lt;br&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%2Fbiv3u7ra30ogo0gwngag.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%2Fbiv3u7ra30ogo0gwngag.png" alt="Web console logs" width="622" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solutions tried out&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some articles says that add the missing variable implicitly add  to the &lt;code&gt;next.config.ts&lt;/code&gt; file.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* config options here */&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;MONGODB_URI&lt;/span&gt;&lt;span class="p"&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;MONGODB_URI&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, it worked!!!. But, it has a huge security issue, which is secured database connection will be exposed to the public.&lt;/p&gt;

&lt;p&gt;Then, removed the next cache from the amplify.yml file. &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%2Flbkxoi0k1pdt5y8706d2.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%2Flbkxoi0k1pdt5y8706d2.png" alt="Amplify config file" width="650" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still did not work :(&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the last, created the &lt;code&gt;.env.production&lt;/code&gt; file on the fly and appended the &lt;code&gt;MONGODB_URI&lt;/code&gt; into the file using amplify.yml file,&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%2Fwu6w162c9kq3qcvgrr1j.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%2Fwu6w162c9kq3qcvgrr1j.png" alt="Amplify config with env" width="657" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, it worked!!!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For some reason, only public environment (starts with NEXT_PUBLIC) can be fetch from the Amplify when it is building without needing &lt;code&gt;.env.production&lt;/code&gt; file. For secured variables, we need to append them into the &lt;code&gt;.env.production&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Anyone encountered this issue?  Hope this help to someone for solution or debate !!!&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>amplify</category>
      <category>env</category>
      <category>deployment</category>
    </item>
    <item>
      <title>Setting up NestJS Project</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Thu, 27 Mar 2025 07:53:41 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/setting-up-nestjs-project-523l</link>
      <guid>https://dev.to/dilumdarshana/setting-up-nestjs-project-523l</guid>
      <description>&lt;p&gt;Hello Backend devs...&lt;/p&gt;

&lt;p&gt;Here let's go through the setting up new NestJS project with basic features. &lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Create a new project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now is time to create a new NestJS app. The NestJS provide awesome command line tool to generate app and the main building blocks. Install NestCLI as a global package using your favourite package manager, then generate the app,&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;# add nest cli&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm add &lt;span class="nt"&gt;-g&lt;/span&gt; @nestjs/cli

&lt;span class="c"&gt;# generate app&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nest new project-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, everything ready to customise the app as you needed. The whole boilerplate with minimal features is there. Run the following command to make sure that everything is working,&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;# start the app server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm start:dev

&lt;span class="c"&gt;# check everything works&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:3000

&lt;span class="c"&gt;# you should see&lt;/span&gt;
Hello World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🟢 &lt;strong&gt;Create a first module&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's say, you want to add a new feature to your app. Example, user management, auth, etc...&lt;/p&gt;

&lt;p&gt;There are few CLI commands to understand based on the requirement. There is a patter in 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;&lt;span class="nv"&gt;$ &lt;/span&gt;nest generate &amp;lt;module|controller|service|resource&amp;gt; &amp;lt;name&amp;gt;

&lt;span class="c"&gt;# eg. generate new module for user management, and the controller&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nest generate module user

&lt;span class="c"&gt;# generate controller&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;nest generate controller user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please pay attention on name of the module (user) and the name of the controller (user). Those has to be same in order to NestJS understand both should generate to the same feature. Let's create the service also,&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;nest generate service user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you should see the new folder and bunch of files created by NestCLI,&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%2F40pi5u6aldi75vn1b0bw.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%2F40pi5u6aldi75vn1b0bw.png" alt="New module" width="406" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is the resource command does? If you want to create whole crud feature at the same time, you can use it. Make sure that you use either options,&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;nest generate resource user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🟢 &lt;strong&gt;How to use main building blocks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, you have user module boilerplate to start coding. Mostly, use module, controller and the service. When you go deeper, you need guards, pipes, filters, etc..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Module:&lt;/em&gt; Connect everything needed for feature. You have nothing to do here, unless you have Injectable classes to be used or another module need to be used in this module. Basically, whatever connected to the DI (Dependency Injection) container, we can connect from here. If you do not have anything from outside, then leave it as it is. See bellow some usage of another module (auth),&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%2F0r4e6stghyyxa5gu2sy1.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%2F0r4e6stghyyxa5gu2sy1.png" alt="Module Usage" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Controller:&lt;/em&gt; The request handler in NestJS level. Basically, routes are defined here. Guard, Interceptors like other decorators can be applied to this level, before request goes to the service level. Let's say I have following API requirements,&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Get all users: GET -&amp;gt; &lt;code&gt;http://localhost:3000/user&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get a specific user: GET -&amp;gt; &lt;code&gt;http://localhost:3000/user/1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a new user: POST -&amp;gt; &lt;code&gt;http://localhost:3000/user&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Look at the bellow code carefully. Main controller decorator define the 'user' part of the route. Rest of the path defined in function decorator. &lt;code&gt;@Get&lt;/code&gt;, &lt;code&gt;@Post&lt;/code&gt;, &lt;code&gt;@Param&lt;/code&gt; and &lt;code&gt;@Body&lt;/code&gt; are the main decorators used to make our life easier. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;@Get&lt;/code&gt;: Define as GET http verb&lt;br&gt;
&lt;code&gt;@Post&lt;/code&gt;: Define as POST http verb&lt;br&gt;
&lt;code&gt;@Params&lt;/code&gt;: Read query params and return to id variable&lt;br&gt;
&lt;code&gt;@Body&lt;/code&gt;: Extract body contents and assign to user object&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('user') // main route path
export class UserController {
  // inject user service into the controller
  constructor(private readonly userService: UserService) {}

  @Get('/') // get all users
  allUsers() {
    return this.userService.findAll();
  }

  @Get(':id') // get a specific user. eg. user/1
  findOne(@Param('id') id: string) {
    return this.userService.findById(+id);
  }

  @Post('/') // create a new user
  createUser(@Body() user: CreateUserDto) {
    return this.userService.createUser(user);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Service:&lt;/em&gt; The business logic layer. This is going to be Injectable to the DI container. Which means can be use from anywhere who can connect with the DI container. Can talk to database from here as well. And other processing and manipulations. Finally, return the response to the controller. Look at our user service bellow,
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { CreateUserDto } from '../modules/user/dto/create-user.dto';

@Injectable() // inject to the DI container
export class UserService {
  findAll() {
    // find all users from database

    // return list of users
    return [];
  }

  findById(id: number) {
    // find user from database

    // return user
    return id;
  }

  createUser(user: CreateUserDto) {
    // user creation logic

    // return created user
    return user;
  }
}

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

&lt;/div&gt;



&lt;p&gt;That's it for a basics to play around with the NestJS REST API creation.&lt;/p&gt;

&lt;p&gt;🎯 Give it a try yourself and let me know how it goes!&lt;/p&gt;

&lt;p&gt;🤝 Let's connect with the database &lt;a href="https://dev.to/dilumdarshana/connecting-database-with-nestjs-using-prisma-orm-5f7h"&gt;click here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>restapi</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What You Need to Know Before Starting a NestJS Project</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Wed, 26 Mar 2025 10:12:03 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/what-you-need-to-know-before-starting-nestjs-project-12p9</link>
      <guid>https://dev.to/dilumdarshana/what-you-need-to-know-before-starting-nestjs-project-12p9</guid>
      <description>&lt;p&gt;Hello Backend devs...&lt;/p&gt;

&lt;p&gt;NestJS has quickly become one of the most popular Node.js progressive frameworks for building scalable and maintainable server-side applications. The patterns are followed by Angular's architecture and fully support with TypeScripts. NestJS brings structure and strong typing to the fast-paced world of REST API developments.&lt;/p&gt;

&lt;p&gt;If you're coming from Express or a similar minimal framework, starting a NestJS project can feel like a big shift—in a good way. Its modular design, built-in dependency injection, and clear separation of concerns promote clean code and make it easier to scale your application as it grows.&lt;/p&gt;

&lt;p&gt;In this guide, I will walk you through the most important concepts you need to know before starting your NestJS project (I am talking about V11.X)&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Main building blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modules - Group together everything&lt;/li&gt;
&lt;li&gt;Controller - Handle incoming request&lt;/li&gt;
&lt;li&gt;Services - Handle business logic&lt;/li&gt;
&lt;li&gt;Pipes - Validate incoming data&lt;/li&gt;
&lt;li&gt;Exception Filters - Handle errors throws by controller or service&lt;/li&gt;
&lt;li&gt;Guards - Handle authentication and authorisation. Can decide request proceed to not (return true / false)&lt;/li&gt;
&lt;li&gt;Interceptors - Adding extra data to request or to response&lt;/li&gt;
&lt;li&gt;Middleware - Can modify request object&lt;/li&gt;
&lt;li&gt;Repositories - Handle data stored in DB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🟢 &lt;strong&gt;The main architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NestJS does not handle HTTP request itself. We need to provide either Express OR Fastify for handling HTTP requests. By default, NestJS uses Express.&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%2Fj1b8xzgrz7tgllqjhvkg.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%2Fj1b8xzgrz7tgllqjhvkg.png" alt="NestJS Server" width="583" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NestJS main building blocks has an order of execution. See bellow,&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%2Fom3h7ym5n4ecun79o00c.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%2Fom3h7ym5n4ecun79o00c.png" alt="Flow of execution" width="717" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;DI Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NestJS uses dependency injection (DI) as its main design pattern for the easy management of dependencies between application components. NestJS DI system is based on the Inversion of Control (IoC) technique, meaning that the framework controls the creation and life cycle of an objects (such as services and controllers) rather than the objects themselves controlling these aspects. &lt;/p&gt;

&lt;p&gt;Basically, NestJS helps with automatically creating and injecting services and other dependencies into classes; hence, one can decouple components and keep the application modular.&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Interceptors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be used to modify request and response. Let's say want to modify the request object before it goes to the controller,&lt;/p&gt;

&lt;p&gt;Create a new interceptor in the correct module. Eg. modules -&amp;gt; auth -&amp;gt; interceptors -&amp;gt; auth.interceptor.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class OAuthInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable&amp;lt;any&amp;gt; {
    const request = context.switchToHttp().getRequest();

    // transform user profile
    if (request.user) {
      request.user = this.normalizeUserProfile(); // modify user object
    }

   return next.handle(); // just return the request object without modifying response object
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;return next.handle();&lt;/code&gt;. It just pass the request to the controller, no change on response.&lt;/p&gt;

&lt;p&gt;Next, call the interceptor from controller,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@UseGuards(AuthGuard('github'))
@UseInterceptors(OAuthInterceptor)
async githubAuthCallback(@Req() req: AuthenticatedRequest, @Res() res: Response) {
  if (!req.user) {
      throw new UnauthorizedException('User not authorized');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it's need to modify the response,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, map } from 'rxjs';

@Injectable()
export class OAuthInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable&amp;lt;any&amp;gt; {
    const request = context.switchToHttp().getRequest();

    // transform user profile
    if (request.user) {
      request.user = this.normalizeUserProfile(); // modify user object
    }

    // modify the response also,
    return next.handle().pipe(
      map((data) =&amp;gt; {
        return {
          success: true, // add anything additional
          data,
        }
      })
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🟢 &lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is vital to understand the basic concepts before start any new technologies. It is may be take some time, but, hold on, it would be a good investment for the future. I will update the post with the new finding continuously.&lt;/p&gt;

&lt;p&gt;🤝 Let's meet again with setting up NestJS app. &lt;a href="https://dev.to/dilumdarshana/setting-up-nestjs-project-523l"&gt;Click Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>restapi</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Why NestJS Became My favourite Node.js Framework (After Years of Express.js)</title>
      <dc:creator>Dilum Darshana</dc:creator>
      <pubDate>Tue, 25 Mar 2025 09:49:49 +0000</pubDate>
      <link>https://dev.to/dilumdarshana/why-nestjs-became-my-favourite-nodejs-framework-after-years-of-expressjs-1i27</link>
      <guid>https://dev.to/dilumdarshana/why-nestjs-became-my-favourite-nodejs-framework-after-years-of-expressjs-1i27</guid>
      <description>&lt;p&gt;After spending nearly a decade building backend applications with Express.js, I thought I had everything I needed. Express is fast, easy to understand, and can build just about anything.&lt;/p&gt;

&lt;p&gt;But as my projects grew larger, so did the complexity. I started facing challenges like maintaining a clean folder structure, managing dependency injection manually, code complexity and struggling with testability.&lt;/p&gt;

&lt;p&gt;That is when I gave NestJS a serious try — and honestly, it felt like switching from manual to automatic. Today, it’s my go-to framework for scalable Node.js applications. Here’s why.&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;What is NestJS ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A progressive (start with minimal) Node.js framework built with awesome TypeScript support based on Express or Fastify. It uses decorators heavily for reusability. OOP (Object Oriented Programming) or FP (Functional Programming) as needed. Would be ideal for medium and large scale projects&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;My Journey with Express.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Express was the most convenient framework at the time I started my development career. It was easy to understand and flexible enough to develop almost every features what I needed.&lt;/p&gt;

&lt;p&gt;After sometimes, when the projects are become larger and larger, it was hard to keep the consistency of the codebase. And, testing them as a module was a bit tricky. I had to create my own boilerplate with the specific folder structure.&lt;/p&gt;

&lt;p&gt;Here is the one I created some times back: &lt;a href="https://github.com/dilumdarshana/node-boilerplate-js" rel="noopener noreferrer"&gt;My Express based Boilerplate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Why I Switched to NestJS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is few reasons I prefer NestJS over Express.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Well architected boilerplate with minimum features&lt;/li&gt;
&lt;li&gt;It is already tested well&lt;/li&gt;
&lt;li&gt;Can scale easily&lt;/li&gt;
&lt;li&gt;Native Typescript support&lt;/li&gt;
&lt;li&gt;Required extensions mostly provided by NestJS team itself (eg. JWT, Passport, Graphql, Cors, etc...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🟢 &lt;strong&gt;Key Features That Sold Me on NestJS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependency injection&lt;/li&gt;
&lt;li&gt;Modular pattern&lt;/li&gt;
&lt;li&gt;Built-in features, like, Guards, Interceptors, Middlewares, Pipes&lt;/li&gt;
&lt;li&gt;Typescript native support (Heavy with Decorators)&lt;/li&gt;
&lt;li&gt;Easy expanding with external modules (Eg. Cors, JWT, Config, etc...)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well structured folder structure &lt;br&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%2F41q9t57posfv28df0de0.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%2F41q9t57posfv28df0de0.png" alt="Folder Structure" width="230" height="588"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy routes with simple controllers&lt;br&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%2Fx5s6nuxtaf4pcr6diewn.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%2Fx5s6nuxtaf4pcr6diewn.png" alt="Sample Controller" width="482" height="170"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support for microservices (I still not tried out this)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API documentation with few lines of codes (Swagger integration)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🟢 &lt;strong&gt;Drawbacks of NestJS (Yes, It might Has Some)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I still did not encountered significant drawbacks. It might be quite overhead for use NestJS for very small projects. And, obviously, there is a learning curve when switch from Express.js&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Should You Switch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Im my opinion, there are several factors need to consider behind it. If you have significant scalable app to be implemented, you have a good knowledge of TypeScript and if your app going to be type of microservices, then better option would be use NestJS&lt;/p&gt;

&lt;p&gt;🟢 &lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Switching from Express.js to NestJS was one of the best decisions I’ve made for building scalable backend apps. Still Express.js would be a good choice specially for small projects and MVPs.&lt;/p&gt;

&lt;p&gt;🎯 If you haven’t tried it yet, I highly recommend giving it a shot!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.nestjs.com/" rel="noopener noreferrer"&gt;NestJS Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🤝 I will write an another post on NestJS internals sooner &lt;a href="https://dev.to/dilumdarshana/what-you-need-to-know-before-starting-nestjs-project-12p9"&gt;Check Here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cheers... Happy coding!!!&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>api</category>
      <category>express</category>
      <category>node</category>
    </item>
  </channel>
</rss>
