<?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: Teja Kummarikuntla</title>
    <description>The latest articles on DEV Community by Teja Kummarikuntla (@tejakummarikuntla).</description>
    <link>https://dev.to/tejakummarikuntla</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%2F464555%2F37d4c2e0-271e-4b9b-a5dc-a70f856639b1.jpeg</url>
      <title>DEV Community: Teja Kummarikuntla</title>
      <link>https://dev.to/tejakummarikuntla</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tejakummarikuntla"/>
    <language>en</language>
    <item>
      <title>💰I Built a Token Billing System for My AI Agent - Here's How It Works</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Tue, 31 Mar 2026 15:39:56 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/i-built-a-token-billing-system-for-my-ai-agent-heres-how-it-works-dl2</link>
      <guid>https://dev.to/tejakummarikuntla/i-built-a-token-billing-system-for-my-ai-agent-heres-how-it-works-dl2</guid>
      <description>&lt;p&gt;I've been building an AI agent that routes requests across multiple LLM providers, &lt;strong&gt;OpenAI&lt;/strong&gt;, &lt;strong&gt;Anthropic&lt;/strong&gt; etc., based on the task. But pretty quickly, I hit a real problem: &lt;em&gt;how do you charge for this fairly?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Flat subscriptions didn't make sense. Token costs vary by model, input vs output, and actual usage. A user generating a two-line summary isn't the same as someone churning out 3,000-word articles, yet flat pricing treats them the same.&lt;/p&gt;

&lt;p&gt;I looked at a few options for usage-based billing. &lt;strong&gt;Stripe Billing&lt;/strong&gt; has metered subscriptions but you have to build your own token tracking pipeline on top. &lt;strong&gt;Orb&lt;/strong&gt; and &lt;strong&gt;Metronome&lt;/strong&gt; are good, but they're separate vendors, you'd still need something to capture token data from your LLM calls and pipe it in. What I wanted was something at the gateway level, where the traffic already flows.&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%2Fbeci2wp1ljaq0d7kl42f.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%2Fbeci2wp1ljaq0d7kl42f.png" alt=" " width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ended up using &lt;strong&gt;&lt;a href="https://konghq.com/products/kong-ai-gateway" rel="noopener noreferrer"&gt;Kong AI Gateway&lt;/a&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;a href="https://konghq.com/products/kong-konnect/features/usage-based-metering-and-billing" rel="noopener noreferrer"&gt;Konnect Metering &amp;amp; Billing&lt;/a&gt;&lt;/strong&gt; (built on &lt;strong&gt;OpenMeter&lt;/strong&gt;). The gateway proxies every LLM request, so it already knows the token counts. The metering layer plugs directly into that. No separate vendor, no custom pipeline.&lt;/p&gt;

&lt;p&gt;So instead of debating about pricing models, I set up the billing layer. A working system where every API request flows through a gateway, gets tracked, and is priced based on real usage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;🚧 Route requests through AI Gateway&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🪙 Tokens get metered per consumer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;💵 Pricing gets applied&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🧾 Invoice generated&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the whole setup, step by step.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up the gateway&lt;/li&gt;
&lt;li&gt;Step 1: Create a consumer&lt;/li&gt;
&lt;li&gt;Step 2: Configure the AI Proxy&lt;/li&gt;
&lt;li&gt;Step 3: Enable token metering&lt;/li&gt;
&lt;li&gt;Step 4: Create a feature&lt;/li&gt;
&lt;li&gt;Step 5: Create a plan with a rate card&lt;/li&gt;
&lt;li&gt;Step 6: Create a subscription&lt;/li&gt;
&lt;li&gt;Step 7: Validate the invoice&lt;/li&gt;
&lt;li&gt;Step 8: Connect Stripe&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;The billing pipeline has three layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kong AI Gateway&lt;/strong&gt; proxies the LLM requests. It sits between the app and the provider, handles auth, and this is the part that matters for billing, it logs token statistics for every request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Konnect Metering &amp;amp; Billing&lt;/strong&gt; (this is built on &lt;strong&gt;OpenMeter&lt;/strong&gt;) takes those token events and aggregates them per consumer, per billing cycle. It supports defining features, pricing models, and plans on top of the raw usage data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stripe&lt;/strong&gt; collects payment. The metering layer generates invoices that sync to Stripe.&lt;/p&gt;

&lt;p&gt;Let me walk through each piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You can do this entirely through the UI or via CLI. I'll cover both as we go.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;a href="https://konghq.com/products/kong-konnect" rel="noopener noreferrer"&gt;Kong Konnect&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;OpenAI&lt;/strong&gt; API key (or any LLM provider key of your choice)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For CLI, you'll also need &lt;a href="https://developer.konghq.com/deck/" rel="noopener noreferrer"&gt;decK (v1.43+)&lt;/a&gt; installed and a &lt;a href="https://cloud.konghq.com/global/account/tokens" rel="noopener noreferrer"&gt;PAT from Kong Konnect&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up the Gateway
&lt;/h2&gt;

&lt;p&gt;Once you log in, click on &lt;strong&gt;API Gateway&lt;/strong&gt; and create one.&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%2Fms4m351xq50wk94vsdk7.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%2Fms4m351xq50wk94vsdk7.png" alt=" " width="800" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm using Serverless here. You can choose Self-managed too. Enter the gateway name as &lt;code&gt;ai-service&lt;/code&gt; and click &lt;strong&gt;Create and configure&lt;/strong&gt;. Once that's done, click &lt;strong&gt;Add a service and route&lt;/strong&gt; and fill in:&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%2Fw0qxc9dwgjcbqnsbyowd.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%2Fw0qxc9dwgjcbqnsbyowd.png" alt=" " width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Name:&lt;/strong&gt; &lt;code&gt;ai-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service URL:&lt;/strong&gt; &lt;code&gt;http://httpbin.konghq.com/anything&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route Name:&lt;/strong&gt; &lt;code&gt;ai-chat&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route Path:&lt;/strong&gt; &lt;code&gt;/chat&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CLI
&lt;/h3&gt;

&lt;p&gt;If you prefer the command line, generate your PAT and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KONNECT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_konnect_pat'&lt;/span&gt;
curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; https://get.konghq.com/quickstart | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-k&lt;/span&gt; &lt;span class="nv"&gt;$KONNECT_TOKEN&lt;/span&gt; &lt;span class="nt"&gt;--deck-output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a running Kong Gateway connected to Konnect. It'll output some environment variables, export them as instructed. You'll also need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DECK_OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_openai_api_key'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then set up the service and route:&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;_format_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-service&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://httpbin.konghq.com/anything&lt;/span&gt;
&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-chat&lt;/span&gt;
    &lt;span class="na"&gt;paths&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;/chat"&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it with &lt;code&gt;deck gateway apply&lt;/code&gt;. Now you have a route at &lt;code&gt;/chat&lt;/code&gt; that we'll wire up to an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a Consumer
&lt;/h2&gt;

&lt;p&gt;You can't bill anyone if the gateway doesn't know &lt;em&gt;who&lt;/em&gt; is making the request. Consumers are how Kong identifies API callers. Later, we'll map each consumer to a billing customer.&lt;/p&gt;

&lt;p&gt;Add a consumer with a key-auth credential:&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%2F0gmknorg1j0xdfl4tcip.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%2F0gmknorg1j0xdfl4tcip.png" alt=" " width="800" height="346"&gt;&lt;/a&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%2F35iwgwyce9skaht7ows1.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%2F35iwgwyce9skaht7ows1.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can enter the Key value as &lt;code&gt;acme-secret-key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, you need to add the key-auth plugin to the service so the gateway actually requires authentication:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on &lt;strong&gt;Plugins&lt;/strong&gt; in the left sidebar&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;New Plugin&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Key Authentication&lt;/strong&gt; from the plugin list&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Service&lt;/strong&gt; as the scope or keep it as &lt;strong&gt;Global&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;_format_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0"&lt;/span&gt;
&lt;span class="na"&gt;consumers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;acme-corp&lt;/span&gt;
    &lt;span class="na"&gt;keyauth_credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;acme-secret-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable the key-auth plugin on the service so the gateway actually requires authentication:&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;_format_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0"&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;key-auth&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-service&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;key_names&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apikey&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply both with &lt;code&gt;deck gateway apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now every request to &lt;code&gt;/chat&lt;/code&gt; must include an &lt;code&gt;apikey&lt;/code&gt; header. The gateway identifies the caller as &lt;code&gt;acme-corp&lt;/code&gt;, and that identity flows through to metering. Without this step, usage events have no subject. They're anonymous, and you can't attribute them to anyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure the AI Proxy
&lt;/h2&gt;

&lt;p&gt;Next, wire the route to an actual LLM. The AI Proxy plugin accepts requests in OpenAI's chat format and forwards them to the configured provider.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;New Plugin&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;AI Proxy&lt;/strong&gt; from the plugin list&lt;/li&gt;
&lt;/ol&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%2Frsy0uvct4i4h9fl4siqt.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%2Frsy0uvct4i4h9fl4siqt.png" alt=" " width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following the below yaml for CLI and configure the plugin fields accordingly:&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;_format_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0"&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-proxy&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;route_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;llm/v1/chat&lt;/span&gt;
      &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;header_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
        &lt;span class="na"&gt;header_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer ${{ env "DECK_OPENAI_API_KEY" }}&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-4o&lt;/span&gt;
      &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;log_payloads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;log_statistics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to note here:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;log_statistics: true&lt;/code&gt; is what makes billing possible. Without it, the gateway proxies requests but doesn't record token counts. When enabled, it captures prompt tokens, completion tokens, and total tokens on every response. This is the data that metering consumes downstream.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;log_payloads: true&lt;/code&gt; logs the actual request/response content. This is optional and useful for debugging, but you'd probably turn it off in production for privacy reasons.&lt;/p&gt;

&lt;p&gt;Apply with &lt;code&gt;deck gateway apply&lt;/code&gt; and test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KONNECT_PROXY_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"apikey: acme-secret-key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="s1"&gt;'{
    "messages": [
      {"role": "system", "content": "You are a mathematician."},
      {"role": "user", "content": "What is 1+1?"}
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a response from GPT-4o. The gateway handled auth, forwarded the request, and logged the token statistics.&lt;/p&gt;

&lt;p&gt;If you want to proxy multiple providers (say, OpenAI and Anthropic with automatic failover), you'd use &lt;code&gt;[ai-proxy-advanced](https://developer.konghq.com/plugins/ai-proxy-advanced/)&lt;/code&gt; instead with a load balancing config. I stuck with a single provider here to keep the billing walkthrough focused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Enable Token Metering
&lt;/h2&gt;

&lt;p&gt;Now we connect the gateway's token logs to the metering system.&lt;/p&gt;

&lt;p&gt;In Konnect, go to &lt;strong&gt;Metering &amp;amp; Billing&lt;/strong&gt; in the sidebar. You'll see an &lt;strong&gt;AI Gateway Tokens&lt;/strong&gt; section. Click &lt;strong&gt;Enable Related API Gateways&lt;/strong&gt;, select your control plane (the quickstart one), and confirm.&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%2Fm5ktuk4v5bkcc0poondr.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%2Fm5ktuk4v5bkcc0poondr.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This activates a built-in meter called &lt;code&gt;kong_konnect_llm_tokens&lt;/code&gt;. It uses SUM aggregation on the token count, grouped by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$.model&lt;/code&gt; : which LLM handled the request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$.type&lt;/code&gt; : whether the tokens are input (&lt;code&gt;request&lt;/code&gt;) or output (&lt;code&gt;response&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The grouping matters because LLM providers charge differently for input vs. output tokens. Output tokens are typically 3-5x more expensive because input can be parallelized across GPUs while output generation is sequential, each token depends on all previous tokens. If your metering doesn't split these, your pricing will be wrong.&lt;/p&gt;

&lt;p&gt;At this point, every authenticated request through the AI Gateway generates a usage event that gets aggregated by the meter. But usage alone doesn't generate invoices. You need to define what's billable and how it's priced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create a Feature
&lt;/h2&gt;

&lt;p&gt;A feature is the link between raw metered data and something that appears on an invoice. Without it, usage is tracked but never billed.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Metering &amp;amp; Billing → Product Catalog → Features&lt;/strong&gt; and create one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;ai-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meter:&lt;/strong&gt; AI Gateway Tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group by filters:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Provider = &lt;code&gt;openai&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type = &lt;code&gt;request&lt;/code&gt; (this tracks input tokens; you'd create a separate feature for output tokens if you want to price them differently)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgk1w21y609vz6zp52x3w.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%2Fgk1w21y609vz6zp52x3w.png" alt=" " width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The filters narrow the meter to a specific slice of usage. In a real setup, you'd likely create multiple features, one per model, one per token direction, to apply different rates. For this walkthrough, I'm keeping it to one feature to show the flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Create a Plan with a Rate Card
&lt;/h2&gt;

&lt;p&gt;Plans bundle features with pricing. Go to &lt;strong&gt;Product Catalog → Plans&lt;/strong&gt; and create one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;Starter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing cadence:&lt;/strong&gt; 1 month&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%2Fnifkwcpt9d3jnpc01qqs.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%2Fnifkwcpt9d3jnpc01qqs.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a rate card:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Feature:&lt;/strong&gt; &lt;code&gt;ai-token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing model:&lt;/strong&gt; Usage Based&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price per unit:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entitlement type:&lt;/strong&gt; Boolean (grants access to the feature)&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%2Fx3q5x8ej2aif9p38k39k.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%2Fx3q5x8ej2aif9p38k39k.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A note on what "price per unit" means here: 1 unit = 1 token, because the meter SUMs individual tokens. So entering &lt;code&gt;1&lt;/code&gt; means $1.00 per token, which is way too expensive for real use. I'm using it here because the &lt;a href="https://developer.konghq.com/how-to/meter-llm-traffic/" rel="noopener noreferrer"&gt;official tutorial&lt;/a&gt; does the same thing: a round number that makes invoice changes easy to spot during testing.&lt;/p&gt;

&lt;p&gt;For production, you'd enter something like &lt;code&gt;0.000003&lt;/code&gt; for GPT-4o input tokens ($3.00 per 1M tokens) or &lt;code&gt;0.00001&lt;/code&gt; for GPT-4o output tokens ($10.00 per 1M tokens). There's no "per 1,000" toggle in the UI. You do the math yourself and enter the per-token price as a decimal.&lt;/p&gt;

&lt;p&gt;Publish the plan. It's now available for subscriptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Create a Customer and Start a Subscription
&lt;/h2&gt;

&lt;p&gt;This is where the consumer from Step 1 connects to the billing system.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Metering &amp;amp; Billing → Billing → Customers&lt;/strong&gt; and create one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;Acme Corp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include usage from:&lt;/strong&gt; select the &lt;code&gt;acme-corp&lt;/code&gt; consumer&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%2Fjhg2bomizkgass3rhmio.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%2Fjhg2bomizkgass3rhmio.png" alt=" " width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This mapping is what ties gateway traffic to a billable entity. The consumer handles identity at the gateway level; the customer handles identity at the billing level. They're separate concepts joined here.&lt;/p&gt;

&lt;p&gt;Now create a subscription:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Acme Corp customer, then &lt;strong&gt;Subscriptions → Create a Subscription&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan:&lt;/strong&gt; &lt;code&gt;Starter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start the subscription&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One important detail: &lt;strong&gt;metering only invoices events that occur after the subscription starts.&lt;/strong&gt; If you sent test requests before creating the subscription, those tokens won't appear on any invoice. I spent some time confused by this before finding it in the docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Validate the Invoice
&lt;/h2&gt;

&lt;p&gt;Send a few requests through the gateway:&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="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..6&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$KONNECT_PROXY_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"apikey: acme-secret-key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="s1"&gt;'{
      "messages": [
        {"role": "user", "content": "Explain what a Fourier transform does in two sentences."}
      ]
    }'&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a minute or two for the events to propagate, then go to &lt;strong&gt;Metering &amp;amp; Billing → Billing → Invoices&lt;/strong&gt;. Click on Acme Corp, go to the &lt;strong&gt;Invoicing&lt;/strong&gt; tab, and hit &lt;strong&gt;Preview Invoice&lt;/strong&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%2Fyi98dp2rv7i6qnom8se2.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%2Fyi98dp2rv7i6qnom8se2.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see the &lt;code&gt;ai-token&lt;/code&gt; feature listed with the aggregated token count and the calculated charge based on your rate card. That's the billing pipeline working end to end, from an API request to a line item on an invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Stripe
&lt;/h2&gt;

&lt;p&gt;Konnect syncs invoices to Stripe, which handles payment collection, receipts, and retry logic for failed payments. You connect your Stripe account in the Metering &amp;amp; Billing settings, and invoices flow through automatically at the end of each billing cycle.&lt;/p&gt;

&lt;p&gt;The result for end users is a transparent invoice showing exactly what they consumed: token count, model, rate applied. Not a flat fee with no breakdown.&lt;/p&gt;

&lt;p&gt;## Things I Ran Into&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The consumer-customer mapping confused me at first.&lt;/strong&gt; Kong Gateway has "consumers" (API identity). Metering &amp;amp; Billing has "customers" (billing identity). They're separate. You create both, then link them. If you skip the consumer or forget to link it, usage events come in but they're not attributed to anyone billable. Set this up before you start sending traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input vs. output pricing is a bigger deal than I expected.&lt;/strong&gt; Output tokens from OpenAI's GPT-4o cost $10.00/1M vs. $2.50/1M for input. If you use a single flat rate for "tokens," you'll underprice output-heavy workloads significantly. Splitting features by token type (request vs. response) and pricing them separately is worth the extra configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The order of operations matters.&lt;/strong&gt; Specifically: create the consumer and link it to a customer &lt;em&gt;before&lt;/em&gt; you start sending traffic you care about billing for. Events that arrive before a subscription exists don't retroactively appear on invoices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'd Take This Next
&lt;/h2&gt;

&lt;p&gt;This walkthrough uses a single provider and a single feature. A production setup would look more like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple features&lt;/strong&gt;: one per model per token direction (GPT-4o input, GPT-4o output, Claude input, Claude output)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiered pricing&lt;/strong&gt;: lower per-token rates at higher usage thresholds to incentivize growth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entitlements with metered limits&lt;/strong&gt;: cap total tokens per month per plan tier, so you can offer Starter (500K tokens), Pro (5M tokens), Enterprise (unlimited)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Proxy Advanced&lt;/strong&gt;: route across multiple providers with load balancing (lowest-latency, round-robin, or cost-based routing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The docs for all of these are at &lt;a href="https://developer.konghq.com/metering-and-billing/" rel="noopener noreferrer"&gt;developer.konghq.com/metering-and-billing&lt;/a&gt; and &lt;a href="https://developer.konghq.com/ai-gateway/" rel="noopener noreferrer"&gt;developer.konghq.com/ai-gateway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're building an AI agent and thinking about how to charge for it, I'd be curious to hear your approach. Per-token, credits, flat rate? What's working, what's not? Drop your thoughts in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
    <item>
      <title>⚠️ AI Gateway Attack: LiteLLM is Compromised</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Tue, 24 Mar 2026 21:37:08 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/litellm-supply-chain-attack-deep-dive-3h7m</link>
      <guid>https://dev.to/tejakummarikuntla/litellm-supply-chain-attack-deep-dive-3h7m</guid>
      <description>&lt;p&gt;On March 24, 2026, attackers published backdoored versions of LiteLLM to PyPI. The malware harvested cloud credentials, SSH keys, Kubernetes tokens, and pretty much everything else it could find on the host. This is the full breakdown of how it happened, step by step.&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%2Fv9ixa1j116zcmhcm3qdz.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%2Fv9ixa1j116zcmhcm3qdz.png" alt=" " width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scale of Impact
&lt;/h2&gt;

&lt;p&gt;LiteLLM gets roughly 3.4 million downloads per day. The compromised versions (1.82.7 and 1.82.8) were live on PyPI before being detected.&lt;/p&gt;

&lt;p&gt;Any organization that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installed litellm for the first time during the window&lt;/li&gt;
&lt;li&gt;Updated litellm to the latest version during the window&lt;/li&gt;
&lt;li&gt;Had a CI/CD pipeline that pulled litellm without pinning to a specific version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...should assume that every credential accessible from that environment has been stolen.&lt;/p&gt;

&lt;p&gt;The remediation guidance is severe: rotate every secret that was present on any machine where the compromised version was installed. SSH keys, cloud credentials, database passwords, API keys, Kubernetes tokens. All of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is LiteLLM?
&lt;/h2&gt;

&lt;p&gt;LiteLLM is an open-source Python library that acts as a unified proxy for large language models. It lets developers write one API call and route it to OpenAI, Anthropic, Google, Mistral, Cohere, or any other LLM provider without rewriting code for each provider's SDK.&lt;/p&gt;

&lt;p&gt;It's massively popular. Around 95 million monthly downloads on PyPI. Thousands of companies use it as the backbone of their AI stack because it makes switching between LLM providers trivially easy.&lt;/p&gt;

&lt;p&gt;You install it with &lt;code&gt;pip install litellm&lt;/code&gt;, import it in your Python code, and it handles the rest. That "installed directly into your application runtime" part is important. Keep it in mind.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who is TeamPCP?
&lt;/h2&gt;

&lt;p&gt;TeamPCP is the threat actor behind this attack. They're also responsible for compromising &lt;a href="https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/" rel="noopener noreferrer"&gt;Aqua Security's Trivy vulnerability scanner on March 19, 2026&lt;/a&gt; and &lt;a href="https://github.com/Checkmarx/kics-github-action/issues/152" rel="noopener noreferrer"&gt;Checkmarx's KICS GitHub Action on March 23&lt;/a&gt;. The LiteLLM attack on March 24 was the third stage of a &lt;a href="https://phoenix.security/teampcp-supply-chain-attack-trivy-checkmarx-github-actions-npm-canisterworm/" rel="noopener noreferrer"&gt;broader campaign&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Attack Chain, Step by Step
&lt;/h2&gt;

&lt;p&gt;This wasn't someone finding a bug in LiteLLM's code. It was a multi-stage supply chain attack that moved laterally across three different open-source projects before reaching LiteLLM's users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1: Compromising Trivy (March 19)
&lt;/h3&gt;

&lt;p&gt;Trivy is a popular open-source vulnerability scanner made by Aqua Security. Millions of developers and CI/CD pipelines use it to scan container images, code repos, and infrastructure for security issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it was compromised:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TeamPCP found a misconfigured GitHub Actions workflow in Trivy's repository. Specifically, a &lt;a href="https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/" rel="noopener noreferrer"&gt;&lt;code&gt;pull_request_target&lt;/code&gt; trigger&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's why that matters. In GitHub Actions, there are two ways to run workflows on pull requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pull_request&lt;/code&gt; runs the workflow using the code from the pull request itself, but with limited permissions. Safe.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pull_request_target&lt;/code&gt; runs the workflow using the code from the &lt;em&gt;base branch&lt;/em&gt; (the main repo), but it gets triggered by an external pull request. The dangerous part is that it gives the workflow access to the repository's secrets (tokens, credentials, etc.) because it's running "trusted" base branch code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem is that if the workflow does anything with the PR's code (checks it out, runs it, uses it as input), an attacker can submit a malicious PR that tricks the workflow into executing attacker-controlled code with full access to the repo's secrets.&lt;/p&gt;

&lt;p&gt;TeamPCP used an account called "MegaGame10418" to exploit this. They submitted a pull request that triggered the vulnerable workflow, which gave them access to Aqua Security's &lt;code&gt;aqua-bot&lt;/code&gt; credentials. These were privileged credentials that could push code to Trivy's repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What they did with access:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the stolen credentials, TeamPCP force-pushed malicious commits to 75 out of 77 git tags in two Trivy GitHub repositories. This means when anyone pulled Trivy at almost any version tag, they got the poisoned version.&lt;/p&gt;

&lt;p&gt;They also published a malicious Trivy binary as version v0.69.4.&lt;/p&gt;

&lt;p&gt;The malicious Trivy contained Python infostealers designed to harvest environment variables, SSH keys, and cloud tokens from CI runners and local systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aqua Security's incomplete response:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aqua Security &lt;a href="https://github.com/aquasecurity/trivy/discussions/10425" rel="noopener noreferrer"&gt;detected the breach and disclosed it&lt;/a&gt;. They rotated credentials. But the rotation was incomplete. Some access paths remained open, which &lt;a href="https://www.stepsecurity.io/blog/trivy-compromised-a-second-time---malicious-v0-69-4-release" rel="noopener noreferrer"&gt;allowed TeamPCP to continue their campaign&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: Pivoting to LiteLLM (March 24)
&lt;/h3&gt;

&lt;p&gt;Here's where it gets really interesting.&lt;/p&gt;

&lt;p&gt;LiteLLM's CI/CD pipeline used Trivy as part of its build process. This is normal. Lots of projects run security scanners in their pipelines. The problem was how LiteLLM referenced Trivy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LiteLLM did not pin Trivy to a specific, verified version.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When LiteLLM's GitHub Actions workflow kicked off a build, it pulled whatever version of Trivy the tag pointed to. Since TeamPCP had rewritten Trivy's tags to point to malicious code, LiteLLM's pipeline pulled the compromised Trivy and ran it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the compromised Trivy did inside LiteLLM's build:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It extracted LiteLLM's &lt;code&gt;PYPI_PUBLISH&lt;/code&gt; token from the GitHub Actions environment variables. This is the token that authorizes publishing new package versions to PyPI (the Python Package Index, where everyone downloads Python packages from).&lt;/p&gt;

&lt;p&gt;With this token, TeamPCP could publish any code they wanted to PyPI under the legitimate LiteLLM package name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: Publishing Poisoned LiteLLM Packages
&lt;/h3&gt;

&lt;p&gt;Using the stolen &lt;code&gt;PYPI_PUBLISH&lt;/code&gt; token, TeamPCP published &lt;a href="https://github.com/BerriAI/litellm/issues/24518" rel="noopener noreferrer"&gt;two backdoored versions of LiteLLM to PyPI&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version 1.82.7&lt;/strong&gt; had the malicious payload embedded directly in &lt;code&gt;litellm/proxy/proxy_server.py&lt;/code&gt;. This is a core module that gets imported when you use LiteLLM's proxy functionality. The payload was base64-encoded to make it harder to spot in a casual code review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version 1.82.8&lt;/strong&gt; escalated the approach. Instead of hiding code in a Python module, they included a file called &lt;code&gt;litellm_init.pth&lt;/code&gt; at the root of the package.&lt;/p&gt;

&lt;h3&gt;
  
  
  The .pth Mechanism (This Is the Clever Part)
&lt;/h3&gt;

&lt;p&gt;Python has a feature where files ending in &lt;code&gt;.pth&lt;/code&gt; that are placed in the &lt;code&gt;site-packages&lt;/code&gt; directory get &lt;a href="https://docs.python.org/3/library/site.html" rel="noopener noreferrer"&gt;processed automatically by &lt;code&gt;site.py&lt;/code&gt; during interpreter startup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Normally, &lt;code&gt;.pth&lt;/code&gt; files are used for path configuration. They tell Python where to find additional packages. But if a &lt;code&gt;.pth&lt;/code&gt; file contains a line starting with &lt;code&gt;import&lt;/code&gt;, Python will execute that import statement automatically.&lt;/p&gt;

&lt;p&gt;This means the malicious code in &lt;code&gt;litellm_init.pth&lt;/code&gt; ran every single time the Python interpreter started. Not when you imported litellm. Not when you used litellm. Just by having litellm installed in your environment.&lt;/p&gt;

&lt;p&gt;Think about what that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;python manage.py runserver&lt;/code&gt; for your Django app? The malware runs.&lt;/li&gt;
&lt;li&gt;Running a Jupyter notebook? The malware runs.&lt;/li&gt;
&lt;li&gt;Running any Python script, even one completely unrelated to LLM work? The malware runs.&lt;/li&gt;
&lt;li&gt;A CI/CD pipeline step that uses Python for anything? The malware runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;.pth&lt;/code&gt; launcher in version 1.82.8 spawned a detached child process via subprocess, so the payload ran silently in the background without the user noticing any delay or output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 4: What the Malware Actually Does
&lt;/h3&gt;

&lt;p&gt;The payload operates in three stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential Harvesting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The script systematically sweeps the host for sensitive data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables&lt;/strong&gt;: All of them. This catches anything stored as env vars, which is how most applications store API keys (OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, DATABASE_URL, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH keys&lt;/strong&gt;: Private keys from &lt;code&gt;~/.ssh/&lt;/code&gt;, authorized_keys files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud provider credentials&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;AWS: access keys, session tokens, credential files&lt;/li&gt;
&lt;li&gt;GCP: application default credentials, service account keys&lt;/li&gt;
&lt;li&gt;Azure: tokens, credential files&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Kubernetes configs&lt;/strong&gt;: kubeconfig files, service account tokens mounted in pods&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Docker authentication&lt;/strong&gt;: Docker registry credentials&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Shell history files&lt;/strong&gt;: bash_history, zsh_history (which often contain accidentally pasted credentials)&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Cryptocurrency wallet directories&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Database credentials and connection strings&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;CI/CD configuration files&lt;/strong&gt; that often contain tokens and secrets&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; files&lt;/strong&gt; wherever they exist&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;System information&lt;/strong&gt;: hostname, username, network configuration&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If a Kubernetes service account token exists in the environment, the harvester goes further. It enumerates cluster nodes and deploys privileged pods to each one, effectively spreading to the entire Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encrypted Exfiltration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The collected data goes through this process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Written to a temporary file&lt;/li&gt;
&lt;li&gt;A random AES-256 session key is generated&lt;/li&gt;
&lt;li&gt;All data is encrypted using AES-256-CBC with the session key&lt;/li&gt;
&lt;li&gt;The session key itself is encrypted with a hardcoded 4096-bit RSA public key (so only the attacker with the private key can decrypt it)&lt;/li&gt;
&lt;li&gt;Both the encrypted data and encrypted session key are packaged into a tar.gz archive called &lt;code&gt;tpcp.tar.gz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The archive is uploaded via HTTPS POST to &lt;code&gt;https://models.litellm.cloud/&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That domain, &lt;code&gt;models.litellm.cloud&lt;/code&gt;, is attacker-controlled but designed to look like legitimate LiteLLM infrastructure. If someone was monitoring network traffic, this request might not immediately raise alarms.&lt;/p&gt;

&lt;p&gt;The double encryption (AES for data, RSA for the AES key) means even if someone intercepts the exfiltrated data, they can't decrypt it without the attacker's private RSA key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persistence and Remote Access&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After exfiltrating credentials, the malware installs persistence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A systemd user service called &lt;code&gt;sysmon.service&lt;/code&gt; is created (named to look like a legitimate system monitoring service)&lt;/li&gt;
&lt;li&gt;This service polls &lt;code&gt;checkmarx.zone/raw&lt;/code&gt; every 50 minutes for additional payloads to execute&lt;/li&gt;
&lt;li&gt;This gives the attacker ongoing remote command execution on the compromised host&lt;/li&gt;
&lt;li&gt;The backdoor includes a kill switch: if the polled URL returns content containing "youtube.com", the backdoor aborts (likely a safety mechanism for the attacker during testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Kubernetes access is available, the malware deploys privileged pods across cluster nodes, giving the attacker persistent access to the entire cluster infrastructure even if the original compromised pod is removed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Late Feb - March 1&lt;/td&gt;
&lt;td&gt;Initial exploitation of Trivy workflows, bot steals &lt;code&gt;aqua-bot&lt;/code&gt; credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 19&lt;/td&gt;
&lt;td&gt;TeamPCP force-pushes malicious code to 75+ Trivy git tags, publishes malicious Trivy v0.69.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 19-22&lt;/td&gt;
&lt;td&gt;Aqua Security discloses, rotates credentials (incompletely)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 23&lt;/td&gt;
&lt;td&gt;TeamPCP &lt;a href="https://www.wiz.io/blog/teampcp-attack-kics-github-action" rel="noopener noreferrer"&gt;compromises Checkmarx's KICS GitHub Action&lt;/a&gt; using similar technique&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 24&lt;/td&gt;
&lt;td&gt;Compromised Trivy runs in LiteLLM's CI/CD, steals PYPI_PUBLISH token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 24&lt;/td&gt;
&lt;td&gt;LiteLLM versions 1.82.7 and 1.82.8 published to PyPI with malware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 24&lt;/td&gt;
&lt;td&gt;Community detects malicious .pth file, &lt;a href="https://github.com/BerriAI/litellm/issues/24512" rel="noopener noreferrer"&gt;GitHub Issue #24512&lt;/a&gt; filed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March 24-25&lt;/td&gt;
&lt;td&gt;Remediation guidance published: rotate all credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The root cause was not a code vulnerability. It was a CI/CD supply chain attack that moved across projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The entry point into LiteLLM was an unpinned dependency (Trivy) in the build pipeline, combined with a publishing token that was accessible to the build step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The .pth mechanism meant the malware ran on every Python startup, not just when litellm was imported. This is a broader execution scope than most supply chain attacks achieve.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The malware harvested everything, not just LLM API keys. It got cloud credentials, SSH keys, K8s tokens, database passwords. This happened because LiteLLM runs inside the application's Python process and inherits all of that process's access.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Architectural isolation matters. If your LLM credentials live in a separate gateway process with its own container and its own secrets, a compromised application dependency can't reach them. The blast radius stays contained.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even with isolation, supply chain attacks can still hit your gateway. But the damage is scoped to what that gateway has access to, not everything in your entire environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>supplychainattack</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Understanding Secure Communication: Encryption, Hashing, and Certificates</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Mon, 07 Apr 2025 17:47:02 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/understanding-secure-communication-encryption-hashing-and-certificates-48jc</link>
      <guid>https://dev.to/tejakummarikuntla/understanding-secure-communication-encryption-hashing-and-certificates-48jc</guid>
      <description>&lt;p&gt;If you're diving into secure communications, you've likely heard terms like &lt;strong&gt;encryption&lt;/strong&gt;, &lt;strong&gt;hashing&lt;/strong&gt;, &lt;strong&gt;digital signatures&lt;/strong&gt;, and &lt;strong&gt;certificates&lt;/strong&gt;. These concepts are essential foundations of secure digital communication. In this blog, we'll clearly define each one, explain how they combine to provide robust security, and walk step-by-step through an example scenario.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sender (You)                                          Receiver (Your Friend)
------------                                          -----------------------
Generate keys &amp;amp; Certificate      ↔️ CA ↔️             Generate keys &amp;amp; Certificate
      │                                                       │
      │────── Your Public Key (Certificate) ────────────────▶ │
      │ ◀─────────── Receiver's Public Key (Certificate)──────│
      │                                                       │
Create Message: "You are in danger"                           │
      │                                                       │
Hash Message                                                  │
      │                                                       │
Sign Hash (Your Private Key)                                  │
      │                                                       │
Encrypt Message (Receiver's Public Key from Cert)             │
      │                                                       │
Send Encrypted Message, Signature, Your Certificate ─────────▶│
                                                              │
                                                 Verify Sender's Certificate (CA Public Key)
                                                              │
                                                 Decrypt Message (Receiver's Private Key)
                                                              │
                                                 Decrypt Signature (Sender's Public Key from Cert)
                                                              │
                                                 Hash Decrypted Message
                                                              │
                                                 Compare Hashes (Integrity &amp;amp; Authenticity Confirmed)

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

&lt;/div&gt;



&lt;p&gt;Let's begin by understanding the concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Encryption
&lt;/h3&gt;

&lt;p&gt;Encryption transforms readable data ("plaintext") into unreadable data ("ciphertext"). It ensures &lt;strong&gt;confidentiality&lt;/strong&gt;: only authorized individuals with the correct key can decrypt and read the message.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Symmetric Encryption:&lt;/strong&gt; Uses the &lt;strong&gt;same key&lt;/strong&gt; for encrypting and decrypting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: AES (Advanced Encryption Standard)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Asymmetric Encryption (Public-key cryptography):&lt;/strong&gt; Uses two different but mathematically related keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public Key:&lt;/strong&gt; Freely shared, used to encrypt messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Kept secret, used to decrypt messages.&lt;/li&gt;
&lt;li&gt;Examples: RSA, ECC (Elliptic Curve Cryptography)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Hashing
&lt;/h3&gt;

&lt;p&gt;Hashing takes data of any length and produces a fixed-length "fingerprint" called a hash.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hashes are deterministic: same input produces the same hash.&lt;/li&gt;
&lt;li&gt;Hashes are irreversible: you can't derive original data from its hash.&lt;/li&gt;
&lt;li&gt;Used primarily for verifying data &lt;strong&gt;integrity&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Examples: SHA-256, SHA-3.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Digital Signatures
&lt;/h3&gt;

&lt;p&gt;A digital signature ensures the &lt;strong&gt;authenticity&lt;/strong&gt; and &lt;strong&gt;integrity&lt;/strong&gt; of digital documents or messages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sender computes a hash of the message.&lt;/li&gt;
&lt;li&gt;Sender encrypts this hash using their private key (signing process).&lt;/li&gt;
&lt;li&gt;Receiver decrypts the signature using the sender's public key to verify authenticity and integrity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A digital signature confirms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authenticity&lt;/strong&gt; (proof of sender identity)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrity&lt;/strong&gt; (message wasn't altered in transit)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Digital Certificates &amp;amp; Certificate Authorities (CA)
&lt;/h3&gt;

&lt;p&gt;A digital certificate is a document that securely associates a &lt;strong&gt;public key&lt;/strong&gt; with an identity (person, website, organization).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issued and digitally signed by a trusted entity called &lt;strong&gt;Certificate Authority (CA)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ensures that public keys belong to their stated owners.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certificates include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identity (name, domain, company)&lt;/li&gt;
&lt;li&gt;Public key of certificate holder&lt;/li&gt;
&lt;li&gt;Issuer details (CA)&lt;/li&gt;
&lt;li&gt;Digital signature from CA to verify authenticity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Communication Scenario with Certificates
&lt;/h2&gt;

&lt;p&gt;Let's clearly outline the steps involved in securely exchanging messages using all these concepts. Consider two individuals—&lt;strong&gt;Sender (You)&lt;/strong&gt; and &lt;strong&gt;Receiver (Your Friend)&lt;/strong&gt;—who want confidential, authentic, and integral communication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Generate Key Pairs and Certificates (One-time Setup)
&lt;/h3&gt;

&lt;p&gt;Each party performs these actions once.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generate key pairs&lt;/strong&gt; (private and public):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  openssl genrsa &lt;span class="nt"&gt;-out&lt;/span&gt; private_key.pem 2048
  openssl rsa &lt;span class="nt"&gt;-in&lt;/span&gt; private_key.pem &lt;span class="nt"&gt;-pubout&lt;/span&gt; &lt;span class="nt"&gt;-out&lt;/span&gt; public_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Request and obtain digital certificates&lt;/strong&gt; from a CA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Certificate Signing Request (CSR):
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-new&lt;/span&gt; &lt;span class="nt"&gt;-key&lt;/span&gt; private_key.pem &lt;span class="nt"&gt;-out&lt;/span&gt; request.csr
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;Submit CSR to CA (Let's Encrypt, DigiCert, or Internal CA).&lt;/li&gt;
&lt;li&gt;CA verifies your identity and issues signed certificates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Sending an Encrypted and Signed Message
&lt;/h3&gt;

&lt;p&gt;Sender wants to send the message: &lt;code&gt;"You are in danger"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hash&lt;/strong&gt; the message (SHA-256):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   SHA256&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"You are in danger"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sign the hash&lt;/strong&gt; with sender's private key (Digital Signature):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   Signature &lt;span class="o"&gt;=&lt;/span&gt; Encrypt&lt;span class="o"&gt;(&lt;/span&gt;Hash, Sender_Private_Key&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Encrypt message&lt;/strong&gt; with receiver's public key (from receiver's certificate):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   Encrypted_Message &lt;span class="o"&gt;=&lt;/span&gt; Encrypt&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"You are in danger"&lt;/span&gt;, Receiver_Public_Key&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sender transmits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encrypted message&lt;/li&gt;
&lt;li&gt;Digital signature&lt;/li&gt;
&lt;li&gt;Sender’s certificate (contains public key and identity signed by CA)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Receiving and Verifying the Message
&lt;/h3&gt;

&lt;p&gt;Receiver does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify sender's certificate&lt;/strong&gt; using CA’s public key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirms authenticity of sender's public key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decrypt the message&lt;/strong&gt; using receiver's private key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   Message &lt;span class="o"&gt;=&lt;/span&gt; Decrypt&lt;span class="o"&gt;(&lt;/span&gt;Encrypted_Message, Receiver_Private_Key&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Decrypt sender's signature&lt;/strong&gt; using sender's public key (extracted from sender's verified certificate):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   Sender_Hash &lt;span class="o"&gt;=&lt;/span&gt; Decrypt&lt;span class="o"&gt;(&lt;/span&gt;Signature, Sender_Public_Key&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Hash&lt;/strong&gt; the decrypted message again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   Receiver_Hash &lt;span class="o"&gt;=&lt;/span&gt; SHA256&lt;span class="o"&gt;(&lt;/span&gt;Message&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compare the hashes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Matching hashes confirm the message is authentic and unaltered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use digital certificates and CA&lt;/strong&gt; to establish secure trust in public keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never share your private keys&lt;/strong&gt;: they're the foundation of your identity and security.&lt;/li&gt;
&lt;li&gt;Leverage existing trusted Certificate Authorities (&lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;Let’s Encrypt&lt;/a&gt;, &lt;a href="https://www.digicert.com/" rel="noopener noreferrer"&gt;DigiCert&lt;/a&gt;) or internal CAs for internal setups.&lt;/li&gt;
&lt;li&gt;Encryption and digital signatures complement each other—encryption provides confidentiality, while signatures ensure authenticity and integrity.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>cryptography</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🦄 How ToolJet Gained 20,000 GitHub Stars and 400 Contributors</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Mon, 21 Aug 2023 12:48:16 +0000</pubDate>
      <link>https://dev.to/tooljet/how-tooljet-gained-20000-github-stars-and-400-contributors-4ee0</link>
      <guid>https://dev.to/tooljet/how-tooljet-gained-20000-github-stars-and-400-contributors-4ee0</guid>
      <description>&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;Open source projects aim to gain momentum by attracting an ample user base and contributors, establishing a thriving and self-sustaining venture that resonates with the intended audience. However, the crux of the matter is discovering effective strategies to captivate users and developers for project reach and engagement.&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%2Fbwrh37kjpwizmgf1vzs1.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%2Fbwrh37kjpwizmgf1vzs1.png" alt=" " width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://github.com/tooljet/tooljet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; stands as an exemplary open-source low-code platform that empowers both developers and business users to swiftly craft applications by establishing direct connections with diverse databases and services. Our journey has led us to a significant milestone of 20,000 GitHub stars, accompanied by a vibrant community of 400 contributors. This achievement not only reflects numerical growth but also underscores our evolution into a thriving ecosystem of engaged users and contributors. In the spirit of fostering collaboration and learning, we're eager to share some of the strategic initiatives that have propelled our progress. It is our hope that these insights may provide valuable guidance to fellow open-source projects seeking to flourish in a similar manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Awareness Building
&lt;/h3&gt;

&lt;p&gt;Cultivating awareness and garnering a dedicated user base is essential for sustained growth and development. A powerful strategy to achieve this is by actively engaging with platforms that cater to tech-savvy enthusiasts, such as Reddit's &lt;a href="https://www.reddit.com/r/selfhosted" rel="noopener noreferrer"&gt;/r/selfhosted&lt;/a&gt;. This subreddit serves as a vibrant hub for discussions and recommendations surrounding self-hosted software and projects. By participating in relevant discussions, sharing valuable insights, and showcasing the unique features and benefits of your open-source project, you can tap into a community of individuals who are already enthusiastic about self-hosting solutions. This interaction not only brings your project to their attention but also establishes a direct line of communication, allowing you to receive feedback, address concerns, and foster genuine connections with potential users and contributors. In doing so, you not only increase the visibility of your open-source project but also lay the foundation for a loyal and engaged user base that can greatly contribute to the project's success over time.&lt;/p&gt;

&lt;p&gt;Here’s how the subreddit /selfhosted helped us to boost the &lt;a href="https://dev.to/comments/108adko/tooljet_20_opensource_lowcode_framework_for/"&gt;ToolJet 2.0 launch&lt;/a&gt; that brought awareness about the prominent features.&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%2Fcakqi7ibi3sxc1v39sx3.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%2Fcakqi7ibi3sxc1v39sx3.png" alt=" " width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Strategic Launches
&lt;/h3&gt;

&lt;p&gt;By strategically timing and presenting significant updates on  platforms like Product Hunt, open-source projects can tap into a community of tech enthusiasts, innovators, and potential collaborators. In this case, Product Hunt's engaged audience, always on the lookout for novel solutions, offers a unique opportunity to showcase new features and improvements. This exposure not only generates interest and feedback but also creates a sense of excitement around the project's progress.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/products/tooljet#tooljet-0-5-3" rel="noopener noreferrer"&gt;Our debut on Product Hunt&lt;/a&gt; propelled ToolJet to the top spot as the &lt;strong&gt;Product of the Day&lt;/strong&gt;🔥 and an impressive 3rd position in the coveted &lt;strong&gt;Product of the Week&lt;/strong&gt; rankings&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%2F9vxd8u9sb1uiww650qs1.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%2F9vxd8u9sb1uiww650qs1.png" alt=" " width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Content Enablement
&lt;/h3&gt;

&lt;p&gt;Leveraging current trends on platforms like DEV can maximise product visibility. By constant content production and aligning them with trending topics, you can effectively capture the attention of a wide audience. This strategy was supportive for us in the past, with our previous DEV blogs reaching thousands of viewers. For instance, our articles on building internal tools with ChatGPT's integration  garnered substantial engagement, demonstrating the power of leveraging trending discussions to enhance content visibility and engagement.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/tooljet" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F5047%2F8494f13c-ffc6-4700-9c7d-f95b8280d9bd.png" alt="ToolJet" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F109823%2Fb91b8a34-cb9b-4f8e-b151-34a1d5b5c3ba.jpeg" alt="" width="800" height="800"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/tooljet/building-an-intelligent-crm-using-chatgpt-postgresql-and-tooljet-4jod" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Building an intelligent CRM using ChatGPT, PostgreSQL, and ToolJet&lt;/h2&gt;
      &lt;h3&gt;Shubhendra Singh Chauhan for ToolJet ・ May 22 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#chatgpt&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lowcode&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/tooljet" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F5047%2F8494f13c-ffc6-4700-9c7d-f95b8280d9bd.png" alt="ToolJet" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F464555%2F37d4c2e0-271e-4b9b-a5dc-a70f856639b1.jpeg" alt="" width="460" height="460"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/tooljet/creating-a-sql-generator-app-with-chatgpt-postgresql-and-tooljet-e2k" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating a SQL generator app with ChatGPT, PostgreSQL, and ToolJet&lt;/h2&gt;
      &lt;h3&gt;Teja Kummarikuntla for ToolJet ・ Mar 21 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#sql&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tooljet&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#chatgpt&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  4. Collaborative Outreach
&lt;/h3&gt;

&lt;p&gt;Engaging in collaborative outreach allows open-source projects to establish symbiotic partnerships with like-minded companies, nurturing mutual growth and innovation. Identifying win-win opportunities becomes pivotal in this endeavor, ensuring that both parties gain value from the collaboration. Equally crucial is setting clear, prioritized outcomes to steer efforts effectively. Measuring results and gleaning insights from these collaborations then forms the foundation for future engagements. Notable instances of such collaborations for ToolJet, include with industry leaders like &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7064241432735215618?updateEntity[%E2%80%A6]pdate%3A%28V2%2Curn%3Ali%3Aactivity%3A7064241432735215618%29" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=HJ91dyD1AQg" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;, &lt;a href="https://twitter.com/GitHubCommunity/status/1580648136632324096" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://twitter.com/mariadb/status/1683958301778939906" rel="noopener noreferrer"&gt;MariaDB&lt;/a&gt;, and more, where joint efforts have led to address the relevant audience effectively.&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%2Fu847jwy7qiiz4xgpg9jc.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%2Fu847jwy7qiiz4xgpg9jc.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Nurturing Contributor Community
&lt;/h3&gt;

&lt;p&gt;Cultivating an engaged project repository is crucial for nurturing community growth and maintaining a responsive development environment. Simultaneously, our active approach to encouraging contributor engagement is highlighted by our strategic emphasis on showcasing ToolJet’s expanding contributor base. This is achieved by creating and managing accessible &lt;a href="https://github.com/tooljet/tooljet/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;good first issues&lt;/a&gt;, which offer contributors opportunities to address specific product concerns. Through this approach, we not only strengthen our community but also enhance the project’s overall quality by collaboratively addressing pertinent issues.&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%2Fwja5tx0jj06pbox9kh38.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%2Fwja5tx0jj06pbox9kh38.png" alt=" " width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Community Engagement
&lt;/h3&gt;

&lt;p&gt;Active engagement with users plays a pivotal role in nurturing meaningful interactions within a project's user base. Recognizing the significance of direct communication, ToolJet regularly hosts &lt;a href="https://www.youtube.com/playlist?list=PL08nijlgUjii3yKUSiBCRHxGF1JnWJiJp" rel="noopener noreferrer"&gt;community calls&lt;/a&gt; in the last week of every month. These sessions provide an opportunity not only to share updates on releases and demonstrate features but also to educate users about specific usage aspects and best practices.  These collaborative interactions encourage open discussions and valuable feedback from the community. The insights garnered from these conversations serve as a valuable resource for internal decisions, enabling them to refine and enhance their products based on community expectations and requirements.&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%2Feb85izbkgci8mor91idk.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%2Feb85izbkgci8mor91idk.png" alt=" " width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Evangelizing on the ground
&lt;/h3&gt;

&lt;p&gt;Elevating awareness of the open-source product through active engagement in relevant conferences and meet-ups stands as a potent strategy. These gatherings offer an optimal platform to spotlight the product's value, advantages, and distinctive attributes directly to a receptive audience. Notably, ToolJet has consistently participated in open source conferences like &lt;a href="https://www.youtube.com/watch?v=tiPXUjvXTgo&amp;amp;t=412s" rel="noopener noreferrer"&gt;IndiaFOSS&lt;/a&gt; and &lt;a href="https://www.youtube.com/watch?v=sV-AMLz3r-o&amp;amp;t=221s" rel="noopener noreferrer"&gt;FOSS Hack&lt;/a&gt; hackathons, leveraging the conversations, informative exchanges, and the cultivation of connections. These events effectively disseminate product information within a community that shares common interests and requirements. This proactive approach not only builds brand recognition but also positions the open-source product as a pertinent and influential solution within its niche.&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%2Fcryvrzogwl3hwtzgxga4.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%2Fcryvrzogwl3hwtzgxga4.png" alt=" " width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;As we reflect on this milestone, it's essential to recognize that each platform offers a unique landscape and experimentation is key. What worked for us might require adaptation for others. The journey is one of perpetual learning, where embracing new approaches, seeking valuable feedback, and nurturing authentic connections contribute to a thriving open-source ecosystem. So, while our story may inspire, it's your experimentation and commitment that will shape your own success story, leading to impactful stars, contributors, and accomplishments. We sincerely hope that these insights find their way into your project's growth journey. Thank you for taking the time to read through and explore our journey.&lt;/p&gt;

&lt;p&gt;Feeling intrigued by our journey? Take a closer look at &lt;a href="http://github.com/tooljet/tooljet" rel="noopener noreferrer"&gt;ToolJet on GitHub&lt;/a&gt; and consider giving it a star⭐️ if it captures your interest. Dive into the project's potential by exploring the &lt;a href="https://github.com/tooljet/tooljet/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3Aup-for-grabs" rel="noopener noreferrer"&gt;good first issues and up for grabs&lt;/a&gt; – your contribution could be the next step towards shaping our collective endeavor.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build an AWS S3 Browser with ToolJet</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Thu, 03 Aug 2023 10:05:53 +0000</pubDate>
      <link>https://dev.to/tooljet/build-an-aws-s3-browser-with-tooljet-56d4</link>
      <guid>https://dev.to/tooljet/build-an-aws-s3-browser-with-tooljet-56d4</guid>
      <description>&lt;p&gt;Amazon S3 (Simple Storage Service) has emerged as a popular choice due to its reliability, scalability, and cost-effectiveness. S3 service provides developers with a flexible object storage service, making it a great option for storing and retrieving any amount of data at any time.&lt;/p&gt;

&lt;p&gt;However, interacting with Amazon S3 programmatically can sometimes be a daunting task. In this tutorial, we will introduce you to an efficient approach that simplifies the process of building a powerful AWS S3 Browser App without the need for extensive development work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ToolJet?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; is an open-source low-code framework that enables us to build full-stack web applications within a few minutes. With ToolJet, you can create standalone fully-functional full-stack applications or embed applications into other websites.&lt;/p&gt;

&lt;p&gt;ToolJet allows you to build applications that use relational and non-relational databases, REST APIs, OpenAI technologies, and cloud storage like &lt;a href="https://cloud.google.com/storage" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt;, &lt;a href="https://aws.amazon.com/pm/serv-s3" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, and &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;Minio&lt;/a&gt;. It is an excellent development tool helping individuals, developers, and businesses create and ship products faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before we continue&lt;/strong&gt;&lt;br&gt;
I would be super happy if you could give us a star! And let me also know in the comments section. ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&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%2F8nfnzw4wf7oybaclylqd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nfnzw4wf7oybaclylqd.gif" alt="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;To develop this application, we will utilize ToolJet along with its S3 integration, enabling us to perform various operations. These operations include fetching a list of all available buckets, retrieving data from selected buckets, and uploading data into the chosen buckets.&lt;/p&gt;

&lt;p&gt;Before diving into the technical aspects, let's take a look at the application's interface and its overall functionality. The application features two tabs. The first tab is called "Browser," which allows users to browse through the existing buckets and explore the data contained within them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser
&lt;/h3&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%2Fl2jsw77fi3dsaffzdj1o.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%2Fl2jsw77fi3dsaffzdj1o.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Uploader
&lt;/h3&gt;

&lt;p&gt;In the second tab, users can upload data into a selected bucket. This feature allows them to choose a specific bucket and then upload their desired data into it.&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%2F9sungepl3wkozp5yun8c.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%2F9sungepl3wkozp5yun8c.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To understand how this application is built, the user interface (UI) and its functional behavior are developed using ToolJet's UI components and their properties. On the other hand, the operations related to Amazon S3 are facilitated through the AWS S3 integration provided by ToolJet. This integration allows the application to interact with Amazon S3 and perform specific actions. For this tutorial, three specific S3 operations&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%2F9cc4uiy6e9zgi19c7uqw.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%2F9cc4uiy6e9zgi19c7uqw.png" alt=" " width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"List Bucket": This operation enables the application to retrieve a list of all available buckets in the Amazon S3 account.&lt;/li&gt;
&lt;li&gt;"List Objects in Bucket": With this operation, the app can fetch the data inside a selected bucket, displaying its contents to the user in the "Browser" tab.&lt;/li&gt;
&lt;li&gt;"Upload Object": The "Upload Object" operation allows users to select a bucket and then upload their chosen data into that bucket using the functionality available in the second tab of the application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the purpose of this tutorial, we have limited the operations to only three; however, it's worth mentioning that the AWS S3 integration offers a wide range of other operations as well. You can explore these additional operations and incorporate them into the application to build any required features beyond the tutorial's scope.&lt;/p&gt;

&lt;p&gt;Create a ToolJet Application&lt;br&gt;
To kickstart, let's guide you through creating a new ToolJet application. Whether you are using the cloud-based version of ToolJet or the self-hosted version, the steps are straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign up or Sign in: Visit tooljet.com and sign up for a free account if you don't have one. If you already have an account, sign in.&lt;/li&gt;
&lt;li&gt;Dashboard: After signing in, you will be taken to the ToolJet dashboard. Here, you will find an overview of your existing applications and access to other functionalities.&lt;/li&gt;
&lt;li&gt;Create a New App: To create a new application, click on the "Create New App" button, which will take you to the 'Application Builder'.&lt;/li&gt;
&lt;li&gt;Name the application: You can name the application of your choice from the top left corner of the application builder; we can call it "Inventory and Orders Manager."&lt;/li&gt;
&lt;/ol&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%2Fzuzfk3t669ho2v4gy8cg.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%2Fzuzfk3t669ho2v4gy8cg.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to AWS S3
&lt;/h3&gt;

&lt;p&gt;To perform all the operations from ToolJet to S3, the first step is to configure and establish the connection with the data source. Here's how you can do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return to the ToolJet Dashboard.&lt;/li&gt;
&lt;li&gt;From the left sidebar, navigate to "Global Datasources."&lt;/li&gt;
&lt;li&gt;Click on "Add new Datasource."&lt;/li&gt;
&lt;li&gt;In the gallery of data sources, search for "AWS S3" and select it.&lt;/li&gt;
&lt;li&gt;Fill in the required details and test the connection before saving it. &lt;/li&gt;
&lt;/ol&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%2Fb79v0487z4liihd4dq3x.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%2Fb79v0487z4liihd4dq3x.png" alt=" " width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Aspects of the Application Builder
&lt;/h3&gt;

&lt;p&gt;Let's explore the major areas of the Application Builder that will ease your development process with ToolJet.&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%2Faml8osct3n0bzgnmqanq.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%2Faml8osct3n0bzgnmqanq.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Canvas&lt;/strong&gt;: Canvas is your playground for building your application's user interface (UI). Here, you can visually design and arrange the components of your application's interface. Drag and drop components onto Canvas to create a seamless and user-friendly UI for your apps.&lt;br&gt;
&lt;strong&gt;2. Components Manager&lt;/strong&gt;: Located on the right side of the Application Builder, the Components Manager provides you with a comprehensive list of components you can utilize in your application. These pre-built components serve various purposes, such as buttons, forms, tables, etc. With a wide range of components, you can quickly assemble a powerful and visually appealing UI for your app.&lt;br&gt;
&lt;strong&gt;3. Query Panel&lt;/strong&gt;: The expandable Query Panel is at the bottom of the Application Builder. This panel plays a crucial role in connecting your application to data sources. It allows you to list and access the connected data sources, seamlessly integrating your Google Sheets data and other data sources. By selecting a data source from the Query Panel, you can create queries to perform specific operations and retrieve data dynamically.&lt;br&gt;
&lt;strong&gt;4. Left sidebar&lt;/strong&gt;: The Left Sidebar provides convenient access to features such as creating and managing pages, inspecting components, and the debugger for efficient development and debugging of applications.&lt;/p&gt;

&lt;p&gt;Now that you are familiar with the Application Builder, let's dive deeper into each area and explore how you can leverage its features to build the application from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Application.
&lt;/h2&gt;

&lt;p&gt;Now that we have explored the ToolJet Application Builder, it's time to dive into building the application step by step. As you know, this app consists of two tabs, so let's begin the development process by creating the "Browser" tab first. We'll focus on implementing the functionality that allows users to browse through the existing buckets and explore the data inside them. Let's get started!&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%2F1fd4s15zie3zf02534bx.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%2F1fd4s15zie3zf02534bx.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a first step, let's set up the navbar for the application. To do this, we'll be using the "container" component, which will serve as the navbar. You can find the "container" component in the components list, and then simply drag and drop it onto the canvas. Once placed, arrange it at the top of the page to ensure it functions as a proper navbar.&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%2Fyxvf2oxiiwzxzpaffw3e.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%2Fyxvf2oxiiwzxzpaffw3e.png" alt=" " width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's customize the appearance of the navbar. You can choose any color you prefer, or you can use the hash code &lt;code&gt;#2C3748&lt;/code&gt; to match the color shown in the image. To set the color, click on the container component and navigate to the "Styles" tab in the components manager on the right side. Then, update the value of the "Background Color" field to your desired color.&lt;/p&gt;

&lt;p&gt;Now, let's add a title to the navbar. Search for the "text" component in the components list and add it on top of the current container. To update the default text of the component, go to its properties and modify the value of the "Text" field to "AWS S3 File Browser" or any other title you prefer.&lt;/p&gt;

&lt;p&gt;Similarly, for the logout icon on the right side of the navbar, you can use the "icon" component with "IconLogout" as the value for the "Icon" field in the component properties.&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%2Fhcvv1nscmj8f1b6vnn78.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%2Fhcvv1nscmj8f1b6vnn78.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the UI for the Navbar is complete, there is one more functional aspect to address: making the logout icon operational. To achieve this, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the logout icon component to select it.&lt;/li&gt;
&lt;li&gt;In the component properties, locate the "Events" section and expand it.&lt;/li&gt;
&lt;li&gt;Click on the "+ Add handler" button to add a new event handler.&lt;/li&gt;
&lt;li&gt;Set the following values for the event handler:&lt;/li&gt;
&lt;li&gt;Event: "On click"&lt;/li&gt;
&lt;li&gt;Action: "Logout"&lt;/li&gt;
&lt;/ol&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%2Fhkljxkbabbwba0j5v13t.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%2Fhkljxkbabbwba0j5v13t.png" alt=" " width="657" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By setting up the event handler in this way, when a user clicks on the logout icon, the currently logged-in user will be signed out and need to log in again to access the application.&lt;/p&gt;

&lt;p&gt;With this implementation, the logout functionality will be fully operational, and users can securely log out from the application.&lt;/p&gt;

&lt;p&gt;To accommodate both the 'Browser' and 'Uploader' sections of the application, we will utilize a Tabs component. Let's add the Tabs component to the canvas and center it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the Tabs component and access its properties.&lt;/li&gt;
&lt;li&gt;Update the 'tabs' field value to the following:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{[
  { title: 'Browser', id: '0' },
  { title: 'Uploader', id: '1' }
]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration will ensure that the Tabs component displays the two required tabs, one for the 'Browser' section and the other for the 'Uploader' section. Additionally, you can remove any default components within the Tabs component to create a clean slate and set the "Highlight Colour" from Styles to #ec912dff  for further customization.&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%2Flfheqd3bdm2zippy98ge.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%2Flfheqd3bdm2zippy98ge.png" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Browser Tab
&lt;/h3&gt;

&lt;p&gt;In the 'Browser' tab, we will be working with two tables. The first table will list all available buckets, and the second table will display all the objects within a selected bucket.&lt;/p&gt;

&lt;p&gt;Let's start by adding two tables, table1 and table2, to the tab component. Adjust the leftmost table (table1) to display only one column by configuring its properties accordingly. Then, arrange the other table (table2) to the right of table1.&lt;/p&gt;

&lt;p&gt;For table1, we will customize it by turning off all options in the properties except for the 'Show search box' field. Additionally, set the table's border radius to '5' from the 'Styles' tab of the components manager.&lt;/p&gt;

&lt;p&gt;Similarly, for table2, we can turn off all options, while keeping the 'Show search box' enabled to help with data search once the table is loaded.&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%2F5bxiz4xc2kd8d657scki.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%2F5bxiz4xc2kd8d657scki.png" alt=" " width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above table2, let's add an "Input" component along with two buttons. One button will be used for searching, and the other button, placed to the right, will be labeled "Uploader."&lt;/p&gt;

&lt;p&gt;By implementing these steps, the 'Browser' tab will be visually organized, with table1 displaying the available buckets, table2 showing the objects within the selected bucket, and the corresponding search and uploader functionalities appropriately positioned above the second table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the S3 queries
&lt;/h3&gt;

&lt;p&gt;Now, let's add functionality to the UI by creating our first query, which will enable us to fetch all the buckets from the S3 data source.&lt;/p&gt;

&lt;p&gt;To get started, open the query panel by pulling it up from the bottom of the screen. Next, click on the '+ Add' button, or you can directly choose the previously connected S3 data source from the available options.&lt;/p&gt;

&lt;p&gt;Once you select the data source, a new window will appear in the query panel. Here, you can give the query a name, such as 'getBuckets,' and choose the operation 'List buckets.' Then, click on the 'Preview' button to test a sample request and hit "Create" to save it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: By enabling "Run this query on application load," your queries will automatically execute as the app loads. This means that the necessary data will be fetched and displayed without any additional user input. It helps ensure that the app starts with the most up-to-date information.&lt;/p&gt;
&lt;/blockquote&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%2Famiipfzhe8j567qx5r4e.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%2Famiipfzhe8j567qx5r4e.png" alt=" " width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this query in place, we will be able to fetch a list of all the buckets, which is needed to populate table1 in the 'Browser' tab.&lt;/p&gt;

&lt;p&gt;To connect the query with the table1 component, follow these simple steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the table1 component to select it, and then navigate to its properties.&lt;/li&gt;
&lt;li&gt;In the properties section, find the "Table data" field and replace its current value with &lt;code&gt;{{queries.getBuckets.data.Buckets}}&lt;/code&gt; . This will establish the connection between the query and the component, resulting in the table being populated with the data retrieved from the 'getBuckets' query.&lt;/li&gt;
&lt;li&gt;If necessary, you can further customize the table by removing any unnecessary columns, leaving only the "Buckets" column to display the names of the available buckets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these adjustments, table1 will dynamically display the list of available buckets, we can similarly do the same for table2 when the query for fetching objects of the selected bucket is ready.&lt;/p&gt;

&lt;p&gt;To bring all the objects from a selected bucket in table1 to table2, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new query named "getObjects" and select the operation "List objects in a bucket". It's essential to dynamically fill the value of the field "Bucket" by selecting a row in table1. To do this, insert &lt;code&gt;{{components.table1.selectedRow.Name}}&lt;/code&gt; . Additionally, set the maxkeys value to 1000.&lt;/li&gt;
&lt;li&gt;After creating the query, click on a row in table1 and click "preview" to test the query. Save it by clicking on the "Create" button.&lt;/li&gt;
&lt;li&gt;Now, just like we connected the "getBuckets" query to table1, let's connect the "getObjects" query to table2. Update the "Table data" property of table2 to &lt;code&gt;{{queries.getObjects.data.Contents}}&lt;/code&gt; .&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As part of the desired functionality, we want to update &lt;code&gt;table2&lt;/code&gt; every time a new row is clicked in &lt;code&gt;table1&lt;/code&gt;. To achieve this, go to the properties of table1 and add an event handler with the following settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event: Row clicked&lt;/li&gt;
&lt;li&gt;Action: Run query&lt;/li&gt;
&lt;li&gt;Query: getObjects&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%2Fc8jxvzm7wnbcyhymicqu.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%2Fc8jxvzm7wnbcyhymicqu.png" alt=" " width="617" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these changes, the "getObjects" query will now be executed and populate table2 every time a new row is selected in table1.&lt;/p&gt;

&lt;p&gt;With these steps, both tables in the "Browser" tab are now fully functional. Users can make use of the components' search feature to easily find specific items. Additionally, there are opportunities to implement custom search functionality for the TextInput field above table2, providing even more search capabilities.&lt;/p&gt;

&lt;p&gt;Regarding the "Upload files" button, its purpose is to take users to the "Uploader" tab when clicked. To achieve this, you can add an event handler to the button from its properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event: On Click&lt;/li&gt;
&lt;li&gt;Action: Control component&lt;/li&gt;
&lt;li&gt;Component: tabs1&lt;/li&gt;
&lt;li&gt;Action: Set current tab&lt;/li&gt;
&lt;li&gt;ID: 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing this event handler, the "Upload files" button will successfully navigate users to the "Uploader" tab, where we will explore how to upload files to a selected bucket.&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%2Fbq4m6ct4thgyymgssxyh.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%2Fbq4m6ct4thgyymgssxyh.png" alt=" " width="670" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Uploader Tab
&lt;/h2&gt;

&lt;p&gt;In the 'Uploader' tab, we need to include multiple fields that will take inputs such as the bucket name, file name, and the file to be uploaded. To build the UI, we can use the components shown in the image, and since the 'Browser' tab is already set up, arranging these components will be straightforward.&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%2Fhgeqngecxeeo69rsgvas.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%2Fhgeqngecxeeo69rsgvas.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the Dropdown component, we want to list all the bucket names as options. To achieve this, we can set the option values and option labels to &lt;code&gt;{{queries.getBuckets.data.Buckets.map(item =&amp;gt; item.Name)}}&lt;/code&gt; . This will dynamically populate the dropdown options with all the bucket names retrieved from the 'getBuckets' query. Additionally, we can set the Default option to &lt;code&gt;{{components.table1.selectedRow.Name}}&lt;/code&gt; , which will set the dropdown's default value to the selected row of table1 in the 'Browser' tab.&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%2Fxuzxjk44565z6qtds5fw.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%2Fxuzxjk44565z6qtds5fw.png" alt=" " width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the S3 queries
&lt;/h3&gt;

&lt;p&gt;To enable file uploading to the selected S3 bucket, we will create a new query called 'uploadObject' by following these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on '+ Add' in the query panel and select the S3 data source. Name the query 'uploadObject' and choose the 'Upload object' operation. This will open a window requesting details of the file to be uploaded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Dynamically fill the query fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bucket: &lt;code&gt;{{components.table2.selectedRow.Name}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Key: &lt;code&gt;{{components.textinput2.value}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Content Type: &lt;code&gt;{{components.filepicker1.file[0].type}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Upload Data: &lt;code&gt;{{components.filepicker1.file[0].dataURL}}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ejc9yv8dexelf8en99y.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%2F5ejc9yv8dexelf8en99y.png" alt=" " width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note that the IDs of the components in the interpolation text may differ based on your component naming.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To test the query, you can add some dummy data to the fields, click 'Preview,' and then 'Create' to save the query.&lt;/li&gt;
&lt;li&gt;To trigger this query on the 'Upload' button click, you can add an event handler that runs the 'uploadObject' query upon clicking the button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these steps, you have successfully built an application that allows you to browse through S3 buckets and perform file uploads. While the tutorial's scope is limited to these features, the S3 integration offers a wide range of operations that you can utilize to extend the application's functionality according to specific requirements.&lt;/p&gt;

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

&lt;p&gt;To continue your journey and explore more possibilities with ToolJet, we encourage you to visit and try out our further tutorials here at blog.tooljet.com. Our blog is filled with tutorials, guides, and helpful resources that can further enhance your knowledge and help you unlock the full potential of ToolJet.&lt;/p&gt;

&lt;p&gt;In addition to the blog, we also recommend referring to our comprehensive documentation available at &lt;a href="https://docs.tooljet.com" rel="noopener noreferrer"&gt;docs.tooljet.com&lt;/a&gt;. The documentation covers a wide range of topics, including detailed explanations of components, advanced query techniques etc.&lt;/p&gt;

&lt;p&gt;Furthermore, we invite you to join our vibrant community on our &lt;a href="https://tooljet.com/slack" rel="noopener noreferrer"&gt;Slack workspace&lt;/a&gt;. By joining our community, you can connect with other active ToolJet users, share your experiences, and seek assistance when needed.&lt;/p&gt;

&lt;p&gt;Once again, congratulations on your achievements so far! We sincerely thank you for reading this tutorial and choosing ToolJet for your application development needs. We hope this tutorial has provided you with valuable insights and empowered you to build powerful web applications with ease.&lt;/p&gt;

&lt;p&gt;We look forward to seeing what you create next with ToolJet. Happy building!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build an Inventory and Order Management App with Google Sheets and ToolJet</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Mon, 24 Jul 2023 12:57:34 +0000</pubDate>
      <link>https://dev.to/tooljet/building-an-inventory-and-order-management-app-with-google-sheets-and-tooljet-4jjf</link>
      <guid>https://dev.to/tooljet/building-an-inventory-and-order-management-app-with-google-sheets-and-tooljet-4jjf</guid>
      <description>&lt;p&gt;Efficient supply chain management is vital for businesses aiming to optimize operations and drive productivity. When it comes to building a cost-effective supply chain management tool, the combination of Google Sheets as a database and ToolJet as a powerful low-code platform can be a game-changer.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will delve into the process of leveraging Google Sheets and harnessing ToolJet's capabilities to construct a robust and streamlined inventory and order management app which can be tailored to your specific&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ToolJet?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; is an open-source low-code framework that enables us to build full-stack web applications within a few minutes. With ToolJet, you can create standalone fully-functional full-stack applications or embed applications into other websites.&lt;/p&gt;

&lt;p&gt;ToolJet allows you to build applications that use relational and non-relational databases, REST APIs, OpenAI technologies, and cloud storage like &lt;a href="https://cloud.google.com/storage" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt;, &lt;a href="https://aws.amazon.com/pm/serv-s3" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, and &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;Minio&lt;/a&gt;. It is an excellent development tool helping individuals, developers, and businesses create and ship products faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before we continue&lt;/strong&gt;&lt;br&gt;
I would be super happy if you could give us a star! And let me also know in the comments section. ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&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%2F8nfnzw4wf7oybaclylqd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nfnzw4wf7oybaclylqd.gif" alt="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;To give you a glimpse of what awaits you, let's start by showcasing the complete application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dashboard
&lt;/h3&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%2Fomiceteth6o7dk77lzp7.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%2Fomiceteth6o7dk77lzp7.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Dashboard page provides a comprehensive overview of your orders and inventory. It offers a visually appealing glimpse of your overall order status and inventory counts. With this at-a-glance view, you can quickly assess the health of your supply chain and make informed decisions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Inventory Manager
&lt;/h3&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%2Ft0m9xrgibpckdg8xsqn4.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%2Ft0m9xrgibpckdg8xsqn4.png" alt=" " width="800" height="457"&gt;&lt;/a&gt;&lt;br&gt;
The Inventory Manager page allows you to efficiently manage your products and track their status. A table displays the products along with their quantity, price, and category. You have full control over your inventory, with options to add, delete, and edit products seamlessly. This ensures that you always have accurate information about your stock and can easily make necessary updates.&lt;/p&gt;
&lt;h3&gt;
  
  
  Orders Manager
&lt;/h3&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%2Fh9uhl78kjvgxr1ytk772.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%2Fh9uhl78kjvgxr1ytk772.png" alt=" " width="800" height="445"&gt;&lt;/a&gt;&lt;br&gt;
The Orders Manager page enhances the way to handle orders. It presents a Kanban view, allowing you to visualize and track orders in different statuses, such as todo, in progress, and completed. This intuitive interface enables you to effortlessly manage orders, add new ones, update their status, and delete them if needed. With the Orders Manager, you can ensure timely order fulfillment and provide exceptional customer service.&lt;/p&gt;

&lt;p&gt;Throughout the application, we leverage ToolJet components and the powerful Google Sheets integration provided by ToolJet. This integration allows you to connect your Google Sheets data to the application seamlessly. Given this tutorial brings a wide range of functionalities to implement, we'll be covering the essentials which can be followed to address the tailored needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a ToolJet Application
&lt;/h2&gt;

&lt;p&gt;To kickstart, let's guide you through creating a new ToolJet application. Whether you are using the cloud-based version of ToolJet or the self-hosted version, the steps are straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sign up or Sign in&lt;/strong&gt;: Visit tooljet.com and sign up for a free account if you don't have one. If you already have an account, sign in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard&lt;/strong&gt;: After signing in, you will be taken to the ToolJet dashboard. Here, you will find an overview of your existing applications and access to other functionalities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a New App&lt;/strong&gt;: To create a new application, click on the "Create New App" button, which will take you to the 'Application Builder'.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name the application&lt;/strong&gt;: You can name the application of your choice from the top left corner of the application builder; we can call it "Inventory and Orders Manager."&lt;/li&gt;
&lt;/ol&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%2Ftjlccpj2qk2huw1o7z1x.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%2Ftjlccpj2qk2huw1o7z1x.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Connect to Google Sheets
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Return to the ToolJet Dashboard.&lt;/li&gt;
&lt;li&gt;From the left sidebar, navigate to "Global Datasources."&lt;/li&gt;
&lt;li&gt;Click on "Add new Datasource."&lt;/li&gt;
&lt;li&gt;In the gallery of data sources, search for "Google Sheets" and select it.&lt;/li&gt;
&lt;li&gt;Choose "Read only" or "Read and write" based on your requirements. For this tutorial, we'll choose "Read and Write" to enable reading and writing data from Google Sheets.&lt;/li&gt;
&lt;li&gt;Proceed to authorize ToolJet to access your Google Sheets by following the prompts.&lt;/li&gt;
&lt;li&gt;Once the authorization process is completed, you can return to the Application Builder to continue the application development. &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Aspects of the Application Builder
&lt;/h3&gt;

&lt;p&gt;Let's explore the major areas of the Application Builder that will ease your development process with ToolJet.&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%2Fmw8k24ufzd4k0hv7b1cv.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%2Fmw8k24ufzd4k0hv7b1cv.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Canvas&lt;/strong&gt;: Canvas is your playground for building your application's user interface (UI). Here, you can visually design and arrange the components of your application's interface. Drag and drop components onto Canvas to create a seamless and user-friendly UI for your apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Components Manager&lt;/strong&gt;: Located on the right side of the Application Builder, the Components Manager provides you with a comprehensive list of components you can utilize in your application. These pre-built components serve various purposes, such as buttons, forms, tables, etc. With a wide range of components, you can quickly assemble a powerful and visually appealing UI for your app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query Panel&lt;/strong&gt;: The expandable Query Panel is at the bottom of the Application Builder. This panel plays a crucial role in connecting your application to data sources. It allows you to list and access the connected data sources, seamlessly integrating your Google Sheets data and other data sources. By selecting a data source from the Query Panel, you can create queries to perform specific operations and retrieve data dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Left sidebar&lt;/strong&gt;: The Left Sidebar provides convenient access to features such as creating and managing pages, inspecting components, and the debugger for efficient development and debugging of applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that you are familiar with the Application Builder, let's dive deeper into each area and explore how you can leverage its features to build a robust and efficient application for your Supply chain management.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build the Application.
&lt;/h2&gt;

&lt;p&gt;Now that we have explored the ToolJet Application Builder, it's time to dive into building the application step by step. &lt;br&gt;
As an initial step, let's create two pages for the "Inventory Manager" and the "Orders Manager." Additionally, we will design the left sidebar of the application to include navigation that allows users to switch between these pages effortlessly. To proceed with creating the pages and designing the left sidebar, follow these steps:&lt;/p&gt;

&lt;p&gt;Open the ToolJet Application Builder and navigate to the "Pages" section in the Left Sidebar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on "Pages" option in the left side bar to create a new page for the "Inventory Manager".&lt;/li&gt;
&lt;li&gt;Set the page name of your choice, you can name it as "Inventory Manager"&lt;/li&gt;
&lt;li&gt;Repeat the process to create a new page for the "Orders Manager".&lt;/li&gt;
&lt;li&gt;Toggle the "Disable Menu" in the Pages settings to hide the page navigation sidebar in the view mode.
&lt;/li&gt;
&lt;li&gt;Once both pages are created, you can set the application layout from the Global settings.&lt;/li&gt;
&lt;li&gt;Also, you can set the Background color of the Canvas to completely white, &lt;code&gt;#ffffff&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2F3dukmgcmb5soqb5kwcyw.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%2F3dukmgcmb5soqb5kwcyw.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's create a sidebar for the app to enhance navigation and provide an intuitive user experience. The sidebar will allow users to switch between different pages seamlessly. Follow these steps to add the sidebar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drag and drop the "Container" component from the Components Manager onto the Canvas.&lt;/li&gt;
&lt;li&gt;Position the container on the left side of the page and adjust its height as needed.&lt;/li&gt;
&lt;li&gt;Customize the appearance of the container by setting its color. For example, you can set the color to a vibrant green shade like #237A6F.&lt;/li&gt;
&lt;li&gt;To represent the different pages of the application, search for the "Icon" component in the Components Manager and add three icons to the sidebar container. These icons will serve as visual cues for navigation.&lt;/li&gt;
&lt;li&gt;Consider adding a logo or branding image using the Image component at the top of the sidebar container. This will provide a cohesive and professional look to your application.&lt;/li&gt;
&lt;/ol&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%2Fgbysygdkxfgbk615aj7k.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%2Fgbysygdkxfgbk615aj7k.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To enable navigation between pages by clicking on the icons in the sidebar, follow these steps:&lt;/p&gt;

&lt;p&gt;Click on the desired Icon component in the Container. The Components Manager will display the options to set properties and styles for the component in separate tabs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Properties tab, expand the "Events" section.&lt;/li&gt;
&lt;li&gt;Add an event handler for the "On click" event.&lt;/li&gt;
&lt;li&gt;Specify the action as "Switch page"&lt;/li&gt;
&lt;li&gt;Select the appropriate page from the dropdown list. For example, you can choose "Home" for the Home icon.&lt;/li&gt;
&lt;/ol&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%2Fympltjfhtkytf271fodh.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%2Fympltjfhtkytf271fodh.png" alt=" " width="800" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat these steps for the other pages you want to link to the sidebar icons. Adding event handlers to the icons establishes a connection between the click event and the respective pages, enabling smooth navigation within your application.&lt;/p&gt;

&lt;p&gt;By following these steps, you will successfully add a sidebar to your application, enhancing its visual appeal and providing users with a convenient way to navigate between pages.&lt;/p&gt;

&lt;p&gt;Moving on, we'll first focus on building the "Inventory Manager" page. This page will allow you to manage your products and track their status effectively. Let's get started.&lt;/p&gt;
&lt;h2&gt;
  
  
  Inventory Manager Page
&lt;/h2&gt;

&lt;p&gt;The Inventory Manager page offers a comprehensive view of product counts, with a table displaying product details and the "Add New Product" button that enables one to create/add a new product to the list of products. We will build the page in two steps, creating the UI and then connecting it to the Google Sheets data source.&lt;/p&gt;
&lt;h3&gt;
  
  
  Building the UI
&lt;/h3&gt;

&lt;p&gt;To start building the UI of the Inventory Manager page, let's divide the design into two segments. In the first segment, we'll focus on the page title and the interface to display the counts of products in different statuses.&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%2Fp4232hc7pwvc922uh1rp.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%2Fp4232hc7pwvc922uh1rp.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search for the "Text" component in the Component Manager and add it at the top left corner of the application. Once added, modify the default text to "Product Inventory" from properties and set its color to the same vibrant green used for the left sidebar design.&lt;/li&gt;
&lt;li&gt;Next, create count holders using the "Container" component. Place them side to the previously added text component with a distance, adjusting their size accordingly to accommodate the counts. Inside each container, add a "Text" component to display labels such as "Qty in Total," "Qty in Stock," "Low in Stock," and "Out of Stock." Further, add another "Text" component to hold the respective number values for each count.&lt;/li&gt;
&lt;li&gt;To separate the title and count sections visually, insert a "Divider" component below the "Product Inventory" text and the count containers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you will have successfully created the first segment of the Inventory Manager page. To continue building the second section of the Inventory Manager page, which includes a search input with a button and a button to open a modal, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search for the "TextInput" component in the Component Manager and add it below the divider. Set the border radius of the input field  6 using the styles tab of the component.&lt;/li&gt;
&lt;li&gt;Next to the search input, add a button component with an appropriate size, color, and border radius set to 6.&lt;/li&gt;
&lt;li&gt;Search for and add the "Modal" component to the extreme right of the search button, configure the properties and style of the button, give it a name such as "Add a Product" and set the desired color and border-radius. We will design the UI of the modal in the next step.&lt;/li&gt;
&lt;li&gt;Add a Table component below the search input and buttons that will hold all the product details. Set the table type to 'Classic' and the border radius to 12 using the styles tab of the table component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please refer to the provided image for the modal design and place the components inside the modal accordingly.&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%2Fod12cylotacm70odhp8s.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%2Fod12cylotacm70odhp8s.png" alt=" " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By following these steps, you will have successfully added the necessary components for the second section of the Inventory Manager page. The search input, buttons, and table will allow users to search for products, add new products, and view product details in a structured manner.&lt;/p&gt;

&lt;p&gt;To fetch data from Google Sheets, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obtain the Spreadsheet ID from the Google Sheets URL. Locate the ID by finding the string of characters after spreadsheets/d/ and before /edit#gid=.... For example, if the URL is &lt;a href="https://docs.google.com/spreadsheets/d/1n_Sm_NYQMwBanxGrHJaKvBJuz94U-xlfFJK3Knf32343OZS8/edit#gid=1311251363" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1n_Sm_NYQMwBanxGrHJaKvBJuz94U-xlfFJK3Knf32343OZS8/edit#gid=1311251363&lt;/a&gt;, the Spreadsheet ID is 1n_Sm_NYQMwBanxGrHJaKvBJuz94U-xlfFJK3Knf32343OZS8.&lt;/li&gt;
&lt;li&gt;In the Query pane click on the "Google Sheets" data source that you connected earlier. Choose the operation "Read data from a spreadsheet".&lt;/li&gt;
&lt;li&gt;In the second field, enter the Spreadsheet ID obtained in Step 1.&lt;/li&gt;
&lt;li&gt;Optionally, specify the range and sheet values to define the specific data you want to retrieve.&lt;/li&gt;
&lt;li&gt;Give a name to this query, such as "getProducts" or any name you choose.&lt;/li&gt;
&lt;li&gt;Click on the preview button to verify that the query successfully retrieves data from the spreadsheet.&lt;/li&gt;
&lt;li&gt;To automatically execute this query as the application loads, enable the option "Run this query on application load?".&lt;/li&gt;
&lt;li&gt;Save the query, and it will be successfully created for further use.&lt;/li&gt;
&lt;/ol&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%2Ffaf7ixpbvtano5lifv1f.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%2Ffaf7ixpbvtano5lifv1f.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By following these steps, you have created a query that fetches all the product data from the connected Google Sheets. This data can now be displayed in the table component on the Inventory Manager page. Additionally, the query can be executed automatically as the application loads, ensuring the table is populated with the latest data.&lt;/p&gt;

&lt;p&gt;Here's how you can populate the table with the data from the getProducts query&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the table component in the Application Builder.&lt;/li&gt;
&lt;li&gt;Go to the properties section of the table component.&lt;/li&gt;
&lt;li&gt;Locate the "Table data" field and update its value to 
&lt;code&gt;{{queries.getProducts.data}}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Once updated, the table should automatically populate with the data from the query.&lt;/li&gt;
&lt;li&gt;If the data does not appear immediately, you can manually run the "getProducts" query from the Query Panel, ensuring the table is populated with the query data.&lt;/li&gt;
&lt;li&gt;To display an image within the table component, select the corresponding column in the properties section. Within the column settings, set the column type to "Image". This configuration will automatically transform the URL values within the column into visible images.&lt;/li&gt;
&lt;/ol&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%2F2wsf7ybl7q95ivrqr5i4.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%2F2wsf7ybl7q95ivrqr5i4.png" alt=" " width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, you can arrange and customize the columns displayed in the table from the same properties section.&lt;/p&gt;

&lt;p&gt;To make the "Add a Product" modal functional and enable the addition of products to the configured Google Sheets, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Access the Query Panel and click on the Google Sheets Datasource to view the query options.&lt;/li&gt;
&lt;li&gt;Set the operation to "Append data to spreadsheet" to indicate that you want to add new data to the spreadsheet.&lt;/li&gt;
&lt;li&gt;Specify the Spreadsheet ID to identify the target spreadsheet where the data will be appended.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the "Rows" section, you will define the data to be added in a JSON format. The field values within the JSON will be dynamically updated from the input components.&lt;/p&gt;

&lt;p&gt;Here's an example of how the Rows value can be set: &lt;code&gt;[{"Name": "{{components.textinput4.value}}", "Description": "{{components.textarea1.value}}", "Price": "{{components.textinput2.value}}", "Category": "{{components.textinput5.value}}", "Status": "{{components.textinput3.value}}", "Image":"{{components.textinput7.value}}"}]&lt;/code&gt; Using the &lt;code&gt;{{}}&lt;/code&gt; notation, you can access the values from the respective input components filled in by the user.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save this query as "addProducts" to use it for adding products to the spreadsheet.&lt;/li&gt;
&lt;li&gt;To execute this query when the "Add Product" button is clicked, add an event to the button's properties. Set the event as "On click", the action as "Run Query", and select the "addProducts" query.&lt;/li&gt;
&lt;li&gt;Similarly, for the cancel button, add an event to close the modal when clicked. You can also include the same event to close the modal after executing the "Run Query" event for the "Add Product" button.&lt;/li&gt;
&lt;/ol&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%2F9rqolfcezqh1vfyqh8l6.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%2F9rqolfcezqh1vfyqh8l6.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By following these steps, you have set up the query to add new products to the Google Sheets. The "Add a Product" modal will capture the input from the user, and upon clicking the "Add Product" button, the data will be appended to the spreadsheet. The modal will also provide a cancel option to close the modal without adding the product.&lt;/p&gt;

&lt;p&gt;Now to dynamically update the values of the counters in the Inventory Manager page, we will utilize JavaScript code and ToolJet's RunJS query functionality. By implementing a custom script, we can calculate the product counts based on their statuses and return the results. This approach allows for real-time updates and accurate representation of the inventory status. Let's dive into the step-by-step process of executing the JavaScript code using the RunJS query and connecting the count values to the respective text components.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the Query Panel and select "RunJS" from the default datasource&lt;/li&gt;
&lt;li&gt;This brings up the RunJS query options. Save the query with the name "getProductsCount".&lt;/li&gt;
&lt;li&gt;Within the RunJS query, add the provided code snippet
&lt;/li&gt;
&lt;/ol&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;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProducts&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;In Stock&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;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inStockCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Low Stock&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;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lowStockCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Out of Stock&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;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outOfStock&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&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;inStockCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lowStockCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;outOfStock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;totalCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Save the "getProductsCount" query and ensure its execution on the successful completion of the "getProducts" query. To do that, On query success, add a event handler to the "getProducts" query to trigger "getProductsCount".&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%2Fjv37zscoopicjdoznpru.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%2Fjv37zscoopicjdoznpru.png" alt=" " width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations on successfully building the Inventory Manager page! You have learned how to design the UI, connect to Google Sheets as a data source, populate the table with data, add new products, and dynamically update the count values.&lt;/p&gt;

&lt;p&gt;Now that you have gained a solid understanding of how to build the UI and create queries in ToolJet, we will move forward at a slightly faster pace in the upcoming sections. With your newfound knowledge and proficiency, we will explore building the Orders Manager page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Orders Manager Page
&lt;/h3&gt;

&lt;p&gt;In the Orders Manager page, we will shift our focus to tracking and managing orders efficiently. To retrieve the order data, we will work with a different spreadsheet, where the product name of each order is indexed from the Product sheet using the product ID as a key. This process will involve utilizing query transformations to establish the necessary connections between datasets.&lt;br&gt;
Regarding UI, we will introduce a new component called Kanban, which visually represents orders in different stages.&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%2Fw7761yeyzaxsfnbgrm71.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%2Fw7761yeyzaxsfnbgrm71.png" alt=" " width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building the UI for the Orders Manager page follows a similar process to the previous sections. However, as mentioned, we introduce a new component called the Kanban. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the Kanban component to the page, following the image provided in the blog.&lt;/li&gt;
&lt;li&gt;Each card within the Kanban component should include three text components: one for the product name, one for the customer name, and one for the total price of the order.&lt;/li&gt;
&lt;li&gt;Customize the styling and layout of the Kanban component to match your design preferences.&lt;/li&gt;
&lt;li&gt;Top of this, the "Create an order" is a 'Modal' component and the text "All orders" is the text component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the UI of the modal for creating an orde. &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%2Fy3d9n0orafmqcb0td5dj.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%2Fy3d9n0orafmqcb0td5dj.png" alt=" " width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By following the provided image and incorporating the necessary components, you can create a Kanban view that effectively displays order details and set the modal for adding inputs that can be used to append data to the Orders Spreadsheet.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting the Queries
&lt;/h3&gt;

&lt;p&gt;We will utilize query transformations to combine information from different queries and enhance the data retrieval process. Specifically, we will focus on retrieving the orders data from a separate spreadsheet and associating the product name with each order using the product ID. This will enable us to present comprehensive and meaningful information within our application.&lt;/p&gt;

&lt;p&gt;To connect to a new spreadsheet using the same datasource and retrieve the orders data, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Google Sheets query within the ToolJet Query Panel.&lt;/li&gt;
&lt;li&gt;Set the operation for this query to "Read data from a Spreadsheet".&lt;/li&gt;
&lt;li&gt;Specify the appropriate Spreadsheet ID to identify the target spreadsheet containing the orders data.&lt;/li&gt;
&lt;li&gt;Save this query, you can name it as "getOrdersWithProductId"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This query will be able to bring the Orders data with product id in each object,  to retrieve the product name for each order using the product ID and also to add a new key called "columnId" based on the status(will be needed for Kanban component) , we will utilize query transformations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Toggle the "Enable transformations" option within the query options.&lt;/li&gt;
&lt;li&gt;Add the provided JavaScript code snippet in the code editor
&lt;/li&gt;
&lt;/ol&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;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProducts&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&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;productId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProductID&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;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prod&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;productId&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;productName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;product&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unknown Product&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ProductName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;columnId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&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="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;updatedData&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%2Fr0mcnzdcq989t5ksufqv.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%2Fr0mcnzdcq989t5ksufqv.png" alt=" " width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can save the query and preview it, and here is how you can spot the difference with and without the query transformations.&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="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;OrderDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;02/02/2023&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob Smith&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9876543210&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;456 Elm St, Town&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Call before delivery&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TotalPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;343&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;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="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;OrderDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;02/02/2023&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bob Smith&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ProductID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9876543210&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;456 Elm St, Town&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Call before delivery&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;TotalPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;343&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ProductName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;columnId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To connect the query response to the Kanban component and configure the column data and card data, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the Column Data field value of the Kanban component to the following JSON format: &lt;code&gt;[{ "id": "todo", "title": "Orders Placed" },{ "id": "inprogress", "title": "Shipped" },{ "id": "done", "title": "Delivered" }]&lt;/code&gt; This configuration will define the columns as "Orders Placed," "Shipped," and "Delivered" within the Kanban component.&lt;/li&gt;
&lt;li&gt;Set the Card Data field value of the Kanban component to &lt;code&gt;{{queries.getOrdersWithProductId.data}}&lt;/code&gt;. This connects the query response to the Kanban component, ensuring that the data is displayed correctly.&lt;/li&gt;
&lt;li&gt;To display the product name, customer name, and price in the respective card components, click on the text component within the first card of the first column. Set the text properties to &lt;code&gt;{{cardData.ProductName}}&lt;/code&gt; to display the product name, &lt;code&gt;{{cardData.Name}}&lt;/code&gt; to display the customer name, and &lt;code&gt;$&lt;/code&gt; &lt;code&gt;{{cardData.TotalPrice}}&lt;/code&gt; to display the price.&lt;/li&gt;
&lt;li&gt;On top of this, clicking on a specific card bring up an expanded view of the card; you can set the required components and display essential order details.&lt;/li&gt;
&lt;/ol&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%2Fhngv562qyakmu4xr0nrb.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%2Fhngv562qyakmu4xr0nrb.png" alt=" " width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now try out the implementation of the "Create an order" modal and the respective queries. The process is quite similar to how we implemented the "Add a Product" modal in the previous section.&lt;/p&gt;

&lt;p&gt;To get started, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design the UI for the "Create an order" modal using the appropriate components, such as text inputs, dropdowns, and buttons.&lt;/li&gt;
&lt;li&gt;Create a new query within the ToolJet Query Panel for appending data to the Google Sheets spreadsheet.&lt;/li&gt;
&lt;li&gt;Set the operation of the query to "Append data to spreadsheet" and specify the appropriate Spreadsheet ID.&lt;/li&gt;
&lt;li&gt;Configure the query to include the necessary input fields from the modal to capture the order details.&lt;/li&gt;
&lt;li&gt;Add event handlers to the relevant buttons in the modal to execute the query on click.&lt;/li&gt;
&lt;li&gt;Optionally, set up additional queries or query transformations to fetch updated order data and display it in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you can enable the creation of new orders through the modal and seamlessly update the Google Sheets spreadsheet with the order details. This allows for smooth data management and enhances the functionality of your Inventory and Order Management App.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dashboard page
&lt;/h3&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%2Fsln0bqa43dd42j4c6xop.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%2Fsln0bqa43dd42j4c6xop.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By now, you have gained valuable experience in designing UI components, creating queries, and integrating data into your application. Now, let's move on to the exciting task of building the Dashboard, which will provide a comprehensive overview of your inventory and orders.&lt;/p&gt;

&lt;p&gt;Building the Dashboard is a fantastic opportunity to showcase your newfound skills. With the knowledge you have gained so far, you can confidently create a visually appealing and informative Dashboard that displays key counts and highlights from your application. You can make use of the application export share in the Overview section above.&lt;/p&gt;

&lt;p&gt;To begin, refer to the provided image for the Dashboard layout with sections 1 and 2. As you have already built two complete pages, you are well-equipped to tackle this challenge; follow the image and place the components in the appropriate locations within the Canvas of the Application Builder.&lt;/p&gt;

&lt;p&gt;One essential aspect of the Dashboard is displaying the counts of products in different statuses, similar to what you did in the Inventory Manager page. To achieve this, you can create queries that retrieve the necessary data and calculate the counts based on the status of the products. You can refer back to the section on creating queries for updating the count fields in the Product Inventory page to refresh your memory on this process.&lt;/p&gt;

&lt;p&gt;By leveraging your previous experience and the knowledge you have gained, you have the skills and creativity to bring the Dashboard to life. Feel free to customize the components, styles, and layouts to match your desired design aesthetics Remember, the Dashboard serves as a visual snapshot of your application, providing a quick glimpse of the key metrics and insights. Focus on presenting the most relevant and impactful information to empower decision-making and streamline your business operations.&lt;/p&gt;

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

&lt;p&gt;Congratulations on successfully building your Inventory and Order Management App with ToolJet! You have demonstrated your skills in UI design, query creation, and application development. Now that you have completed this tutorial, you might be wondering what's next.&lt;/p&gt;

&lt;p&gt;To continue your journey and explore more possibilities with ToolJet, we encourage you to visit and try out our further tutorials here at blog.tooljet.com. Our blog is filled with tutorials, guides, and helpful resources that can further enhance your knowledge and help you unlock the full potential of ToolJet.&lt;/p&gt;

&lt;p&gt;In addition to the blog, we also recommend referring to our comprehensive documentation available at &lt;a href="https://docs.tooljet.com" rel="noopener noreferrer"&gt;docs.tooljet.com&lt;/a&gt;. The documentation covers a wide range of topics, including detailed explanations of components, advanced query techniques etc.&lt;/p&gt;

&lt;p&gt;Furthermore, we invite you to join our vibrant community on our &lt;a href="https://tooljet.com/slack" rel="noopener noreferrer"&gt;Slack workspace&lt;/a&gt;. By joining our community, you can connect with other active ToolJet users, share your experiences, and seek assistance when needed.&lt;/p&gt;

&lt;p&gt;Once again, congratulations on your achievements so far! We sincerely thank you for reading this tutorial and choosing ToolJet for your application development needs. We hope this tutorial has provided you with valuable insights and empowered you to build powerful web applications with ease.&lt;/p&gt;

&lt;p&gt;We look forward to seeing what you create next with ToolJet. Happy building!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Customer Support Desk App with ToolJet and PostgreSQL</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Mon, 26 Jun 2023 10:52:58 +0000</pubDate>
      <link>https://dev.to/tooljet/building-a-customer-support-desk-app-with-tooljet-and-postgresql-2lnl</link>
      <guid>https://dev.to/tooljet/building-a-customer-support-desk-app-with-tooljet-and-postgresql-2lnl</guid>
      <description>&lt;p&gt;In this tutorial, you'll learn how to build a customer support desk with ToolJet and a PostgreSQL database. The application enables you to track and respond to customers' queries and save their contact details on the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ToolJet?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; is an open-source low-code framework that enables us to build full-stack web applications within a few minutes. With ToolJet, you can create standalone fully-functional full-stack applications or embed applications into other websites.&lt;/p&gt;

&lt;p&gt;ToolJet allows you to build applications that use relational and non-relational databases, REST APIs, OpenAI technologies, and cloud storage like &lt;a href="https://cloud.google.com/storage" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt;, &lt;a href="https://aws.amazon.com/pm/serv-s3" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, and &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;Minio&lt;/a&gt;. It is an excellent development tool helping individuals, developers, and businesses create and ship products faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before we continue&lt;/strong&gt;&lt;br&gt;
I would be super happy if you could give us a star! And let me also know in the comments section. ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&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%2F8nfnzw4wf7oybaclylqd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nfnzw4wf7oybaclylqd.gif" alt="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the application user interface
&lt;/h2&gt;

&lt;p&gt;Here, you'll learn how to use the various UI widgets provided by ToolJet to build user-friendly interfaces. Before, then you need to create an account.&lt;/p&gt;

&lt;p&gt;If you are new to ToolJet, &lt;a href="https://app.tooljet.com/signup" rel="noopener noreferrer"&gt;create an account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a Workspace and a new app called Customer Support Desk.&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%2Fkapz2hdfe0x9pbfwyu9i.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%2Fkapz2hdfe0x9pbfwyu9i.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating the application, a new canvas will be displayed where you can build any user interface using the UI widgets on the right-hand side panel.&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%2Fgpamqsntpaaiye6hdnib.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%2Fgpamqsntpaaiye6hdnib.png" alt=" " width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, I'll guide you through building the user interface for the customer support application.&lt;br&gt;
The application is divided into three pages - the Dashboard page, the Ticket Details page, and the Customers' Information page. &lt;/p&gt;

&lt;p&gt;Click on the pages icon on the top left corner to create the additional pages.&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%2Fc7lbo39kdr1kemmkrl16.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%2Fc7lbo39kdr1kemmkrl16.png" alt=" " width="497" height="357"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Dashboard Page
&lt;/h3&gt;

&lt;p&gt;Here, you'll learn how to work with various UI widgets by building the dashboard page of the application.&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%2Fk3ril2ebmrzjbe6iamm3.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%2Fk3ril2ebmrzjbe6iamm3.png" alt=" " width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To replicate this design, let's divide the page into 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%2Forsen1feolvqn77k5sj7.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%2Forsen1feolvqn77k5sj7.png" alt=" " width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, the dashboard is divided into three sections - the left-hand sidebar, the top menu (with a red border), and the main section.&lt;br&gt;
For the left-hand side icons bar, ToolJet has a collection of icons you can pick from, and you can also change the background colour of the container when you select it.&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%2F37stzexlk1aus3hxgs1y.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%2F37stzexlk1aus3hxgs1y.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The top menu bar comprises the Text and Container components provided by ToolJet. You can change their sizes and colour to match the image below.&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%2Fj9cvl1j3wbybaigessgv.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%2Fj9cvl1j3wbybaigessgv.png" alt=" " width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main section of the application contains the Tab and Table components provided by ToolJet. The Table component is the child element of the Tab menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiz2vcehv42s1t86vru9x.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%2Fiz2vcehv42s1t86vru9x.png" alt=" " width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Ticket Details page
&lt;/h3&gt;

&lt;p&gt;The Ticket Details page displays a ticket's content and enables you to respond to them.&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%2Fiy0l7tmh0k56ib79nucs.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%2Fiy0l7tmh0k56ib79nucs.png" alt=" " width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To recreate the header component, use ToolJet's DropDown component, and the main body of the page contains the TextInput, Text Editor, Text, and Button components provided by ToolJet.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Customers' Information Page
&lt;/h3&gt;

&lt;p&gt;This page displays the information about your customers. You can recreate the design using the Text and Table components.&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%2Ftnxdnxi8tsotii3p46f9.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%2Ftnxdnxi8tsotii3p46f9.png" alt=" " width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You've successfully built the user interface for the application. In the upcoming section, you'll learn how to make the application functional by connecting it to a PostgreSQL database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up an ElephantSQL (PostgreSQL) database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.elephantsql.com/" rel="noopener noreferrer"&gt;ElephantSQL&lt;/a&gt; enables us to create a PostgreSQL database on the cloud instead of your local machine. Follow the steps below to create a PostgreSQL database:&lt;/p&gt;

&lt;p&gt;Create an ElephantSQL account &lt;a href="https://customer.elephantsql.com/login" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add a new database instance. No credit card or billing information is required.&lt;/p&gt;

&lt;p&gt;Once you've created the database instance, your database information is displayed.&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%2Fcin4ixt1cryn45ldja5h.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%2Fcin4ixt1cryn45ldja5h.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's connect your ToolJet app to the database.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connecting a PostgreSQL database to ToolJet
&lt;/h3&gt;

&lt;p&gt;Select Global Datasources from the top-right menu bar on your ToolJet 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%2Fxt4f1m2p6h57bxef1rs9.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%2Fxt4f1m2p6h57bxef1rs9.png" alt=" " width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select PostgreSQL from the list of databases under the Global Datasources panel, and provide the required information as shown below.&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%2Fu2h24hdejmdbr2lz2pa7.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%2Fu2h24hdejmdbr2lz2pa7.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, the host is the same as the server name on ElephantSQL (excluding the brackets). The username and database name are the same, and copy and paste the password into its field.&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%2Fd5y0cukj9sssnkf8irlm.jpeg" 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%2Fd5y0cukj9sssnkf8irlm.jpeg" alt=" " width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down the page and click &lt;code&gt;Test Connection&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%2Fbutd23yivp1z0muqvpk0.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%2Fbutd23yivp1z0muqvpk0.png" alt=" " width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the connection is verified, we can start making queries to the database.&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%2Fp7wkpzi7nbkeqj8ah5cd.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%2Fp7wkpzi7nbkeqj8ah5cd.png" alt=" " width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you've successfully created and connected your database to the application. You can now start making queries to the database.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to communicate with a PostgreSQL database in ToolJet
&lt;/h2&gt;

&lt;p&gt;In this section, you'll learn how to communicate with the PostgreSQL database and how to make database queries in ToolJet.&lt;/p&gt;

&lt;p&gt;ToolJet allows us to communicate with external resources or create custom functions via a panel known as &lt;a href="https://docs.tooljet.com/docs/app-builder/query-panel" rel="noopener noreferrer"&gt;Query Panel&lt;/a&gt;. In ToolJet, any function that interacts with a database, API, or cloud storage and runs a JavaScript or Python code is called a Query.&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%2Fvww8zc5cm5va9lfml6vf.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%2Fvww8zc5cm5va9lfml6vf.png" alt=" " width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create the database tables and add some dummy data before querying the database. You need to create two tables, one for the contacts and the other for the support tickets. &lt;/p&gt;

&lt;p&gt;Navigate to the browser section on your ElephantSQL database and execute the code snippet below.&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%2Fhqpplglemlfbndihukob.jpeg" 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%2Fhqpplglemlfbndihukob.jpeg" alt=" " width="800" height="384"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tickets&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;product_version&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;issue_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;assigned_to&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;tickets&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assigned_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Chris Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Encountered an error message when trying to save a file.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Open'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'High'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Support Team'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;tickets&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assigned_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'1.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Encountered an error message when trying to save a file.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Open'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'High'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Support Team'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, create the Contacts table and add some dummy data to the table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;organization&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Manager'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ToolJet'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'United States'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'English'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sean Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sean@example.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Manager'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ToolJet'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'United States'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'English'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Querying the PostgreSQL database in ToolJet
&lt;/h3&gt;

&lt;p&gt;To make the customer support application fully functional, you need to be able to fetch the data within the application. Therefore, create a query called &lt;code&gt;getTickets&lt;/code&gt; that retrieves all the support tickets from the database.&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%2Fgue4yysv5lhda02bxten.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%2Fgue4yysv5lhda02bxten.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the SQL query into the SQL mode editor, set the &lt;code&gt;Run the query on application load?&lt;/code&gt; option to true, and save the query.&lt;/p&gt;

&lt;p&gt;Next, set the value of the ticket's table to the result returned from the query.&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%2Fm4gvvdhfaaapg5ljp7ea.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%2Fm4gvvdhfaaapg5ljp7ea.png" alt=" " width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to display the details of a support ticket when you click on each data row on the table.&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%2Fh30fbcki5uei5ce8bkwf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh30fbcki5uei5ce8bkwf.gif" alt=" " width="600" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do this, you have to add a Switch Page event on the table and save the selected data row into a variable (to be accessed on the Ticket Details page).&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%2Fmkkksav52wmhmjw7jdqb.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%2Fmkkksav52wmhmjw7jdqb.png" alt=" " width="800" height="395"&gt;&lt;/a&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%2Fm15mbzrj03grhrfj9djy.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%2Fm15mbzrj03grhrfj9djy.png" alt=" " width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retrieve the selected row details using the &lt;code&gt;{{variables.&amp;lt;variable_name&amp;gt;.values}}&lt;/code&gt; method provided by ToolJet. Therefore, when you click on any data on the table, it redirects you to the Ticket Details page, where you can see all the ticket information.&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%2Fmnuqen1fa5d6z7tpmn61.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%2Fmnuqen1fa5d6z7tpmn61.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create another PostgreSQL query that shows the number of tickets available according to their status.&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%2F5tyceeh91mq7z5763225.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%2F5tyceeh91mq7z5763225.png" alt=" " width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the code below into the SQL editor. It returns the status and number of tickets for each of them.&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%2Ftyz50lksse7ojtjo10n8.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%2Ftyz50lksse7ojtjo10n8.png" alt=" " width="800" height="406"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ticket_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
    &lt;span class="n"&gt;tickets&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Open'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'On Hold'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Resolved'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Display the number of tickets for each status, as done below.&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%2Fzi6rgxuilvvn9hxmvg6d.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%2Fzi6rgxuilvvn9hxmvg6d.png" alt=" " width="800" height="189"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{queries.ticketStatusCount.data[&amp;lt;index&amp;gt;].ticket_count}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, create another query that retrieves the contacts from the database and displays them on the table within the Customers page.&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%2Fo8yrlmj5tnw8cckbs63t.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%2Fo8yrlmj5tnw8cckbs63t.png" alt=" " width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update the table's content as shown below.&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%2Fajw4wboxlzuoni7lfk3q.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%2Fajw4wboxlzuoni7lfk3q.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations on making it thus far! In the upcoming section, you'll learn how to respond to the customer's queries via email using the SendInBlue API in ToolJet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to send emails with the SendInBlue API in ToolJet
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.brevo.com/" rel="noopener noreferrer"&gt;Sendinblue&lt;/a&gt; is a digital marketing tool that provides Email, SMS, Facebook, Chat, and more, via one platform to help grow businesses by building stronger customer relationships.&lt;/p&gt;

&lt;p&gt;In this section, you'll learn how to send emails via Sendinblue in ToolJet. First, you need to log in or &lt;a href="https://www.brevo.com/" rel="noopener noreferrer"&gt;create a Sendinblue account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Select SMTP and API on your dashboard, generate an SMTP key, and copy it somewhere on your computer. You'll need it shortly.&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%2Fhwunzhymvszu5dovntem.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%2Fhwunzhymvszu5dovntem.png" alt=" " width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Return to your ToolJet app, add a new SMTP data source, and fill in the required credentials. Your password is the generated SMTP key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv0yzl8i2pbkol4w2z16.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%2Fcv0yzl8i2pbkol4w2z16.png" alt=" " width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If successfully connected, it will display "Connection Verified". Then you can start sending emails.&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%2Fbu4vnvkcxylb39ujbdyj.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%2Fbu4vnvkcxylb39ujbdyj.png" alt=" " width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create the query for sending the emails. Provide your email and name, and retrieve the recipient's email, subject, and response from the Ticket Details page.&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%2F0oi8li77mj54gibrjhvg.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%2F0oi8li77mj54gibrjhvg.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The query retrieves the data automatically from the components. Finally, execute the query when you click the Send button.&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%2Ffr7o0fcd11ec2etcjnij.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%2Ffr7o0fcd11ec2etcjnij.png" alt=" " width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You've completed the application. Here is a working demo of the application: &lt;a href="https://app.tooljet.com/applications/867240f3-eb26-45c8-ba1d-d94f004925e9" rel="noopener noreferrer"&gt;Customer Support Desk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also download its &lt;a href="https://tooljet.com" rel="noopener noreferrer"&gt;JSON file&lt;/a&gt; and import it into a ToolJet app, but you'll need to provide your Sendinblue and database credentials.&lt;/p&gt;

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

&lt;p&gt;So far, you've learnt how to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add a PostgreSQL database to ToolJet&lt;/li&gt;
&lt;li&gt;send emails within a ToolJet application using Sendinblue, and&lt;/li&gt;
&lt;li&gt;build full-stack applications in a few minutes with ToolJet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ToolJet is both an intutive development tool and open-source - meaning its code is readily available for everyone to improve and contribute. It has a large community of developers and talented contributors constantly maintaining and improving the software. As a user, you can be sure of getting the best performance when you use ToolJet.&lt;/p&gt;

&lt;p&gt;Are you interested in contributing to ToolJet? Feel free to check out our GitHub repo- &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt; to contribute and raise issues about ToolJet.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building ChatGPT-powered apps in 5 steps 🚀</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Mon, 22 May 2023 08:06:56 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/building-chatgpt-powered-apps-in-5-steps-3e85</link>
      <guid>https://dev.to/tejakummarikuntla/building-chatgpt-powered-apps-in-5-steps-3e85</guid>
      <description>&lt;p&gt;In this article, we will explore the possibility of rapidly building ChatGPT apps using the low-code solution, ToolJet. &lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the traditional approach, building apps and integrating them with ChatGPT requires significant programming expertise and time-consuming development cycles. However, we’ll use ToolJet and its seamless integration with OpenAI, and streamline the process, allowing developers to build applications that harness the capabilities of ChatGPT rapidly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ToolJet
&lt;/h2&gt;

&lt;p&gt;Before we dive into the details, let's take a moment to understand what ToolJet is. ToolJet is an open-source low-code development platform that enables users to build internal tools with minimal coding. It provides an intuitive visual interface that allows developers to create applications by connecting pre-built components and queries. The drag-and-drop functionality and a library of various integrations streamline the development process, making it an aligned choice for building ChatGPT apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet - 19k+ GitHub stars&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" alt="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are interested in exploring about it further, visit its GitHub page: &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt;, and if you find it valuable, show your support by starring the repository ⭐️ &lt;/p&gt;

&lt;p&gt;Let’s get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Sign in or Sign up
&lt;/h3&gt;

&lt;p&gt;If you're already a ToolJet user, sign in to your account. If you're new to ToolJet, you can quickly sign up and create a new account from &lt;a href="http://www.tooljet.com" rel="noopener noreferrer"&gt;www.tooljet.com&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create a new app
&lt;/h3&gt;

&lt;p&gt;From the dashboard, locate the "Create New App" button in the top right and click on it. This will get you into the application builder. You can name the app as you wish.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  3. Select OpenAI from Global Datasources
&lt;/h3&gt;

&lt;p&gt;Navigate to the Global data sources from the Home page and select OpenAI from the plugins section of the window; this will bring up a window asking for your credentials&lt;/p&gt;

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

&lt;h3&gt;
  
  
  4. Configure the OpenAI plugin with credentials
&lt;/h3&gt;

&lt;p&gt;Enter your API Key and Organization ID; you can create them from the OpenAI website.&lt;/p&gt;

&lt;p&gt;Once done, test the connection and save it.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhesf3c2xieldoao25txj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhesf3c2xieldoao25txj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Fetch data from OpenAI
&lt;/h3&gt;

&lt;p&gt;Go back to the Dashboard and open the application created in Step 2. Pull up the query panel from the bottom and select the OpenAI plugin from the “Global Datasources section” of the query panel.&lt;/p&gt;

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

&lt;p&gt;This will bring you the options to define the OpenAI query. The plugin allows you to perform “Chat” and “Conversation” operations at the moment.&lt;/p&gt;

&lt;p&gt;For testing purposes, you can choose the “Chat” and fill in the “message as input” field asking to "Draft an email for the customer reminding the payment due".&lt;/p&gt;

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

&lt;p&gt;Congratulations, you’ve successfully configured the OpenAI plugin and made the initial request 🎉&lt;/p&gt;

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

&lt;p&gt;Now that you know how to create a ToolJet app and connect it with OpenAI, be sure to try out our sample applications that are built using OpenAI responses.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/tooljet/creating-a-sql-generator-app-with-chatgpt-postgresql-and-tooljet-e2k"&gt;Creating a SQL generator app with ChatGPT, PostgreSQL, and ToolJet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/tooljet/use-chatgpt-api-to-build-a-readme-generator-app-with-low-code-4h19"&gt;Use ChatGPT API to build a README generator app with low-code&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you find ToolJet valuable and can potentially help you to minimize the engineering efforts, visit the &lt;a href="http://github.com/tooljet/tooljet" rel="noopener noreferrer"&gt;GitHub page&lt;/a&gt; and show your support by starring the repository.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>chatgpt</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>10 open-source alternatives to run your businesses</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Wed, 29 Mar 2023 06:30:32 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/10-open-source-alternatives-to-run-your-businesses-193c</link>
      <guid>https://dev.to/tejakummarikuntla/10-open-source-alternatives-to-run-your-businesses-193c</guid>
      <description>&lt;p&gt;By choosing open-source alternatives to commercial proprietary software, it not only save money but also contribute to a community that believes in collaborative development and the sharing of knowledge.&lt;/p&gt;

&lt;p&gt;Here are the a few prominent open-source alternatives that could nearly offers the same functionalities, and can potentially  help start-ups or businesses in Digital technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa JS&lt;/a&gt; - 17.5k ⭐️
&lt;/h2&gt;

&lt;p&gt;Medusa is an open source composable commerce engine built with Node.js. Medusa enables developers to build scalable and sophisticated commerce setups with low effort and great developer experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  2. &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; - 18.4k ⭐️
&lt;/h2&gt;

&lt;p&gt;ToolJet is an open-source low-code framework to build and deploy internal tools quickly with minimal engineering effort.You can connect to your data sources, such as databases ( PostgreSQL, MongoDB, Elasticsearch &amp;amp; more), API endpoints (ToolJet supports importing OpenAPI spec &amp;amp; OAuth2 authorization), SaaS tools (Stripe, Slack, Google Sheets, Airtable, Notion &amp;amp; more) and object storage services ( S3, GCS, Minio, etc ) to fetch and write data.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  3. &lt;a href="https://github.com/chatwoot/chatwoot" rel="noopener noreferrer"&gt;Chatwoot&lt;/a&gt; - 14.8K ⭐️
&lt;/h2&gt;

&lt;p&gt;Chatwoot is an open-source, self-hosted customer engagement suite. Chatwoot lets you view and manage your customer data, communicate with them irrespective of which medium they use, and re-engage them based on their profile.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  4. &lt;a href="https://gitlab.com/bramw/baserow" rel="noopener noreferrer"&gt;Baserow&lt;/a&gt; - 1.5k ⭐️
&lt;/h2&gt;

&lt;p&gt;Baserow is an open source no-code database and Airtable alternative. Create database without technical experience. &lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  5. &lt;a href="https://github.com/supabase/supabase" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; - 47.4k ⭐️
&lt;/h2&gt;

&lt;p&gt;Supabase is an open source Firebase alternative, build the features of Firebase using enterprise-grade open source tools.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  6. &lt;a href="https://github.com/odoo/odoo" rel="noopener noreferrer"&gt;Odoo&lt;/a&gt; - 28.5k ⭐️
&lt;/h2&gt;

&lt;p&gt;Odoo is a suite of web based open source business apps.&lt;/p&gt;

&lt;p&gt;The main Odoo Apps include an Open Source CRM, Website Builder, eCommerce, Warehouse Management, Project Management, Billing &amp;amp; Accounting, Point of Sale, Human Resources, Marketing, Manufacturing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  7. &lt;a href="https://github.com/RocketChat/Rocket.Chat" rel="noopener noreferrer"&gt;Rocket.chat&lt;/a&gt; - 35.1k ⭐️
&lt;/h2&gt;

&lt;p&gt;Rocket.Chat is an open-source fully customizable communications platform developed in JavaScript for organizations with high standards of data protection.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;Javascript
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7a7t4i0ggag3tjcxqxz.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. &lt;a href="https://github.com/n8n-io/n8n" rel="noopener noreferrer"&gt;n8n&lt;/a&gt; - 29.1k ⭐️
&lt;/h2&gt;

&lt;p&gt;n8n is an extendable workflow automation tool. With a fair-code distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  9. &lt;a href="https://github.com/penpot/penpot" rel="noopener noreferrer"&gt;Penpot&lt;/a&gt; - 21.2k ⭐️
&lt;/h2&gt;

&lt;p&gt;Penpot is the first Open Source design and prototyping platform meant for cross-domain teams. Non dependent on operating systems, Penpot is web based and works with open standards (SVG). Penpot invites designers all over the world to fall in love with open source while getting developers excited about the design process in return.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Clojure&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  10. &lt;a href="https://github.com/postalserver/postal" rel="noopener noreferrer"&gt;Postal&lt;/a&gt; - 12.5k ⭐️
&lt;/h2&gt;

&lt;p&gt;Postal is a complete and fully featured mail server for use by websites &amp;amp; web servers. Think Sendgrid, Mailgun or Postmark but open source and ready for you to run on your own servers. Postal is developed by Krystal to serve its own mail processing requirements and we have since decided that it should be released as an open source project for the community.&lt;/p&gt;

&lt;h4&gt;
  
  
  Techstack
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Ruby&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Thanks for reading and feel free to add your thoughts and other OSS alternatives in the comments. Find more of such alternatives at &lt;a href="https://www.opensourcealternative.to/" rel="noopener noreferrer"&gt;opensourcealternative.to&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>opensource</category>
      <category>startup</category>
    </item>
    <item>
      <title>Creating a SQL generator app with ChatGPT, PostgreSQL, and ToolJet</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Tue, 21 Mar 2023 12:17:27 +0000</pubDate>
      <link>https://dev.to/tooljet/creating-a-sql-generator-app-with-chatgpt-postgresql-and-tooljet-e2k</link>
      <guid>https://dev.to/tooljet/creating-a-sql-generator-app-with-chatgpt-postgresql-and-tooljet-e2k</guid>
      <description>&lt;p&gt;In this tutorial, you'll learn how to build a SQL Generator application using ChatGPT, ToolJet, and PostgreSQL.&lt;/p&gt;

&lt;p&gt;Users can state the kind of data they want to retrieve from the database in plain English sentences, then ChatGPT converts them to the appropriate SQL syntax required to fetch the data from the database.&lt;br&gt;
We'll be using ToolJet for the application interface.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is ToolJet?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; is an open-source low-code framework that enables us to build full-stack web applications within a few minutes. With ToolJet, you can create standalone fully-functional full-stack applications or embed applications into other websites.&lt;/p&gt;

&lt;p&gt;ToolJet allows you to build applications that use relational and non-relational databases, REST APIs, and cloud storage like &lt;a href="https://cloud.google.com/storage" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt;, &lt;a href="https://aws.amazon.com/pm/serv-s3" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, and &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;Minio&lt;/a&gt;. It is an excellent development tool helping individuals, developers, and businesses create and ship products faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before we continue&lt;/strong&gt;&lt;br&gt;
I would be super happy if you could give us a star! And let me also know in the comments section. ❤️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&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%2F8nfnzw4wf7oybaclylqd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nfnzw4wf7oybaclylqd.gif" alt="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZjlhNzRjMWJlYzgyNWZjYjcyOGMxYjJiYjQxOTQzYTU1NTg5YzAzNiZjdD1n/dfbMVqwq8GrC19xSEF/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is ChatGPT?
&lt;/h2&gt;

&lt;p&gt;ChatGPT is the most talked about technology at the moment. It is an AI language model trained by &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; to answer questions, provide information, and engage in human language conversations with users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openai.com/blog/chatgpt" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; performs tasks such as writing, debugging, and explaining code snippets. With its latest language model &lt;a href="https://openai.com/product/gpt-4" rel="noopener noreferrer"&gt;GPT-4&lt;/a&gt;, ChatGPT can accept images as prompts and provides answers based on the given prompt or commands.&lt;/p&gt;

&lt;p&gt;In this article, we'll use ChatGPT to generate SQL query syntax from plain English words. Before we begin, let's set up the application's database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up an ElephantSQL (PostgreSQL) database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.elephantsql.com/" rel="noopener noreferrer"&gt;ElephantSQL&lt;/a&gt; enables us to create a PostgreSQL database on the cloud instead of your local machine. Follow the steps below to create a PostgreSQL database:&lt;/p&gt;

&lt;p&gt;Create an ElephantSQL account &lt;a href="https://customer.elephantsql.com/login" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add a new database instance. No credit card or billing information is required.&lt;/p&gt;

&lt;p&gt;Once you've created the database instance, your database information is displayed.&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%2Ff3b2z0bgxc582jgqwb30.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%2Ff3b2z0bgxc582jgqwb30.png" alt=" " width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You've successfully created the database needed for this application. Next, let's design the application interface and connect the database to the application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Designing the application interface with ToolJet
&lt;/h2&gt;

&lt;p&gt;Here, you'll learn how to create a ToolJet account and design a fully functional application with ToolJet.&lt;/p&gt;

&lt;p&gt;If you are new to ToolJet, &lt;a href="https://app.tooljet.com/signup" rel="noopener noreferrer"&gt;create an account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a Workspace and a new app called SQL Generator.&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%2F1zv6f21fhqw3k7mv8pk2.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%2F1zv6f21fhqw3k7mv8pk2.png" alt=" " width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Design a user interface similar to the image below. ToolJet enables you to create applications' interfaces by dragging and dropping various UI components.&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%2Ffsqyog123m1lilrz4g6g.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%2Ffsqyog123m1lilrz4g6g.png" alt=" " width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, I added the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the Table component with columns name and age,&lt;/li&gt;
&lt;li&gt;the Text component that serves as a form label,&lt;/li&gt;
&lt;li&gt;the TextInput component that accepts the SQL query in plain English.&lt;/li&gt;
&lt;li&gt;a read-only Text Input component that displays the generated SQL query, and&lt;/li&gt;
&lt;li&gt;two Buttons. One for generating the SQL query from ChatGPT and the other for executing the query on the PostgreSQL database.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How to communicate with ChatGPT in ToolJet
&lt;/h2&gt;

&lt;p&gt;Here, you'll learn how to communicate with ChatGPT via Rest API in ToolJet.&lt;/p&gt;

&lt;p&gt;ToolJet allows us to communicate with external resources or create custom functions via a panel, known as &lt;a href="https://docs.tooljet.com/docs/app-builder/query-panel/" rel="noopener noreferrer"&gt;Query Panel&lt;/a&gt;. In ToolJet, any function that communicates with either of the following: database, API, cloud storage, and runs a JavaScript or Python code is called a Query.&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%2F4gmq8sopkt1kzcmyt2yn.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%2F4gmq8sopkt1kzcmyt2yn.png" alt=" " width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up an OpenAI account
&lt;/h3&gt;

&lt;p&gt;Log in or create an OpenAI account &lt;a href="https://openai.com/product" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Personal&lt;/code&gt; on the navigation bar and select &lt;code&gt;View API keys&lt;/code&gt; from the menu bar to create a new secret key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fchcm65u0eof884a10jhl.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%2Fchcm65u0eof884a10jhl.png" alt=" " width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generate a new API key and copy it somewhere on your computer. We'll use it in the upcoming section.&lt;/p&gt;
&lt;h3&gt;
  
  
  Communicating with the ChatGPT API in ToolJet
&lt;/h3&gt;

&lt;p&gt;On your Query Panel, select REST API and create a POST request to the ChatGPT endpoint - &lt;code&gt;https://api.openai.com/v1/chat/completions&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%2Flfznjk0calsn20u38p9g.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%2Flfznjk0calsn20u38p9g.png" alt=" " width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the image above, I added the following to the Query Headers variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Type: application/json
Authorization: Bearer &amp;lt;YOUR_CHATGPT_API_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable Transformations and copy the code below into the code editor to retrieve the exact response needed from the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;choices&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click Body, enable Raw JSON, and copy the code below into the code editor - it contains parameters sent along with the POST request.&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;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gpt-3.5-turbo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write the SQL query for this operation: {{components.textarea1.value}}. I need only the SQL query. No explanations, please."&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code snippet above uses the GPT-3.5 Turbo model provided by OpenAI, and the content variable contains the prompt sent to the ChatGPT API.&lt;br&gt;
ToolJet allows users to access the attributes and values of each component. Hence, the &lt;code&gt;{{components.textarea1.value}}&lt;/code&gt; represents the value (plain English command) provided by the user.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; Yours may not be &lt;code&gt;textarea1&lt;/code&gt;, if so, change it to the component’s name for the user's input.&lt;/p&gt;
&lt;/blockquote&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%2F2js3lqhnd9b9650xgv06.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%2F2js3lqhnd9b9650xgv06.png" alt=" " width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, click the &lt;code&gt;Save&lt;/code&gt; button.🎉&lt;/p&gt;

&lt;p&gt;So far, we've configured the ChatGPT query. Next, let's execute the query when a user clicks the &lt;code&gt;Generate SQL Command&lt;/code&gt; button.&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%2Fbek95zspe7cpr2p4hpmu.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%2Fbek95zspe7cpr2p4hpmu.png" alt=" " width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, I added an &lt;code&gt;on Click&lt;/code&gt; event to the button, and the ChatGPT query runs when a user clicks the button. It takes the user's input and sends a request for its equivalent SQL syntax when a user clicks the button.&lt;/p&gt;

&lt;p&gt;To display the result, update the default value of the read-only Text Input component by setting it to &lt;code&gt;{{queries.chatgptQuery.data}}&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%2Fp8ea6c3b3naehsykhrey.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%2Fp8ea6c3b3naehsykhrey.png" alt=" " width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations!🎊 You've been able to communicate with ChatGPT. Next, let's learn how to connect and communicate with the PostgreSQL database created earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to communicate with a PostgreSQL database in ToolJet
&lt;/h2&gt;

&lt;p&gt;Here, you'll learn how to communicate with a PostgreSQL database in ToolJet. First, let me walk you through connecting a PostgreSQL database to ToolJet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting a PostgreSQL database to ToolJet
&lt;/h3&gt;

&lt;p&gt;Click &lt;code&gt;Add a Datasource&lt;/code&gt; from the Query Panel on ToolJet, select PostgreSQL from the list of databases, and provide the required information as shown below.&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%2F9yetdsfc1jz10r7vq2nl.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%2F9yetdsfc1jz10r7vq2nl.png" alt=" " width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the image above, the host is the same as the server name on ElephantSQL (excluding the brackets). The username and database name are the same, and copy and paste the password into its field.&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%2F1wyvomrogzlmjz5bjss8.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%2F1wyvomrogzlmjz5bjss8.png" alt=" " width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down the page and click &lt;code&gt;Test Connection&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%2Ftzrmg24c207azi3bk9f9.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%2Ftzrmg24c207azi3bk9f9.png" alt=" " width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the connection is verified, we can start making queries to the database.&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%2Fsr80ndfs5u756z2awhfi.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%2Fsr80ndfs5u756z2awhfi.png" alt=" " width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicating the PostgreSQL database in ToolJet
&lt;/h3&gt;

&lt;p&gt;Here, you'll learn how to send the SQL queries generated by ChatGPT to the PostgreSQL database and view the queried data within the Table component on the user interface.&lt;/p&gt;

&lt;p&gt;Before we begin, let's add some data to the database. Click on the PostgreSQL query, select SQL mode, and run the code snippet below to create a new table containing the data below.``&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
CREATE TABLE People(NAME varchar(255), AGE int);&lt;br&gt;
INSERT INTO People(name, age) VALUES(‘David’, 19);&lt;br&gt;
INSERT INTO People(name, age) VALUES(‘Tabby’, 21);&lt;br&gt;
INSERT INTO People(name, age) VALUES('Teja', 24);&lt;br&gt;
INSERT INTO People(name, age) VALUES('Julius', 27);&lt;br&gt;
INSERT INTO People(name, age) VALUES('Kedrick', 26);&lt;br&gt;
SELECT * FROM people;&lt;br&gt;
&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%2Ftfaum4rn3zwsckqg2c6r.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%2Ftfaum4rn3zwsckqg2c6r.png" alt=" " width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can view the data on ElephantSQL once it has been uploaded.&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%2F0kyvvsz948dsk8168020.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%2F0kyvvsz948dsk8168020.png" alt=" " width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's receive the SQL command from the ChatGPT and display the data within the Table component on the user interface.&lt;/p&gt;

&lt;p&gt;Update the SQL editor to contain the query retrieved from ChatGPT and save it.&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%2Fqfqwjvb9eqlhrcoev1cb.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%2Fqfqwjvb9eqlhrcoev1cb.png" alt=" " width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add an &lt;code&gt;on Click&lt;/code&gt; event that runs the database query to the &lt;code&gt;Execute&lt;/code&gt; button.&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%2F7bm8n8wqf5rxblnf6bqj.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%2F7bm8n8wqf5rxblnf6bqj.png" alt=" " width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, update the Table component to display the data retrieved from the database.&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%2F5he99rr43yc9d4it2a4j.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%2F5he99rr43yc9d4it2a4j.png" alt=" " width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations!🎉 You've completed the application for this tutorial. Click the Release button and the share icon to host and make the application public.&lt;/p&gt;

&lt;p&gt;Here is the desktop version of the application: &lt;a href="https://app.tooljet.com/applications/480a5bbb-3607-4c19-ac95-038d4e56cc2a" rel="noopener noreferrer"&gt;https://app.tooljet.com/applications/480a5bbb-3607-4c19-ac95-038d4e56cc2a&lt;/a&gt;.&lt;br&gt;
You can also import this application into your ToolJet workspace by &lt;a href="https://github.com/tejakummarikuntla/ToolJet-Sample-Apps/tree/main/SQL%20generator%20with%20ChatGPT" rel="noopener noreferrer"&gt;downloading its JSON file&lt;/a&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%2F7m2mwwlsotlyy0jsuqh5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m2mwwlsotlyy0jsuqh5.gif" alt=" " width="600" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;So far, you have learnt how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add a PostgreSQL database to ToolJet&lt;/li&gt;
&lt;li&gt;communicate with ChatGPT via REST API in ToolJet, and&lt;/li&gt;
&lt;li&gt;design applications in a few minutes with ToolJet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; is both an excellent development tool and open-source - meaning its code is readily available for everyone to modify and improve. It has a large &lt;a href="https://tooljet.slack.com/ssb/redirect" rel="noopener noreferrer"&gt;community&lt;/a&gt; of developers and talented contributors constantly maintaining and improving the software. As a user, you can be sure of getting the best performance when you use ToolJet.&lt;/p&gt;

&lt;p&gt;Are you interested in contributing to ToolJet? Feel free to check out our GitHub repo - &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;https://github.com/ToolJet/ToolJet&lt;/a&gt; to contribute and raise issues about ToolJet.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>tooljet</category>
      <category>javascript</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>10 repositories to star if you are a Javascript developer</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Wed, 08 Mar 2023 10:59:15 +0000</pubDate>
      <link>https://dev.to/tejakummarikuntla/10-repositories-to-star-if-you-are-a-javascript-developer-40aj</link>
      <guid>https://dev.to/tejakummarikuntla/10-repositories-to-star-if-you-are-a-javascript-developer-40aj</guid>
      <description>&lt;p&gt;If you are a Javascript developer and an open-source contributor, here are the highly active open-source projects you can use in your daily work or contribute back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://github.com/storybookjs/storybook"&gt;Storybook&lt;/a&gt; - 77.2k stars
&lt;/h2&gt;

&lt;p&gt;A javascript tool enables developers to build organized and compelling UI systems in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Simplifies the process of component development&lt;/li&gt;
&lt;li&gt;Ability to integrate your tools to connect workflows.&lt;/li&gt;
&lt;li&gt;Ability to reuse the components and share with other developers.&lt;/li&gt;
&lt;li&gt;Documentation to your components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;Javascript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Follow the Storybook&lt;a href="https://storybook.js.org/docs/react/get-started/introduction"&gt; guide&lt;/a&gt; to get started.&lt;/li&gt;
&lt;li&gt;Join the Storybook &lt;a href="https://storybook.js.org/community/"&gt;community&lt;/a&gt; of frontend developers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. &lt;a href="//github.com/tooljet/tooljet"&gt;ToolJet&lt;/a&gt; - 18.2k stars
&lt;/h2&gt;

&lt;p&gt;ToolJet is an open source low-code platform that can save a huge amount of time in building internal tools for your daily or regular chores&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ToolJet has an inbuilt no-code database to save data from your application&lt;/li&gt;
&lt;li&gt;It has integrations with all the prominent databases so that you can connect directly without needing to build APIs&lt;/li&gt;
&lt;li&gt;Can use more than 35+ ready to use components to build the UI of the application&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;a href="//blog.tooljet.com"&gt;ToolJet blog&lt;/a&gt; to learn to use it better&lt;/li&gt;
&lt;li&gt;Join the ToolJet &lt;a href="https://tooljet.com/slack"&gt;slack&lt;/a&gt; to post your questions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://github.com/supabase/supabase"&gt;Supabase&lt;/a&gt; - 46.5k stars
&lt;/h2&gt;

&lt;p&gt;Supbase is an open-source database service; you can use Supabase as an alternative to Firebase&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Every Supabase project comes with a complete Postgres database&lt;/li&gt;
&lt;li&gt;Offer multiple GUI features to view Tables, clone, and run quereis in the editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;PLSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;a href="https://supabase.com/docs"&gt;Supabase docs&lt;/a&gt; to learn to use it better&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://discord.com/invite/R7bSpeBSJE"&gt;Supabase discord&lt;/a&gt; to post your questions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://github.com/gatsbyjs/gatsby"&gt;Gatsby&lt;/a&gt; - 54.2k stars
&lt;/h2&gt;

&lt;p&gt;Gatsby is an open-source framework based on react that helps developers build websites and apps. It combines the control and scalability of dynamically rendered sites with the speed of static-site generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Helps to load data from anywhere, like Markdown files, Headless CMS, REST or GraphQL API etc.,&lt;/li&gt;
&lt;li&gt;Gatsby sites do not require servers to host; you can host your entire site on a CDN&lt;/li&gt;
&lt;li&gt;With Valhalla Content Hub, you can bring Gatsby’s data layer to any project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find more from the &lt;a href="https://www.gatsbyjs.com/features"&gt;Gatsby features&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Follow their &lt;a href="https://github.com/gatsbyjs/gatsby#--quickstart------tutorial------plugins------starters------showcase------contribute----support-twitter-discussions------discord"&gt;guides&lt;/a&gt; in Readme to get started.&lt;/li&gt;
&lt;li&gt;Explore Gatsby’s &lt;a href="https://www.gatsbyjs.com/resources/webinars/"&gt;webinars&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. &lt;a href="https://github.com/cypress-io/cypress"&gt;Cypress&lt;/a&gt; - 42.8k stars
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cyperss can help you can easily create tests for web applications, debug them visually, and automatically run them in your continuous integration builds.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ability to debug the failures directly in the browser&lt;/li&gt;
&lt;li&gt;Enables integration with prominent CI providers&lt;/li&gt;
&lt;li&gt;Flakey test management helps to discover and diagnose unreliable tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find more about the features in &lt;a href="https://docs.cypress.io/guides/overview/why-cypress#Features"&gt;Cypress docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Learn to start using Cypress from their &lt;a href="https://docs.cypress.io/guides/overview/why-cypress"&gt;detailed docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Find more about Cypress from their &lt;a href="https://www.cypress.io/blog"&gt;blog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. &lt;a href="https://github.com/d3/d3"&gt;D3js&lt;/a&gt; - 105k stars
&lt;/h2&gt;

&lt;p&gt;D3.js is a JavaScript library for manipulating documents based on data; it helps you bring data to life using HTML, SVG, and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports larger datasets&lt;/li&gt;
&lt;li&gt;Code reusability&lt;/li&gt;
&lt;li&gt;Ability to manipulate the Document Object Model (DOM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Find all the &lt;a href="https://github.com/d3/d3#resources"&gt;resources&lt;/a&gt; to use D3js from the README&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. &lt;a href="https://github.com/juliangarnier/anime/"&gt;Anime&lt;/a&gt; - 44.6k stars
&lt;/h2&gt;

&lt;p&gt;Anime.js is a lightweight JavaScript animation library with a simple yet powerful API. It works with CSS properties, SVG, DOM attributes and JavaScript Objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intuitive and compelling documentation&lt;/li&gt;
&lt;li&gt;Lightweight and supported by the prominent browsers&lt;/li&gt;
&lt;li&gt;Can manipulate modern CSS and can implement animations being arbitrary from JavaScript values.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;a href="https://animejs.com/documentation/"&gt;Animejs documentation&lt;/a&gt; to get started&lt;/li&gt;
&lt;li&gt;Follow the Animejs Codepen &lt;a href="https://codepen.io/collection/XLebem"&gt;examples&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. &lt;a href="https://github.com/adonisjs/core"&gt;AdonisJs&lt;/a&gt; - 13.6k stars
&lt;/h2&gt;

&lt;p&gt;AdonisJs is a full-stack Web framework with a focus on ergonomics and speed. It takes care of much of the Web development hassles, offering you a clean and stable API to build Web apps and microservices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AdonisJs has first-class support for databases like MariaDB and MySQL&lt;/li&gt;
&lt;li&gt;The dedicated validation provider helps to validate user input easily.&lt;/li&gt;
&lt;li&gt;Comes with tools to protect websites against common web attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Learn to use Adonis form their &lt;a href="https://docs.adonisjs.com/"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the AdnoisJS &lt;a href="https://discord.gg/vDcEjq6"&gt;discord server&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. &lt;a href="https://github.com/theatre-js/theatre"&gt;TheatreJs&lt;/a&gt; - 8.8k stars
&lt;/h2&gt;

&lt;p&gt;Theatre.js is an animation library for high-fidelity motion graphics. It is designed to help you express detailed animation, enabling you to create intricate movements and convey nuance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Theatre.js can be used both programmatically and visually.&lt;/li&gt;
&lt;li&gt;Can animate 3D objects made with ThreeJs or other 3D libraries&lt;/li&gt;
&lt;li&gt;Ability to design micro-interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Typescript&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Learn to use TheatreJS form their &lt;a href="https://www.theatrejs.com/docs/latest"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the AdnoisJS &lt;a href="https://discord.com/invite/bm9f8F9Y9N"&gt;discord server&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. &lt;a href="https://github.com/Leaflet/Leaflet"&gt;LeafletJs&lt;/a&gt; - 37k stars
&lt;/h2&gt;

&lt;p&gt;Leaflet is an open-source JavaScript library for mobile-friendly interactive maps. Weighing just about 42 KB of gzipped JS plus 4 KB of gzipped CSS code, it has all the mapping features most developers ever need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Designed with simplicity, performance and usability as core.&lt;/li&gt;
&lt;li&gt;Works efficiently across all major desktop and mobile platforms&lt;/li&gt;
&lt;li&gt;Leaflet plugins database helps to extend the functionality to use third-party plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Learn to use LeafletJs form their &lt;a href="https://leafletjs.com/examples.html"&gt;tutorials&lt;/a&gt; and &lt;a href="https://leafletjs.com/reference.html"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope this finds you helpful. Thank you!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Use ChatGPT API to build a README generator app with low-code</title>
      <dc:creator>Teja Kummarikuntla</dc:creator>
      <pubDate>Fri, 10 Feb 2023 12:05:00 +0000</pubDate>
      <link>https://dev.to/tooljet/use-chatgpt-api-to-build-a-readme-generator-app-with-low-code-4h19</link>
      <guid>https://dev.to/tooljet/use-chatgpt-api-to-build-a-readme-generator-app-with-low-code-4h19</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you will learn to build an application that helps to generate a README markdown for a given repository URL. We will use OpenAI’s ChatGPT language model API for generating the content and ToolJet to build the queries and present them in the front end.&lt;/p&gt;

&lt;p&gt;Here’s how the app looks like &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%2F7ovwe5mssj7klub7ehx7.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%2F7ovwe5mssj7klub7ehx7.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bonus: To use the app without building it, you can download and import the ready-to-use &lt;a href="https://github.com/tejakummarikuntla/readme-generator" rel="noopener noreferrer"&gt;exported app&lt;/a&gt; to your ToolJet account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;By the way, we will not write more than 3 lines of code. Wait and watch how we do it ⚡&lt;/strong&gt;&lt;/em&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%2Fz382phmjjm0bbq45243w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz382phmjjm0bbq45243w.gif" width="498" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ChatGPT and its API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://openai.com/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; enabled &lt;a href="https://openai.com/api/" rel="noopener noreferrer"&gt;APIs&lt;/a&gt; to access the `GPT-3 models, and you can sign-up from &lt;a href="https://openai.com/api" rel="noopener noreferrer"&gt;openai.com/api&lt;/a&gt; to start using it. Once you have access, &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate the API Key from the &lt;code&gt;Manage Account&lt;/code&gt; section of your profile&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://platform.openai.com/docs/api-reference/introduction" rel="noopener noreferrer"&gt;API reference&lt;/a&gt; guide in the documentation to perform the requests.
In this tutorial, we will be using the following endpoint with request data for generating the README.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint: &lt;code&gt;https://api.openai.com/v1/completions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Method: &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Headers: 
&lt;pre&gt;
{
  “Content-Type":  “application/json”,
  "Authorization": “Bearer &lt;code&gt;&amp;lt;API KEY&amp;gt;&lt;/code&gt;”
} 
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Body:
&lt;pre&gt;
{
"model": "text-davinci-002",
"prompt": " &lt;code&gt;&amp;lt;PROMPT TO GENERATE THE README&amp;gt;&lt;/code&gt;”,
"max_tokens": 2048,
"n": 10,
"temperature": 0.9,
“Stream”: false,
“logprobs”:null
}
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not get intimidated if you are not sure how to use these, you’ll learn to use them with ToolJet in the coming sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  ToolJet - an open-source low-code framework.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; enables us to build the application with the pre-built ready-to-use components and quickly deploy it to the web, and we’ll also use it to create the ChatGPT API-request queries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Check out the &lt;a href="https://github.com/tooljet/tooljet" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and hit ⭐ to save it and to let others know about ToolJet, this means a lot for us.&lt;/em&gt;&lt;/strong&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%2Fqzn0nw57u3uufvuevwel.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%2Fqzn0nw57u3uufvuevwel.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
With this, you are all set to start building the application. Let’s go! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the application UI.
&lt;/h2&gt;

&lt;p&gt;Let us start building the application with the front end; as you know, we’ll be using ToolJet to build it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set-up
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Sign-up at &lt;a href="https://tooljet.com" rel="noopener noreferrer"&gt;https://tooljet.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;After logging in, create a new application&lt;/li&gt;
&lt;li&gt;Name the application as you wish or set it as &lt;code&gt;README generator&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2F43szhn6ro8mip8qz1ge4.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%2F43szhn6ro8mip8qz1ge4.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Arrange the components
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Search for the &lt;code&gt;Container&lt;/code&gt; component in the Components Manager.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;Container&lt;/code&gt; component to the &lt;code&gt;Canvas&lt;/code&gt; by dragging and dropping.&lt;/li&gt;
&lt;li&gt;Extend the width and height of the container by expanding the component borders&lt;/li&gt;
&lt;li&gt;Search and add the &lt;code&gt;Text&lt;/code&gt; component to the Canvas from the component manager and set the text value to &lt;code&gt;Generate README for your repositories&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&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%2Foti1v14pfpuh0m2or8gr.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%2Foti1v14pfpuh0m2or8gr.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search and add the &lt;code&gt;Text Input&lt;/code&gt; component and set the placeholder text to &lt;code&gt;Enter your repository URL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a button from the component manager and set the Button text to &lt;code&gt;Generate&lt;/code&gt; from the properties tab and border-radius to 10 from the styles tab of the component.&lt;/li&gt;
&lt;/ol&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%2Fhauh0erairnwyyec0tqq.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%2Fhauh0erairnwyyec0tqq.png" alt=" " width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the &lt;code&gt;Text area&lt;/code&gt; component to the canvas from the component manager, remove the default text and set the &lt;/li&gt;
&lt;li&gt;Placeholder value to &lt;code&gt;Enter your repo URL and click generate&lt;/code&gt;
Add two buttons with Buttons text &lt;code&gt;Download&lt;/code&gt; and &lt;code&gt;Copy&lt;/code&gt; below the text area component
&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%2F0tvv6sphdqd6hgprsw7t.png" alt=" " width="800" height="390"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this, the application UI is ready! &lt;br&gt;
Additionally, you can improve the UI by exploring the various styles and properties of the components. &lt;/p&gt;

&lt;h3&gt;
  
  
  Build the query with OpenAI API.
&lt;/h3&gt;

&lt;p&gt;Let’s build the query to perform an API request to OpenAI’s ChatGPT model. We’ll be using the request-data from the previous section.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From the query panel, click on &lt;code&gt;RestAPI&lt;/code&gt; option&lt;/li&gt;
&lt;li&gt;Set the query name to &lt;code&gt;getReadme&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Change the Request method from &lt;code&gt;GET&lt;/code&gt; to &lt;code&gt;POST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enter the Endpoint &lt;a href="https://api.openai.com/v1/completions" rel="noopener noreferrer"&gt;https://api.openai.com/v1/completions&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Set the Headers to

&lt;ul&gt;
&lt;li&gt;Content-Type: &lt;code&gt;applicaiton/json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Authorization: &lt;code&gt;Bearer &amp;lt;API KEY&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Replace the &lt;code&gt;&amp;lt;API KEY&amp;gt;&lt;/code&gt; placeholder with your Api Key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbme3lc6y6x699xbf93og.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%2Fbme3lc6y6x699xbf93og.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hop over to the Body section and toggle the &lt;code&gt;Raw JSON&lt;/code&gt; to paste the below JSON &lt;/p&gt;

&lt;pre&gt;
{
   "model": "text-davinci-002",
   "prompt": "generate the README for the github repo: {{components.textinput1.value}}",
   "max_tokens": 1000,
   "temperature": 0.9,
   "top_p": 1,
   "n": 3,
   "stream": false,
   "logprobs: null
}
&lt;/pre&gt;

&lt;p&gt;Notice the the value of the &lt;code&gt;prompt&lt;/code&gt; attribute, the &lt;code&gt;&lt;/code&gt;&lt;code&gt;{{components.textinput1.value}}&lt;/code&gt;&lt;code&gt;&lt;/code&gt; brings the URL from the text input component, the component-id &lt;code&gt;textinput1&lt;/code&gt; can be different in your case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fby82tmesvxwqsuhtueim.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%2Fby82tmesvxwqsuhtueim.png" alt=" " width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Toggle ON the &lt;code&gt;Enable Transformations&lt;/code&gt; to write a Javascript code that alters the incoming data for displaying on the &lt;code&gt;Text Area&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;Enable Transformations&lt;/code&gt; turned ON, paste the below code
&lt;pre&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;
const choices = data.choices
const result = choices.map(item =&amp;gt; item.text).join('')
return result
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&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%2F0qt43debbpbz77siokk5.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%2F0qt43debbpbz77siokk5.png" alt=" " width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hit &lt;strong&gt;Save&lt;/strong&gt; on the top right corner of the query panel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect Query to the UI
&lt;/h3&gt;

&lt;p&gt;Now that we have the UI and the Query read, we can now connect both to bring the functionality and make this a complete app.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select the button &lt;code&gt;Generate&lt;/code&gt; and click on &lt;code&gt;Add handler&lt;/code&gt; from the Properties tab
Set the event to &lt;code&gt;On click&lt;/code&gt;, action to &lt;code&gt;Run Query&lt;/code&gt; and the Query to &lt;code&gt;getReadme&lt;/code&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%2Fv6k7n0a7ar6vaivfwuy4.png" alt=" " width="800" height="385"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the Properties tab, click on the Loading State’s &lt;code&gt;Fx&lt;/code&gt; and set the value to &lt;code&gt;&lt;/code&gt;&lt;code&gt;{{queries.getReadme.isLOading}}&lt;/code&gt;&lt;code&gt;&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%2Fafaehcxll1oabli16056.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%2Fafaehcxll1oabli16056.png" alt=" " width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;code&gt;Text area&lt;/code&gt; component and the default value to &lt;code&gt;&lt;/code&gt;&lt;code&gt;{{queries.getReadme.data}}&lt;/code&gt;&lt;code&gt;&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%2Fr4pglwfuwxh8gsg1dgpt.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%2Fr4pglwfuwxh8gsg1dgpt.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;br&gt;
Select the &lt;code&gt;Download&lt;/code&gt; button and click on &lt;code&gt;Add Handler&lt;/code&gt; button from the Properties tab&lt;/p&gt;

&lt;p&gt;Set the &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event: On click&lt;/li&gt;
&lt;li&gt;Action: Generate file&lt;/li&gt;
&lt;li&gt;Type: Text&lt;/li&gt;
&lt;li&gt;File name: README.md&lt;/li&gt;
&lt;li&gt;Data: &lt;code&gt;&lt;/code&gt;&lt;code&gt;{{queries.getReadme.data}}&lt;/code&gt;&lt;code&gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqtgrj506pz8a7q8xcrgj.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%2Fqtgrj506pz8a7q8xcrgj.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, add an event handler to the Copy button to copy the data from the query &lt;code&gt;getReadme&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event: On click&lt;/li&gt;
&lt;li&gt;Action: Copy to clipboard&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;&lt;/code&gt;&lt;code&gt;{{queries.getReadme.data}}&lt;/code&gt;&lt;code&gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awesome!!, pat yourself if you have made it so far!&lt;br&gt;
We are just a step away from deploying the application, let’s quickly test the functionality of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the application
&lt;/h2&gt;

&lt;p&gt;You can instantly test the application by entering a GitHub repository URL and hit the generate button.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make sure to test the README generation&lt;/li&gt;
&lt;li&gt;The Download button should export the text content to a file named &lt;code&gt;README.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The copy button should copy the README content to the clipboard.&lt;/li&gt;
&lt;/ol&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%2Fq7fd9trof7ka408txfkt.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%2Fq7fd9trof7ka408txfkt.png" alt=" " width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the application
&lt;/h2&gt;

&lt;p&gt;As we built the app using ToolJet, it has the ability to deploy the application to the web in a single click.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the Release button in the to right corner&lt;/li&gt;
&lt;li&gt;Click on the Share button left to the Release button&lt;/li&gt;
&lt;li&gt;Toggle the &lt;code&gt;Make application public?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Customize the URL path if needed&lt;/li&gt;
&lt;/ol&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%2Fvuuanclxcfss7jywr5ib.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%2Fvuuanclxcfss7jywr5ib.png" alt=" " width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you’ve successfully built the application 🎉!!&lt;br&gt;
It’s okay if you were unable to make it at any instance, you can always get help from our Slack workspace. Join us at &lt;a href="https://tooljet.com/slack" rel="noopener noreferrer"&gt;https://tooljet.com/slack&lt;/a&gt; and raise your questions at &lt;code&gt;#general&lt;/code&gt; or drop them here as a comment. We are always here to help you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and what’s next?
&lt;/h2&gt;

&lt;p&gt;With this tutorial, you’re pretty much ready to build apps with ToolJet. You had learnt&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building UI with components and customize them with properties and styles&lt;/li&gt;
&lt;li&gt;Using Query panel to add data-source and perform requests&lt;/li&gt;
&lt;li&gt;Running JavaScript to transform and return the data&lt;/li&gt;
&lt;li&gt;Connect the Quereis with the application UI&lt;/li&gt;
&lt;li&gt;Deploy and sharing to the web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this, you are proficient enough to build applications using ToolJet, make sure to check out &lt;a href="https://blog.tooljet.com" rel="noopener noreferrer"&gt;blog&lt;/a&gt; and try out other tutorials.&lt;/p&gt;

&lt;p&gt;Top of this, you are absolutely welcome to contribute to the &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;ToolJet codebase&lt;/a&gt;, check out our &lt;a href="https://github.com/ToolJet/ToolJet" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and be sure to hit the Star ⭐&lt;/p&gt;

</description>
      <category>portfolio</category>
      <category>career</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
