> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nekt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# From data to app with Lovable

> Build a Lovable app powered by your Nekt data, using the MCP Server, the Data API, or a Supabase destination.

[Lovable](https://lovable.dev) turns prompts into working web applications. On its own, it builds an app with mock data. Connected to Nekt, it builds an app on top of your real, governed tables, and keeps showing current data after you ship it.

This guide covers the three ways to connect the two, when to use each, and the exact prompts to paste into Lovable.

<Info>
  **You do not need to write code.** Each step gives you a prompt to paste into Lovable or your agent. The text around each prompt explains what is happening and how to confirm it worked.
</Info>

## Build time vs. runtime

The single most useful thing to understand before you start: the three methods do not compete, they solve different halves of the problem.

* The **MCP Server** is how the *Lovable agent* understands your data while it builds. It reads your schemas, previews rows, and generates an app grounded in tables that actually exist. It is not used by your app's end users.
* The **Data API** and the **Supabase destination** are how the *deployed app* gets data at runtime, when a real user opens a page.

A typical project uses MCP plus one runtime method.

```mermaid theme={null}
flowchart LR
    subgraph BUILD["Build time"]
        L[Lovable agent] -->|MCP Server| N[Nekt]
    end
    subgraph RUN["Runtime"]
        A[Your app] -->|Edge Function<br/>Data API| N2[Nekt]
        A -->|SQL| S[(Supabase)]
        N2b[Nekt] -->|Destination<br/>on a schedule| S
    end
```

## Choosing a runtime method

|                         | **Data API**                                       | **Supabase destination**                       |
| :---------------------- | :------------------------------------------------- | :--------------------------------------------- |
| **How data arrives**    | The app queries Nekt on demand                     | Nekt pushes tables into Supabase on a schedule |
| **Latency**             | Seconds per query (analytical engine)              | Milliseconds (Postgres)                        |
| **Best for**            | Dashboards, reports, aggregates that change slowly | Search, filters, pagination, per-user views    |
| **Data volume**         | Any size, filtered in SQL                          | Whatever fits comfortably in your Postgres     |
| **Freshness**           | As fresh as the underlying table                   | As fresh as the last destination run           |
| **Setup effort**        | One Edge Function and an API key                   | A destination in Nekt, no app code             |
| **Joins with app data** | Not directly                                       | Yes, it is the same database                   |

<Tip>
  If your app has interactive filtering or search over more than a few thousand rows, use the **Supabase destination**. If it renders precomputed charts and KPIs, use the **Data API**. When in doubt, Supabase is the more forgiving default for an app.
</Tip>

## Before you start

<Steps>
  <Step title="Have data in your Nekt Catalog">
    You need at least one table in the [Catalog](/catalog/overview). If you are starting from scratch, add a [Source](/sources/overview) first.
  </Step>

  <Step title="Create a Lovable project">
    Sign up at [lovable.dev](https://lovable.dev). New Lovable projects come with a Supabase backend, which matters for two of the three methods below.
  </Step>

  <Step title="Know your plan requirements">
    The MCP Server requires a **paid Nekt plan**, and custom MCP servers require a **paid Lovable plan**. The Data API requires a **paid Nekt plan**. The Supabase destination works on any plan that includes Destinations.
  </Step>
</Steps>

***

## Step 1: Shape app-ready tables in Nekt

Whichever method you choose, do this first. An app should read tables that are already shaped the way the screen displays them: one row per data point, columns already named and typed, no joins or heavy aggregation left for runtime.

Precomputing in Nekt makes the app faster, keeps the business logic in one governed place, and means you can change what the app shows without touching the app.

Paste this into Claude, Codex, or any agent connected to the [Nekt MCP Server](/mcp-server/introduction):

```text Prompt theme={null}
Using the Nekt MCP tools, create the tables my app will read.

I am building an app with these screens: [describe each screen, for example:
a KPI header, a monthly revenue chart, and a searchable customer list].

For each screen:
- Create a scheduled Query in Nekt that writes a clean table to the trusted layer.
- Name each table app_<screen>, for example app_kpis, app_monthly_revenue,
  app_customers.
- Shape each table so it matches exactly what the screen shows, one row per data
  point, so the app does not have to transform anything.
- Give every table a stable, unique identifier column I can use as a primary key.
- Schedule it to refresh daily.

When done, list the tables you created with their columns and types.
```

**Confirm it worked:** open your [Catalog](https://app.nekt.ai) and check that each `app_*` table exists and holds the expected rows.

<Note>
  For logic SQL cannot express (machine learning, external API calls, complex reshaping), ask the agent to use a [Notebook](/notebooks/overview) instead. The result lands in the Catalog the same way, so the rest of this guide is unchanged.
</Note>

***

## Method 1: Connect the MCP Server

This gives the Lovable agent direct, read-only access to your Catalog while it builds. Instead of inventing a schema, it lists your real tables, previews rows, and writes the UI against columns that exist.

<Info>Requires a **paid Lovable plan** (for custom MCP servers) and a **paid Nekt plan** (for MCP Server access).</Info>

### Setup

<Steps>
  <Step title="Enable the MCP Server in Nekt">
    Go to **Integrations → MCP Server** and click **Configure**, then **Enable MCP Server**. Copy the **Endpoint** URL. See the [MCP setup guide](/mcp-server/setup) for cloud-specific steps.
  </Step>

  <Step title="Create a scoped token">
    In the **MCP Tokens** section, create a token. Scope it to only the `app_*` tables from Step 1 rather than granting full access, since this token defines everything the agent can read.
  </Step>

  <Step title="Add Nekt as a connector in Lovable">
    In Lovable, go to **Settings → Connectors → Personal connectors** and click **New MCP server**.

    * **Server name**: `Nekt`
    * **Server URL**: the Endpoint you copied

    Authenticate with **OAuth** (recommended) by clicking **Add & authorize** and logging in with your Nekt credentials, or select the bearer token option and paste the token you created.
  </Step>

  <Step title="Confirm the connection">
    Nekt appears in your list of personal connectors. Ask Lovable *"List the tables available in Nekt"* and check that your `app_*` tables come back.
  </Step>
</Steps>

### Build the app

Paste this into Lovable:

```text Prompt theme={null}
You have access to my Nekt MCP server.

1. List the tables available in Nekt and preview a few rows of each app_* table.
2. Build a dashboard with one section per table:
   - app_kpis as a KPI header row
   - app_monthly_revenue as a line chart
   - app_customers as a searchable, paginated table
3. Use the real column names and types from Nekt. Do not invent fields.
4. For now, seed the UI with the sample rows you previewed so I can review the
   layout. I will wire up live data next.
```

**Confirm it worked:** the preview renders with your actual column names and realistic values, not placeholder data like "Product A" or "[user@example.com](mailto:user@example.com)".

<Warning>
  The MCP connection belongs to your Lovable account, not to the deployed app. When a user opens your published app, it cannot reach Nekt through MCP. Keep going to Method 2 or Method 3 to give the app live data.
</Warning>

***

## Method 2: Query the Data API at runtime

The app calls a small server-side function, that function queries Nekt with SQL, and the results come back as JSON. Your Nekt API key never leaves the server.

In a Lovable project, that server-side function is a **Supabase Edge Function**, which every Lovable project can create.

### Architecture

```mermaid theme={null}
flowchart LR
    A[Browser<br/>your Lovable app] -->|invoke| B[Supabase<br/>Edge Function]
    B -->|cached JSON| A
    B -->|Data API<br/>x-api-key| C[Nekt]
    C -->|app_* tables| D[(Catalog)]
```

### Setup

<Steps>
  <Step title="Create a Nekt API key">
    Go to **Workspace Settings → API Keys** and [create an API key](https://app.nekt.ai/settings/api-keys). Copy it, you will paste it into Lovable as a secret.
  </Step>

  <Step title="Ask Lovable to build the Edge Function">
    Paste the prompt below. When Lovable asks where to store the API key, add it as a Supabase secret named `NEKT_API_KEY`, never as a plain value in the code.
  </Step>
</Steps>

```text Prompt theme={null}
Add live data from Nekt to this app using the Nekt Data API.

Create a Supabase Edge Function called nekt-data that:
- Reads my API key from the NEKT_API_KEY secret. Never expose it to the browser.
- Calls POST https://api.nekt.ai/api/v1/sql-query/
    - Header: x-api-key: <NEKT_API_KEY>, Content-Type: application/json
    - Body: {"sql": "<a SELECT against one app_* table>", "mode": "csv"}
- Reads presigned_urls from the response, downloads the CSV from the first URL,
  and parses it into rows.
- Runs one query per table (app_kpis, app_monthly_revenue, app_customers) in
  parallel and returns all datasets as JSON keyed by section.
- Only runs SQL from a fixed list defined inside the function. Never accept SQL
  from the client.
- Caches each result for 1 hour, since the tables refresh daily.

Then update the app to call this function on load and render each dataset into
its matching section, with loading and error states.
```

**Confirm it worked:** open the preview. The sections fill with current Nekt data, and the Edge Function logs in Supabase show a successful call.

<Accordion title="Reference: the Data API call and the function Lovable builds">
  The Data API runs SQL and returns a presigned URL to download the results:

  <CodeGroup>
    ```bash Request theme={null}
    curl --request POST \
      --url https://api.nekt.ai/api/v1/sql-query/ \
      --header 'x-api-key: YOUR_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
        "sql": "SELECT month, revenue FROM \"trusted\".\"app_monthly_revenue\" ORDER BY month",
        "mode": "csv"
      }'
    ```

    ```json Response theme={null}
    {
      "state": "SUCCEEDED",
      "presigned_urls": [
        "https://storage.googleapis.com/bucket/query-....csv?..."
      ],
      "data_scanned_in_bytes": 11250,
      "execution_time_in_millis": 408
    }
    ```
  </CodeGroup>

  ```javascript supabase/functions/nekt-data/index.ts theme={null}
  // One entry per section. The client picks a key, never sends SQL.
  const DATASETS = {
    kpis: `SELECT * FROM "trusted"."app_kpis"`,
    revenue: `SELECT month, revenue FROM "trusted"."app_monthly_revenue" ORDER BY month`,
    customers: `SELECT id, name, total_spent FROM "trusted"."app_customers"`,
  };

  async function runQuery(sql: string) {
    const res = await fetch("https://api.nekt.ai/api/v1/sql-query/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-api-key": Deno.env.get("NEKT_API_KEY")!,
      },
      body: JSON.stringify({ sql, mode: "csv" }),
    });

    const result = await res.json();
    if (result.state !== "SUCCEEDED" || !result.presigned_urls?.length) {
      throw new Error(`Query failed: ${result.state_change_reason ?? result.state}`);
    }

    const csv = await fetch(result.presigned_urls[0]).then((r) => r.text());
    return parseCsv(csv);
  }
  ```

  <Warning>Presigned URLs expire after **1 hour**, and the results themselves live in your own cloud storage, not on Nekt servers. Cache the parsed rows, never the URL.</Warning>
</Accordion>

### What to watch out for

* **Cache aggressively.** The Data API is powered by Athena or BigQuery, so a query takes seconds, not milliseconds. Without caching, every page load pays that cost and requeries data that only changes daily.
* **Keep the SQL server-side.** The Edge Function should expose a fixed set of named datasets. Accepting SQL from the browser hands any visitor full read access to your Catalog.
* **All CSV values are strings.** Ask Lovable to cast numbers and dates when parsing. Or use `parquet` mode, reviewing the [known limitations](/data-api/known-limitations) first.
* **This is not a transactional API.** Filtering and pagination against the Data API on every keystroke will feel slow. That is what Method 3 is for.

***

## Method 3: Send data to Supabase

Nekt writes your tables directly into the Postgres database your Lovable app already uses. The app queries them with its normal Supabase client, with no Nekt-specific code anywhere, and Nekt refreshes them on a schedule.

This is the best fit for real applications: filters, search, pagination, and joins against your app's own tables all work at Postgres speed.

### Architecture

```mermaid theme={null}
flowchart LR
    D[(Nekt Catalog)] -->|Destination<br/>scheduled| S[(Supabase Postgres)]
    S -->|supabase-js| A[Your Lovable app]
```

### Setup

<Steps>
  <Step title="Get your Supabase connection details">
    In your Lovable project, open the linked Supabase project. Click **Connect** in the top navigation and select the **Session pooler** tab. Copy the host, port, user, password, and database.

    <Warning>You must use the **Session pooler** connection. Transaction mode and direct connections are not supported.</Warning>
  </Step>

  <Step title="Whitelist Nekt's IP if needed">
    If your Supabase project has **Network Restrictions** enabled (**Project Settings → Database → Network Restrictions**), add Nekt's public IP in CIDR notation, for example `203.0.113.42/32`. The IP is shown on the connector configuration page in Nekt.
  </Step>

  <Step title="Add a Supabase destination in Nekt">
    Go to [Destinations](https://app.nekt.ai/destinations), click **Add destination**, and select **Supabase**. Enter the Session pooler credentials and set the target schema.

    <Tip>
      Load Nekt tables into a dedicated schema such as `nekt`, not `public`. It keeps a clear line between tables your app owns and tables Nekt manages, and it prevents a load method from ever touching your application data.
    </Tip>
  </Step>

  <Step title="Select tables, keys, and schedule">
    Choose the `app_*` tables from Step 1, define a primary key for each, pick a load method (**upsert** is the usual choice for app tables), and set how often the data should refresh.

    See the full [Supabase destination guide](/destinations/supabase) for load methods, SSL, and record metadata.
  </Step>

  <Step title="Run the pipeline">
    Trigger the destination manually with the refresh icon, then confirm the tables appear in the Supabase Table Editor.
  </Step>
</Steps>

### Secure the tables

Tables loaded by Nekt arrive without row level security. If they sit in an API-exposed schema, anyone with your app's public key can read every row.

<Warning>
  Before publishing your app, enable RLS on every Nekt-managed table and add explicit read policies. This is the most common mistake in this setup.
</Warning>

Paste this into Lovable:

```text Prompt theme={null}
Nekt loads tables into my Supabase database on a schedule. Secure them:

1. Enable row level security on every table in the nekt schema.
2. Add a policy that allows authenticated users to SELECT, and nothing else.
   No INSERT, UPDATE, or DELETE from the client, since Nekt owns this data and
   any writes will be overwritten on the next sync.
3. If a table should only be visible to certain users, ask me which column
   identifies the owner and scope the policy to it.
```

### Build the app

```text Prompt theme={null}
Build the app against the Nekt-managed tables in my Supabase database:

- app_kpis renders as a KPI header row.
- app_monthly_revenue renders as a line chart.
- app_customers renders as a table with server-side search, sorting, and
  pagination, querying Supabase directly rather than loading everything at once.

Treat these tables as read-only. Never write to them from the app.
```

**Confirm it worked:** search and paginate the customer table in the preview. It should respond instantly, and the results should match what you see in the Nekt Catalog.

### What to watch out for

* **The tables are read-only.** Anything the app writes into them is lost on the next sync. Keep app-generated data (user preferences, comments, statuses) in separate tables your app owns, and join to the Nekt tables.
* **Match the schedule to the need.** The app is exactly as fresh as the last destination run. Hourly for operational screens, daily for reporting is a reasonable starting point.
* **Set a primary key you trust.** Upsert matches on it. An unstable key produces duplicates.
* **Mind the size.** Supabase is a Postgres instance, not a warehouse. Aggregate and filter in Nekt in Step 1 rather than syncing raw tables of tens of millions of rows.

***

## Putting it together

For most projects, the full flow looks like this:

<Steps>
  <Step title="Shape the tables in Nekt">
    Scheduled Queries produce clean `app_*` tables (Step 1).
  </Step>

  <Step title="Connect MCP so Lovable builds on real schemas">
    Method 1. The agent sees your actual columns and generates a UI that fits them.
  </Step>

  <Step title="Pick a runtime path">
    Method 3 (Supabase) for interactive apps, Method 2 (Data API) for dashboards over precomputed data. Nothing stops you from using both: Supabase for the interactive tables, the Data API for an occasional heavy aggregate.
  </Step>

  <Step title="Secure and publish">
    RLS policies on Supabase tables, secrets for the Nekt API key, then publish from Lovable.
  </Step>
</Steps>

## Keeping it fresh

Your app now updates itself. Scheduled Queries refresh the tables in Nekt, and the destination or the Edge Function carries the new data to the app.

* **Change what it shows:** update the Nekt Query. The app picks up the new values with no redeploy.
* **Change the shape of a table:** update the Query, then tell Lovable which columns changed.
* **Change how it looks:** prompt Lovable and republish.

<CardGroup cols={2}>
  <Card title="Lovable integration" icon="heart" href="/integrations/lovable">
    Connection reference, including the Google Sheets option.
  </Card>

  <Card title="Supabase destination" icon="database" href="/destinations/supabase">
    Full setup, load methods, and troubleshooting.
  </Card>

  <Card title="Data API reference" icon="code" href="/data-api/introduction">
    Endpoint details, output formats, and limitations.
  </Card>

  <Card title="MCP Server" icon="message-bot" href="/mcp-server/introduction">
    Tools, tokens, and permissions.
  </Card>
</CardGroup>

## Need help?

Contact our support team if you run into issues while building your app.
