Home>Developer Toolkit>Building robust AI middleware with Catalyst

Building robust AI middleware with Catalyst

catalyst usecase author
Srilakshmi | Technical writer

Inside the Cookbook

    •  Why is middleware necessary?
      • Protecting your keys and handling API calls
        • Catalyst: The modern full-stack cloud platform
          • Create a Catalyst project
          • Install the CLI globally
          • Initialize your Catalyst project
          • Choose your functions and client features
          • Get your OpenAI API key
          • Add the code
          • Deploy your app
        • Wrapping up

          • When integrating advanced AI models like OpenAI’s GPT series into your applications, a critical yet often overlooked architectural component is AI middleware.
          • This intermediary layer acts as a vital bridge between your frontend and AI APIs, enabling secure, controlled, and scalable communication.

          Why is middleware necessary?

          • Security: Safeguard sensitive API keys by keeping them off client devices.

          • Input validation & transformation: Enforce business logic, sanitize inputs, or enhance prompts before forwarding to the AI.

          • Response formatting: Normalize AI output for consistent client consumption.

          • Operational controls: Implement logging, monitoring, rate-limiting, and user-specific quotas without complicating your frontend.

          In essence, middleware helps you abstract the AI integration, reduce frontend complexity, and adhere to security best practices.

          Protecting your keys and handling API calls

          Consider the below snippet:

          import openai
          openai.api_key = "<your-secret-api-key>"
          response = openai.ChatCompletion.create(
              model="gpt-3.5-turbo",
              messages=[{"role": "user", "content": "your prompt"}]
          )

           

          Anyone inspecting network requests or browser sources can extract your keys and rack up costs or breach data. A backend middleware service acts as a gatekeeper, securely holding API keys and proxying client requests with appropriate validation and controls.

          Catalyst: The modern full-stack cloud platform

          Managing a traditional backend involves configuring servers, SSL, scaling, logging, and deploying code a considerable operational overhead for many teams.

          Catalyst revolutionizes this by providing:

          • Serverless functions: Write backend endpoints in your favorite language without managing infrastructure. Just code, deploy, and you're live.

          • Secrets management: Use environment variables to securely manage sensitive credentials like API keys.

          • Local development & testing: Catalyst runs your full app locally for seamless testing.

          • One-command deployment: Catalyst instantly publishes your app with automatic HTTPS, logging, and scaling.

          This platform-centric approach empowers developers to focus on code and features, not operational complexity.

          One of the best ways to understand this setup is through a practical example. In our case, we can build a small regex generator tool where users describe a pattern in plain English, and the backend returns a valid regex and explanation, powered by AI.

          Let’s jump in:

          Before we start wiring up prompts, we need to lay the foundation, setting up both our Catalyst project and OpenAI access.

          Create a Catalyst project

          If you haven’t already, sign up at Catalyst. You can create a new project either through the Catalyst Developer Console (UI-based) or via the CLI, depending on how you like to work.

          We used the CLI; it’s fast, clean, and gives us more control during development.

          Install the CLI globally

          npm install -g zcatalyst-cli 

           

          Initialize your Catalyst project

          npm install -g zcatalyst-cli 

           

           
           

           

          Choose your functions and client features

          Select the AdvancedIO or BasicIO function as needed. For this, we can use AdvancedIO.


           

          We support multiple programming languages; you can choose your favorite.


           

          For this project, we'll go with a basic web app:


           


           


           

          This setup gives you everything you need to build, test, and deploy both your frontend and backend in one go

          You can achieve the same setup through the Catalyst Web Console

          Get your OpenAI API key

          Now that your app has a home, let’s connect it to OpenAI—the engine behind the regex generation.

          1. Head to OpenAI's platform.

          2. Sign up or log in to your account.

          3. Navigate to the API keys section.

          4. Click Create new secret key and copy it.

          That’s it—with Catalyst initialized and our OpenAI key ready, we’re all set to start coding the AI middleware.

          Now let’s connect everything. Our app will have two main parts: the frontend, which you can build using your favorite language—for now, we’ll use HTML, CSS, and JavaScript to keep it simple—and the backend, written in Python, which takes the input and talks to OpenAI to generate a regex pattern along with a quick explanation.

           

          Add the code

          We’ll place the Python logic inside a Catalyst Advanced I/O function using FastAPI. The best part? Catalyst takes care of all the client and backend setup, so we can focus entirely on building. - Use this sample and enhance the code with your creativity
           

          //This backend function receives a prompt from the user, sends it to OpenAI, and returns a regex pattern with an explanation.
          from fastapi import FastAPI
          from pydantic import BaseModel
          from fastapi.middleware.cors import CORSMiddleware
          import openai
          import logging
          # Set up basic logging to track activity and errors
          logging.basicConfig(level=logging.INFO)
          logger = logging.getLogger(__name__)
          # Initialize the FastAPI app
          app = FastAPI()
          # Enable CORS (Cross-Origin Resource Sharing)
          app.add_middleware(
              CORSMiddleware,
              allow_origins=["*"],  # TODO: Replace with actual domains in production
              allow_credentials=True,
              allow_methods=["*"],
              allow_headers=["*"],
          )
          # Set your OpenAI API key here. 
          # You can securely store your API key as an environment variable
          # and access it instead of hardcoding. Catalyst Functions support this approach.
          openai_api_key = os.getenv("OPENAI_API_KEY")  # Retrieve key from environment variables
          openai.api_key = openai_api_key
          # Define the input format expected from the frontend
          class PromptInput(BaseModel):
              prompt: str  # Example: "Match email addresses"
          # Define the POST endpoint for generating regex
          @app.post("/generate-regex")
          async def generate_regex(data: PromptInput):
              try:
                  # Get the input from the user and clean whitespace
                  user_input = data.prompt.strip()
                  # Handle empty input early
                  if not user_input:
                      logger.warning("Empty prompt received.")
                      return {"error": "Prompt is empty"}
                  logger.info(f"Received prompt: {user_input}")
                  logger.info(f"Using OpenAI Key: {'***' + openai_api_key[-6:]}")  # Log only last part for safety
                  # Define the message format to send to OpenAI
                  base_prompt = f"""
          You are a helpful AI that generates regular expressions.
          Given the following natural language description, return ONLY the regex pattern and then a short explanation.
          Description: "{user_input}"
          Format your response as:
          Pattern: <regex>
          Explanation: <one-line explanation>
          """
                  logger.info("Calling OpenAI API...")
                  # Send the prompt to OpenAI's GPT-3.5 model
                  response = openai.ChatCompletion.create(
                      model="gpt-3.5-turbo",
                      messages=[{"role": "user", "content": base_prompt}],
                      temperature=0.2,
                      max_tokens=200,
                  )
                  logger.info("OpenAI API call successful.")
                  # Extract the content from the API response
                  content = response['choices'][0]['message']['content'].strip()
                  logger.info(f"OpenAI Response:\n{content}")
           
                # Parse the regex and explanation from the response
          regex = ""
          explanation = ""
          for line in content.split('\n'):
              lower_line = line.lower()
              if "pattern" in lower_line:
                  regex = line.split(":", 1)[1].strip()
              elif "explanation" in lower_line:
                  explanation = line.split(":", 1)[1].strip()
          # Handle missing regex pattern in response
          if not regex:
              logger.error("Regex not found in response.")
              return {"error": "Regex not generated properly. Please try again."}
          logger.info(f"Parsed Regex: {regex}")
          logger.info(f"Explanation: {explanation}")
                  # Send back the parsed result to the frontend
                  return {
                      "regex": regex,
                      "explanation": explanation
                  }
              except Exception as e:
                  # Log and return errors in case anything goes wrong
                  logger.exception("Error during regex generation:")
                  return {"error": f"Internal error: {str(e)}"}
          

           

           This JavaScript handles both the AI prompt and regex testing workflow. It sends user input to the backend for regex generation and visually highlights regex matches in test strings.
          You can paste this in your Js file [Main.js]


           

          document.addEventListener('DOMContentLoaded', () => {
              const promptEl = document.getElementById('prompt');
              const generateBtn = document.getElementById('generateBtn');
              const loadingState = document.getElementById('loadingState');
              const errorState = document.getElementById('errorState');
              const errorMessage = document.getElementById('errorMessage');
              const resultSection = document.getElementById('resultSection');
              const regexPattern = document.getElementById('regexPattern');
              const regexExplanation = document.getElementById('regexExplanation');
              // Test elements
              const testBtn = document.getElementById('testBtn');
              const testString = document.getElementById('testString');
              const matchStatus = document.getElementById('matchStatus');
              const matchCount = document.getElementById('matchCount');
              const highlightedText = document.getElementById('highlightedText');
              const matchDetails = document.getElementById('matchDetails');
              const matchList = document.getElementById('matchList');
              const testResults = document.getElementById('testResults');
              // Enable generate button based on prompt input
              promptEl.addEventListener('input', () => {
                  generateBtn.disabled = promptEl.value.trim() === '';
              });
              // Regex generation logic
              generateBtn.addEventListener('click', async () => {
                  const prompt = promptEl.value.trim();
                  if (!prompt) return;
                  errorState.classList.add('hidden');
                  resultSection.classList.add('hidden');
                  loadingState.classList.remove('hidden');
                  console.log('⬆ Sending prompt to backend:', prompt);
                  try {
                      const res = await fetch('/generate-regex', {
                          method: 'POST',
                          headers: { 'Content-Type': 'application/json' },
                          body: JSON.stringify({ prompt }),
                      });
                      const data = await res.json();
                      loadingState.classList.add('hidden');
                      console.log('Response from backend:', data);
                      if (data.error) {
                          errorMessage.textContent = data.error;
                          errorState.classList.remove('hidden');
                      } else {
                          regexPattern.textContent = data.regex || '';
                          regexExplanation.textContent = data.explanation || '';
                          resultSection.classList.remove('hidden');
                      }
                  } catch (err) {
                      loadingState.classList.add('hidden');
                      errorMessage.textContent = 'Failed to generate regex. Please try again.';
                      errorState.classList.remove('hidden');
                      console.error('API Error:', err);
                  }
              });
              // Regex testing logic
              if (testBtn) {
                  testBtn.addEventListener('click', () => {
                      const regexRaw = regexPattern.textContent.trim();
                      const testText = testString.value.trim();
                      console.log('Running Test...');
                      console.log('Regex:', regexRaw);
                      console.log('Test Text:', testText);
                      if (!regexRaw || !testText) return;
                      let regex;
                      try {
                          regex = new RegExp(regexRaw, 'g'); // global flag for all matches
                      } catch (e) {
                          alert('⚠ Invalid regex: ' + regexRaw);
                          console.error('Regex Error:', e);
                          return;
                      }
                      const matches = [...testText.matchAll(regex)];
                      matchCount.textContent = matches.length;
                      if (matches.length > 0) {
                          matchStatus.classList.remove('hidden');
                          testResults.classList.remove('hidden');
                          matchDetails.classList.remove('hidden');
                          // Highlight matches in text
                          highlightedText.innerHTML = testText.replace(regex, match => `<mark>${match}</mark>`);
                          // Show match details
                          matchList.innerHTML = '';
                          matches.forEach((m, i) => {
                              const div = document.createElement('div');
                              div.classList.add('match-item');
                              div.innerHTML = `<span class="match-label">Match ${i + 1}:</span> ${m[0]} <span class="match-position">(Index: ${m.index})</span>`;
                              matchList.appendChild(div);
                          });
                      } else {
                          matchStatus.classList.add('hidden');
                          matchDetails.classList.add('hidden');
                          highlightedText.innerHTML = testText;
                          matchList.innerHTML = '';
                          testResults.classList.remove('hidden');
                      }
                  });
              }
          });
          

           

           

          Add this code in your index.html file located inside Client folder
           

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Regex Assistant - Generate Regular Expressions with AI</title>
              <meta name="description" content="Generate regular expressions from natural language descriptions using AI. Test patterns live and get clear explanations.">
              <link rel="stylesheet" href="main.css">
              <link href="https://cdnjs.cloudflare.com/ajax/libs/lucide/0.344.0/lucide.min.css" rel="stylesheet">
            </head>
            <body>
              <!-- Header -->
              <header class="header">
                <div class="container">
                  <div class="header-content">
                    <div class="logo">
                      <i data-lucide="sparkles"></i>
                    </div>
                    <h1 class="title">Regex Assistant</h1>
                  </div>
                </div>
              </header>
              <main class="main">
                <div class="container">
                  <!-- Input Section -->
                  <div class="card input-section">
                    <div class="form-group">
                      <label for="prompt" class="label">Describe what you want to match</label>
                      <div class="input-wrapper">
                        <textarea id="prompt" placeholder="e.g., match a 10-digit phone number starting with 9" rows="3"></textarea>
                        <i data-lucide="search" class="input-icon"></i>
                      </div>
                    </div>
                    <button id="generateBtn" class="btn-primary" disabled>
                      <i data-lucide="sparkles"></i>
                      <span>Generate Regex</span>
                    </button>
                  </div>
                  <!-- Loading State -->
                  <div id="loadingState" class="card loading-card hidden">
                    <div class="loading-content">
                      <i data-lucide="loader-2" class="loading-spinner"></i>
                      <p>Generating your regex pattern...</p>
                    </div>
                  </div>
                  <!-- Error State -->
                  <div id="errorState" class="card error-card hidden">
                    <div class="error-content">
                      <i data-lucide="alert-circle"></i>
                      <p id="errorMessage">Failed to generate regex. Please try again.</p>
                    </div>
                  </div>
                  <!-- Result Section -->
                  <div id="resultSection" class="result-section hidden">
                    <!-- Generated Regex -->
                    <div class="card">
                      <div class="card-header">
                        <h2>Generated Regex</h2>
                        <button id="copyBtn" class="btn-secondary">
                          <i data-lucide="copy"></i>
                          <span>Copy</span>
                        </button>
                      </div>
                      <div class="regex-display">
                        <code id="regexPattern"></code>
                      </div>
                      <div class="explanation-section">
                        <h3>Explanation</h3>
                        <p id="regexExplanation"></p>
                      </div>
                    </div>
                    <!-- Live Testing -->
                    <div class="card">
                      <div class="card-header">
                        <i data-lucide="test-tube"></i>
                        <h2>Live Testing</h2>
                      </div>
                      <div class="form-group">
                        <label for="testString" class="label">Test String</label>
                        <textarea id="testString" placeholder="Enter text to test against the regex pattern" rows="4"></textarea>
                        <button id="testBtn" class="btn-primary" style="margin-top: 1rem;">
                          <i data-lucide="play"></i>
                          <span>Test Regex</span>
                        </button>
                      </div>
                      <div id="testResults" class="test-results hidden">
                        <div class="matches-header">
                          <span class="matches-count">Matches ( <span id="matchCount">0</span>) </span>
                          <span id="matchStatus" class="match-status hidden">✓ Pattern matches</span>
                        </div>
                        <div id="highlightedText" class="highlighted-text"></div>
                        <div id="matchDetails" class="match-details hidden">
                          <h4>Match Details:</h4>
                          <div id="matchList"></div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <!-- Getting Started -->
                  <div id="gettingStarted" class="card getting-started">
                    <div class="welcome-icon">
                      <i data-lucide="sparkles"></i>
                    </div>
                    <h2>Welcome to Regex Assistant</h2>
                    <p>Describe what you want to match in plain English, and I'll generate a regular expression for you with a clear explanation.</p>
                    <div class="features-grid">
                      <div class="feature-card">
                        <h3>Examples to try:</h3>
                        <ul>
                          <li>• Email addresses</li>
                          <li>• Phone numbers</li>
                          <li>• URL patterns</li>
                          <li>• Date formats</li>
                        </ul>
                      </div>
                      <div class="feature-card">
                        <h3>Features:</h3>
                        <ul>
                          <li>• AI-powered generation</li>
                          <li>• Live testing</li>
                          <li>• Clear explanations</li>
                          <li>• Copy to clipboard</li>
                        </ul>
                      </div>
                    </div>
                  </div>
                </div>
              </main>
              <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
              <script src="main.js"></script>
            </body>
          </html>
          

           

          Add this code in Main.css file located inside the Client Folder 

          
          * {
              margin: 0;
              padding: 0;
              box-sizing: border-box;
          }
          
          body {
              font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
              line-height: 1.6;
              color: #1f2937;
              background: linear-gradient(135deg, #dbeafe 0%, #e0e7ff 50%, #f3e8ff 100%);
              min-height: 100vh;
          }
          
          /* Utility Classes */
          .hidden {
              display: none !important;
          }
          
          .container {
              max-width: 1024px;
              margin: 0 auto;
              padding: 0 1.5rem;
          }
          
          /* Header */
          .header {
              background: rgba(255, 255, 255, 0.8);
              backdrop-filter: blur(12px);
              border-bottom: 1px solid rgba(229, 231, 235, 0.8);
              position: sticky;
              top: 0;
              z-index: 10;
          }
          
          .header-content {
              display: flex;
              align-items: center;
              gap: 0.75rem;
              padding: 1rem 0;
          }
          
          .logo {
              width: 2.5rem;
              height: 2.5rem;
              background: linear-gradient(135deg, #3b82f6, #8b5cf6);
              border-radius: 0.5rem;
              display: flex;
              align-items: center;
              justify-content: center;
              color: white;
          }
          
          .title {
              font-size: 1.5rem;
              font-weight: 700;
              background: linear-gradient(135deg, #2563eb, #7c3aed);
              -webkit-background-clip: text;
              background-clip: text;
              -webkit-text-fill-color: transparent;
              color: transparent;
          }
          
          /* Main Content */
          .main {
              padding: 2rem 0;
          }
          
          /* Cards */
          .card {
              background: rgba(255, 255, 255, 0.8);
              backdrop-filter: blur(12px);
              border-radius: 1rem;
              box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
              border: 1px solid rgba(229, 231, 235, 0.8);
              padding: 1.5rem;
              margin-bottom: 2rem;
              transition: all 0.3s ease;
          }
          
          .card:hover {
              transform: translateY(-2px);
              box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
          }
          
          /* Form Elements */
          .form-group {
              margin-bottom: 1rem;
          }
          
          .label {
              display: block;
              font-size: 0.875rem;
              font-weight: 500;
              color: #374151;
              margin-bottom: 0.5rem;
          }
          
          .input-wrapper {
              position: relative;
          }
          
          textarea {
              width: 100%;
              padding: 0.75rem 3rem 0.75rem 1rem;
              border: 1px solid #d1d5db;
              border-radius: 0.75rem;
              font-size: 1rem;
              resize: vertical;
              transition: all 0.2s ease;
              background: white;
          }
          
          textarea:focus {
              outline: none;
              border-color: #3b82f6;
              box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
          }
          
          .input-icon {
              position: absolute;
              right: 1rem;
              top: 1rem;
              color: #9ca3af;
              width: 1.25rem;
              height: 1.25rem;
          }
          
          /* Buttons */
          .btn-primary {
              width: 100%;
              background: linear-gradient(135deg, #3b82f6, #8b5cf6);
              color: white;
              border: none;
              padding: 0.75rem 1.5rem;
              border-radius: 0.75rem;
              font-size: 1rem;
              font-weight: 500;
              cursor: pointer;
              display: flex;
              align-items: center;
              justify-content: center;
              gap: 0.5rem;
              transition: all 0.2s ease;
          }
          
          .btn-primary:hover:not(:disabled) {
              background: linear-gradient(135deg, #2563eb, #7c3aed);
              transform: translateY(-1px);
          }
          
          .btn-primary:disabled {
              opacity: 0.5;
              cursor: not-allowed;
          }
          
          .btn-secondary {
              background: #f3f4f6;
              color: #374151;
              border: none;
              padding: 0.5rem 1rem;
              border-radius: 0.5rem;
              font-size: 0.875rem;
              cursor: pointer;
              display: flex;
              align-items: center;
              gap: 0.5rem;
              transition: all 0.2s ease;
          }
          
          .btn-secondary:hover {
              background: #e5e7eb;
          }
          
          /* Card Headers */
          .card-header {
              display: flex;
              align-items: center;
              justify-content: space-between;
              margin-bottom: 1rem;
          }
          
          .card-header h2 {
              font-size: 1.25rem;
              font-weight: 600;
              color: #111827;
              display: flex;
              align-items: center;
              gap: 0.75rem;
          }
          
          /* Regex Display */
          .regex-display {
              background: #f9fafb;
              border-radius: 0.75rem;
              padding: 1rem;
              margin-bottom: 1rem;
          }
          
          .regex-display code {
              font-size: 1.125rem;
              font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
              color: #111827;
              word-break: break-all;
          }
          
          /* Explanation Section */
          .explanation-section h3 {
              font-size: 1.125rem;
              font-weight: 500;
              color: #111827;
              margin-bottom: 0.5rem;
          }
          
          .explanation-section p {
              color: #374151;
              line-height: 1.7;
          }
          
          /* Test Results */
          .test-results {
              margin-top: 1rem;
          }
          
          .matches-header {
              display: flex;
              align-items: center;
              justify-content: space-between;
              margin-bottom: 0.5rem;
          }
          
          .matches-count {
              font-size: 0.875rem;
              font-weight: 500;
              color: #374151;
          }
          
          .match-status {
              font-size: 0.75rem;
              color: #059669;
              background: #d1fae5;
              padding: 0.25rem 0.5rem;
              border-radius: 0.25rem;
          }
          
          .highlighted-text {
              background: #f9fafb;
              border-radius: 0.75rem;
              padding: 1rem;
              font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
              font-size: 0.875rem;
              white-space: pre-wrap;
              word-break: break-word;
              line-height: 1.5;
          }
          
          .highlighted-text mark {
              background: #fef3c7;
              padding: 0.125rem 0.25rem;
              border-radius: 0.25rem;
              color: #92400e;
          }
          
          .match-details {
              margin-top: 1rem;
          }
          
          .match-details h4 {
              font-size: 0.875rem;
              font-weight: 500;
              color: #374151;
              margin-bottom: 0.5rem;
          }
          
          .match-item {
              font-size: 0.75rem;
              background: #dbeafe;
              padding: 0.5rem;
              border-radius: 0.25rem;
              margin-bottom: 0.5rem;
          }
          
          .match-item .match-label {
              font-weight: 500;
          }
          
          .match-item .match-position {
              color: #6b7280;
              margin-left: 0.5rem;
          }
          
          /* Loading State */
          .loading-card {
              text-align: center;
              padding: 3rem 1.5rem;
          }
          
          .loading-content {
              display: flex;
              flex-direction: column;
              align-items: center;
              gap: 1rem;
          }
          
          .loading-spinner {
              width: 2rem;
              height: 2rem;
              color: #3b82f6;
              animation: spin 1s linear infinite;
          }
          
          @keyframes spin {
              from { transform: rotate(0deg); }
              to { transform: rotate(360deg); }
          }
          
          /* Error State */
          .error-card {
              border-color: #fca5a5;
              background: rgba(254, 242, 242, 0.8);
          }
          
          .error-content {
              display: flex;
              align-items: center;
              gap: 0.75rem;
              color: #dc2626;
          }
          
          /* Getting Started */
          .getting-started {
              text-align: center;
              padding: 2rem;
          }
          
          .welcome-icon {
              width: 4rem;
              height: 4rem;
              background: linear-gradient(135deg, #3b82f6, #8b5cf6);
              border-radius: 1rem;
              display: flex;
              align-items: center;
              justify-content: center;
              margin: 0 auto 1.5rem;
              color: white;
          }
          
          .welcome-icon i {
              width: 2rem;
              height: 2rem;
          }
          
          .getting-started h2 {
              font-size: 1.5rem;
              font-weight: 700;
              color: #111827;
              margin-bottom: 1rem;
          }
          
          .getting-started > p {
              color: #6b7280;
              margin-bottom: 2rem;
              max-width: 28rem;
              margin-left: auto;
              margin-right: auto;
          }
          
          .features-grid {
              display: grid;
              grid-template-columns: 1fr;
              gap: 1rem;
              max-width: 32rem;
              margin: 0 auto;
              text-align: left;
          }
          
          @media (min-width: 768px) {
              .features-grid {
                  grid-template-columns: 1fr 1fr;
              }
          }
          
          .feature-card {
              background: #f9fafb;
              padding: 1rem;
              border-radius: 0.75rem;
          }
          
          .feature-card h3 {
              font-weight: 500;
              color: #111827;
              margin-bottom: 0.5rem;
          }
          
          .feature-card ul {
              list-style: none;
              color: #6b7280;
              font-size: 0.875rem;
          }
          
          .feature-card li {
              margin-bottom: 0.25rem;
          }
          
          /* Responsive Design */
          @media (max-width: 768px) {
              .container {
                  padding: 0 1rem;
              }
              
              .card {
                  padding: 1rem;
              }
              
              .card-header {
                  flex-direction: column;
                  align-items: flex-start;
                  gap: 1rem;
              }
              
              .matches-header {
                  flex-direction: column;
                  align-items: flex-start;
                  gap: 0.5rem;
              }
          }
          
          /* Animation for result section */
          .result-section {
              animation: fadeInUp 0.5s ease-out;
          }
          
          @keyframes fadeInUp {
              from {
                  opacity: 0;
                  transform: translateY(20px);
              }
              to {
                  opacity: 1;
                  transform: translateY(0);
              }
          }
          
          
          

           

           

          Deploy your app

          Deploy your app easily in your terminal with the following commands:

          Local testing - catalyst serve

          Deployment - catalyst deploy

           


          Check out the Regex Builder UI, live on our built-in Feature Web Client Hosting!

          Explore Slate, our all-in-one solution for frontend deployment: Check it out

          Catalyst makes it super easy to deploy your logic with just a few commands. Use our sample and get creative!

          Extending the pattern

          Use case

          Description

          SQL Query Generation

          Translate natural language to SQL statements.

          AI-Powered Form Validation

          Validate inputs dynamically with AI-powered rules.

          Intelligent FAQ Chatbot

          Provide context-aware answers from knowledge bases.

          Natural Language Filter Builder

          Construct complex filters/search queries interactively.

           

          Wrapping up

          Catalyst is your security-first AI middleware, enabling you to integrate intelligence into your applications with confidence and control perfect for building compliant, scalable, and trustworthy AI solutions

          Happy building!