/how-to-build-lovable

How to build Directory service with Lovable?

Learn how to build a scalable secure Directory service with Lovable step by step including setup schema design access control and deployment tips.

Book a free  consultation
4.9
Clutch rating 🌟
600+
Happy partners
17+
Countries served
190+
Team members
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free No-Code consultation

How to build Directory service with Lovable?

Build a simple Directory service in Lovable that stores people (name, title, email, phone) in Supabase and gives a CRUD UI in your app — no terminal required. You’ll add a small client-side API (uses Supabase REST), create the UI pages, set two Secrets (SUPABASE_URL, SUPABASE_ANON\_KEY) in Lovable Cloud, test in Preview, and Publish. Table creation in Supabase is done in the Supabase dashboard (outside Lovable).

 

What we’re building / changing (plain English)

 

Directory service: a page that lists directory entries, lets you add, edit, delete and search entries. Data lives in a Supabase table. Lovable will create client-side code that talks to Supabase REST endpoints using the anon key stored as a Lovable Secret.

 

Lovable-native approach

 

We will use Chat Mode edits to create files (no terminal). Add Secrets via Lovable Cloud Secrets UI. Use Preview to run and test the UI. If you later need DB schema changes or migrations, do that in the Supabase dashboard (outside Lovable). If you require advanced server code, export to GitHub from Lovable and continue locally.

 

Meta-prompts to paste into Lovable (paste each as a separate message)

 

Prompt 1 — Create Supabase REST helper

Goal: create a small helper that calls Supabase REST endpoints using Secrets.

Exact files to create/modify:

  • create src/lib/supabaseRest.ts with the code below

Acceptance criteria: done when file exists and exports listDirectory, createEntry, updateEntry, deleteEntry functions.

Secrets/integration: uses process.env.SUPABASE_URL and process.env.SUPABASE_ANON\_KEY (Lovable Secrets will inject these at runtime).

// src/lib/supabaseRest.ts
// helper using Supabase REST API (no extra deps)
const url = process.env.SUPABASE_URL!;
const anon = process.env.SUPABASE_ANON_KEY!;

async function request(path: string, opts: any = {}) {
  const res = await fetch(`${url}/rest/v1/${path}`, {
    headers: {
      'apikey': anon,
      'Authorization': `Bearer ${anon}`,
      'Content-Type': 'application/json',
      'Prefer': 'return=representation'
    },
    ...opts
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

export async function listDirectory(q = '') {
  // simple text search on name and email
  const filter = q ? `?or=(name.ilike.*${encodeURIComponent('%'+q+'%')}*,email.ilike.*${encodeURIComponent('%'+q+'%')}*)` : '';
  return request(`directory${filter}`, { method: 'GET' });
}

export async function createEntry(row: any) {
  return request('directory', { method: 'POST', body: JSON.stringify(row) });
}

export async function updateEntry(id: number, row: any) {
  return request(`directory?id=eq.${id}`, { method: 'PATCH', body: JSON.stringify(row) });
}

export async function deleteEntry(id: number) {
  return request(`directory?id=eq.${id}`, { method: 'DELETE' });
}

 

Prompt 2 — Create Directory page UI

Goal: add a page that lists entries, supports add/edit/delete/search.

Exact files to create/modify:

  • create src/pages/directory.tsx (or src/pages/index.tsx if you prefer root)

Acceptance criteria: page renders with a list, a form to add, buttons to edit/delete, and search input that filters list.

// src/pages/directory.tsx
import React, {useEffect, useState} from 'react';
import { listDirectory, createEntry, updateEntry, deleteEntry } from '../lib/supabaseRest';

export default function DirectoryPage() {
  const [rows, setRows] = useState([]);
  const [q, setQ] = useState('');
  const [form, setForm] = useState({id:0,name:'',title:'',email:'',phone:''});

  async function refresh() {
    try {
      const data = await listDirectory(q);
      setRows(data);
    } catch(e){ console.error(e); }
  }

  useEffect(()=>{ refresh() }, [q]);

  async function onSave(e:any){
    e.preventDefault();
    try {
      if (form.id) {
        await updateEntry(form.id, form);
      } else {
        await createEntry(form);
      }
      setForm({id:0,name:'',title:'',email:'',phone:''});
      await refresh();
    } catch(e){ console.error(e); }
  }

  async function onDelete(id:number){
    if(!confirm('Delete?')) return;
    await deleteEntry(id);
    refresh();
  }

  function startEdit(r:any){ setForm(r); }

  return (
    <div style={{padding:20}}>
      <h2>Directory</h2>
      <input placeholder="Search" value={q} onChange={e=>setQ(e.target.value)} />
      <ul>
        {rows.map((r:any)=>(
          <li key={r.id}>
            <b>{r.name}</b> — {r.title} — {r.email} — {r.phone}
            <button onClick={()=>startEdit(r)}>Edit</button>
            <button onClick={()=>onDelete(r.id)}>Delete</button>
          </li>
        ))}
      </ul>

      <form onSubmit={onSave}>
        <h3>{form.id ? 'Edit' : 'Add'}</h3>
        <input placeholder="Name" value={form.name} onChange={e=>setForm({...form,name:e.target.value})} />
        <input placeholder="Title" value={form.title} onChange={e=>setForm({...form,title:e.target.value})} />
        <input placeholder="Email" value={form.email} onChange={e=>setForm({...form,email:e.target.value})} />
        <input placeholder="Phone" value={form.phone} onChange={e=>setForm({...form,phone:e.target.value})} />
        <button type="submit">Save</button>
      </form>
    </div>
  );
}

 

Prompt 3 — Add Secrets in Lovable Cloud and test

Goal: configure Secrets so Preview can call Supabase.

Exact steps (Lovable UI):

  • Open Lovable Cloud > Secrets UI
  • Create secret SUPABASE\_URL with value from your Supabase project (e.g., https://xyz.supabase.co)
  • Create secret SUPABASE_ANON_KEY with project's anon public key

Acceptance criteria: Secrets saved; Preview requests succeed (no 401).

 

Supabase table (outside Lovable)

 

Run this SQL in the Supabase dashboard SQL editor (not in Lovable):

-- create table in Supabase dashboard SQL editor
create table public.directory (
  id serial primary key,
  name text,
  title text,
  email text,
  phone text
);

 

How to verify in Lovable Preview

 

  • Open Preview and navigate to /directory (or /). The list should load.
  • Add an entry via the form: it should appear in the list.
  • Edit and Delete buttons should update/remove rows.
  • Search should filter results (type and wait for refresh).

 

How to Publish / re-publish

 

Use Lovable's Publish button. Publishing will use the same Secrets from Lovable Cloud. No terminal required. If you later need server-only code or migrations, export to GitHub from Lovable and run CLI tools locally (this is outside Lovable).

 

Common pitfalls in Lovable (and how to avoid them)

 

  • Missing Secrets: 401 responses — ensure SUPABASE_URL and SUPABASE_ANON\_KEY are in Lovable Secrets and exactly named.
  • Table not created: 404 or empty lists — run the provided SQL in Supabase dashboard.
  • Using supabase-js: avoid adding new packages inside Lovable unless you export to GitHub; the REST approach avoids dependency problems in Preview.
  • CORS/Network: Supabase project must allow requests from your app domain; usually works with anon key + dashboard settings.

 

Validity bar

 

  • This uses only Lovable features: Chat Mode file edits, Preview, Secrets, Publish. No terminal required for the app to work. Table creation in Supabase dashboard is required outside Lovable. If you need migrations or server functions, export to GitHub and run CLI locally (marked clearly as outside Lovable).

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

How to add weighted fuzzy search to the Directory service

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

How to add adaptive per-route rate limiting to the Directory service

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

How to add resilient webhooks to the Directory service

This prompt helps an AI assistant understand your setup and guide to build the feature

AI AI Prompt

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free No-Code consultation

Best Practices for Building a Directory service with AI Code Generators

Build the directory as a schema-first service: use AI code generators to create structured listing drafts, validate them server-side, store canonical data in a real DB (Supabase), secure API keys in Lovable Secrets, test with Lovable Preview and human review before Publish, and sync to GitHub when you need CLI-level changes. Keep AI outputs deterministic (low temp, schema or function-calling), add rate-limit/backoff, and always include human-in-the-loop moderation for writes.

 

Architecture & workflow (practical)

 

Keep responsibilities separate:

  • AI generator: propose structured JSON for a listing (title, slug, category, contact, geolocation, tags, short/long descriptions).
  • API layer: validate, enrich, dedupe, and persist into Supabase.
  • UI / Lovable: use Chat Mode edits, Preview to iterate UI/UX, and Secrets UI for keys. Use Publish to ship and GitHub sync only when you need to edit locally or add infra.

 

Practical best practices

 

  • Schema-first prompts — tell the model exact JSON schema and use low temperature. Prefer function-calling or strict JSON output to avoid hallucinations.
  • Server-side validation — never trust AI output client-side. Validate required fields, types, and sanitize strings before DB insert.
  • Rate limiting & idempotency — track request IDs and add retry/backoff; prevent duplicate listings from repeated generation attempts.
  • Secrets in Lovable — put OPENAI and SUPABASE keys in the Secrets UI (never in code). Reference them in your Lovable app environment.
  • Human review — queue AI-suggested listings for moderator approval before public visibility. Use Preview to test flows inside Lovable.
  • Observability — log AI calls, model version, prompt hash and response for debugging (store logs outside user-facing DB if sensitive).
  • Preview → Publish → GitHub — iterate in Chat Mode and Preview. Use Publish when stable; export to GitHub only when you need local builds or CI.

 

Minimal working server example (generate → validate → save)

 

// Express-like handler for a serverless/API route
import fetch from "node-fetch";
import { createClient } from "@supabase/supabase-js";

// // These values should come from Lovable Secrets / env
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_KEY = process.env.SUPABASE_KEY;
const OPENAI_KEY = process.env.OPENAI_API_KEY;
const OPENAI_MODEL = process.env.OPENAI_MODEL || "gpt-4";

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

export default async function handler(req, res) {
  // // req.body.prompt: user input or base info
  const prompt = req.body.prompt || "Local coffee shop with pastries";

  // // Ask AI for strict JSON matching schema
  const system = `You must output ONLY valid JSON matching the schema:
  { "name": string, "slug": string, "category": string, "description": string, "latitude": number|null, "longitude": number|null, "contact": { "email": string|null, "phone": string|null } }`;

  const ua = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Authorization": `Bearer ${OPENAI_KEY}` },
    body: JSON.stringify({
      model: OPENAI_MODEL,
      messages: [{ role: "system", content: system }, { role: "user", content: prompt }],
      temperature: 0.0
    })
  });
  const data = await ua.json();
  // // Extract text and parse JSON (be defensive)
  const text = data.choices?.[0]?.message?.content || "";
  let parsed;
  try {
    parsed = JSON.parse(text);
  } catch (e) {
    return res.status(500).json({ error: "AI returned non-JSON", raw: text });
  }

  // // Basic validation
  if (!parsed.name || !parsed.slug) return res.status(400).json({ error: "missing fields" });

  // // Persist to Supabase
  const { data: row, error } = await supabase.from("listings").insert([parsed]).select().single();
  if (error) return res.status(500).json({ error: error.message });
  res.json({ listing: row });
}

 

Tips that matter in Lovable

 

  • No terminal: perform dependency or build changes by editing files in Chat Mode and using Publish or GitHub sync — you can’t run npm locally inside Lovable UI.
  • Secrets UI: set API keys there. Don’t commit them. Use Preview to exercise runtime behavior that depends on secrets.
  • Test the worst cases: simulate malformed AI responses in Preview and ensure your API rejects/flags them instead of persisting.


Recognized by the best

Trusted by 600+ businesses globally

From startups to enterprises and everything in between, see for yourself our incredible impact.

RapidDev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with.

They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

Arkady
CPO, Praction
Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost.

He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Donald Muir
Co-Founder, Arc
RapidDev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space.

They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Mat Westergreen-Thorne
Co-CEO, Grantify
RapidDev is an excellent developer for custom-code solutions.

We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Emmanuel Brown
Co-Founder, Church Real Estate Marketplace
Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 

This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Samantha Fekete
Production Manager, Media Production Company
The pSEO strategy executed by RapidDev is clearly driving meaningful results.

Working with RapidDev has delivered measurable, year-over-year growth. Comparing the same period, clicks increased by 129%, impressions grew by 196%, and average position improved by 14.6%. Most importantly, qualified contact form submissions rose 350%, excluding spam.

Appreciation as well to Matt Graham for championing the collaboration!

Michael W. Hammond
Principal Owner, OCD Tech

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.