# Client-side SQL Taboo?

### The Problem We Are Solving

Writing **SQL in client-side code** is traditionally **taboo**. Outside of local or hobby projects, it exposes query logic, introduces security risks, and forces developers to separate frontend intent from backend execution.

To compensate, teams build **custom payload shapes**, **validation layers**, and **query adapters** that translate frontend requests into backend SQL. This pattern is reliable—but repetitive. Frontends repeatedly shape data, backends repeatedly undo that shaping, and the final result is still the same: **a SQL query returning JSON**.

![alt text](Traditional.png)

This system **short-circuits that loop**. See how: [Technical Summary](README2.md)

Instead of inventing new interfaces, it **preserves SQL as the contract**. Frontend developers write queries directly, which are **encrypted and compiled at build time** into safe runtime code. **No executable SQL exists in client runtime**, and execution remains **server-side**.

![alt text](New.png)

The model is **declarative**, **reactive**, and **compiler-driven**, eliminating boilerplate while keeping execution clear and secure.

# s2client

**SQL-first reactive queries for Vue 3**

Write SQL directly in your Vue components. No REST endpoints, no GraphQL schemas, no ORMs. Just SQL—encrypted at build time, executed server-side, fully reactive.

```vue
<script setup>
import { s2client } from '@compilers/s2client'

// One line. Fully reactive. Built-in loading/error states.
const users = s2client.query(`select * from users`).execute()
</script>

<template>
  <div v-for="user in users" :key="user.id">
    {{ user.name }}
  </div>
</template>
```

## Why s2client?

Traditional frontend development buries SQL behind layers of abstraction. You build REST endpoints, validate payloads, manage state manually, and write mountains of boilerplate. Backend teams create endpoints. Frontend teams wait. Everyone writes more code than necessary.

**s2client eliminates the ceremony.**

- ✅ **Write SQL directly in Vue** - No backend bottlenecks
- ✅ **One line replaces dozens** - No fetch calls, no manual state management
- ✅ **Forms → Query parameters** - Reactive objects bind directly to SQL
- ✅ **Single backend endpoint** - No endpoint sprawl, centralized security
- ✅ **Build-time encryption** - SQL encrypted with AES-256, never exposed in client
- ✅ **Fully reactive** - Query results are Vue refs, updates automatic
- ✅ **Database agnostic** - Works with PostgreSQL, MySQL, SQLite, SQL Server

## The Problem

### Traditional Frontend: ~40 lines of boilerplate

```typescript
// ❌ Manual state management, loading, errors, fetch logic
const users = ref([])
const loading = ref(false)
const error = ref(null)

async function fetchUsers() {
  loading.value = true
  error.value = null
  
  try {
    const response = await fetch('/api/users')
    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
    const data = await response.json()
    users.value = data
  } catch (e) {
    error.value = e.message
  } finally {
    loading.value = false
  }
}

onMounted(() => fetchUsers())
```

### s2client: 1 line

```typescript
// ✅ One line. Loading/error states built-in. Fully reactive.
const users = s2client.query(`select * from users`).execute()
```

## Installation

```bash
npm install @compilers/s2client
```

## Setup

### 1. Configure Vite Plugin

```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { dbbuilderTransform } from '@compilers/s2client/plugin'

export default defineConfig({
  plugins: [
    vue(),
    dbbuilderTransform({
      endpoint: '/api/query',
      debounceMs: 300,
      debug: false
    })
  ]
})
```

### 2. Create Backend Endpoint

```typescript
// Example Express.js endpoint
app.post('/api/query', authenticate, async (req, res) => {
  const { payload, params } = req.body
  
  try {
    // Decode the SQL (comes encrypted from frontend)
    const sql = decodePayload(payload)
    
    // Validate auth/permissions here
    // Apply rate limiting if needed
    
    // Execute query with parameterized values
    const rows = await db.query(sql, params)
    
    res.json({ rows })
  } catch (error) {
    res.status(400).json({ error: error.message })
  }
})
```

That's it. One endpoint handles all queries.

## Examples

### Read Data

```vue
<script setup>
// Automatic execution
const users = s2client.query(`select * from users`).execute()

// Or triggered execution
const orders = s2client.query(`select * from orders`)
</script>

<template>
  <button @click="orders.execute()">Load Orders</button>
  
  <div v-if="orders.loading">Loading...</div>
  <div v-if="orders.error">{{ orders.error }}</div>
  
  <div v-for="order in orders" :key="order.id">
    {{ order.total }}
  </div>
</template>
```

### Insert Data

```vue
<script setup>
const form = reactive({
  name: '',
  email: ''
})

const insertUser = s2client.query(`
  insert into users (name, email) 
  values (:name, :email)
`, form)

function handleSubmit() {
  insertUser.execute()
}
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="form.name" placeholder="Name" />
    <input v-model="form.email" placeholder="Email" />
    
    <button :disabled="insertUser.loading">
      {{ insertUser.loading ? 'Saving...' : 'Submit' }}
    </button>
    
    <div v-if="insertUser.error">{{ insertUser.error }}</div>
  </form>
</template>
```

### Update Data

```vue
<script setup>
const form = reactive({
  id: null,
  name: '',
  email: ''
})

const updateUser = s2client.query(`
  update users 
  set name = :name, email = :email 
  where id = :id
`, form)

// Form inputs update parameters automatically
// No need to gather data into payloads
</script>
```

### Delete Data

```vue
<script setup>
const userId = ref(null)

const deleteUser = s2client.query(`
  delete from users where id = :id
`, { id: userId })

function handleDelete() {
  if (confirm('Delete this user?')) {
    deleteUser.execute()
  }
}
</script>
```

### Reactive Queries

```vue
<script setup>
// Query re-runs automatically when searchTerm changes
const searchTerm = ref('')

const results = s2client.query(`
  select * from products 
  where name like :search
`, { 
  search: computed(() => `%${searchTerm.value}%`)
}).execute()
</script>

<template>
  <input v-model="searchTerm" placeholder="Search products..." />
  
  <div v-for="product in results" :key="product.id">
    {{ product.name }}
  </div>
</template>
```

## Built-in Properties

Every query returns a reactive object with:

- `loading` - Boolean, true during execution
- `error` - Error message if query fails
- `executed` - Boolean, true after first execution
- `last_update` - Timestamp of last successful execution
- `execute()` - Method to manually run/re-run the query
- `reset()` - Method to clear results and reset state

```vue
<script setup>
const users = s2client.query(`select * from users`).execute()
</script>

<template>
  <div v-if="users.loading">Loading...</div>
  <div v-if="users.error">Error: {{ users.error }}</div>
  
  <div v-for="user in users" :key="user.id">
    {{ user.name }}
  </div>
  
  <button @click="users.execute()">Refresh</button>
  <button @click="users.reset()">Clear</button>
  
  <p>Last updated: {{ users.last_update }}</p>
</template>
```

## Security

s2client doesn't bypass security—it **centralizes** it.

### Build-Time Security
- ✅ SQL encrypted with **AES-256** at build time
- ✅ Compiler enforces **parameterized queries only**
- ✅ No executable SQL exists in client runtime
- ✅ Query structure hidden from client inspection

### Runtime Security
You still control access server-side:
- ✅ Validate **authentication/authorization**
- ✅ Sanitize and validate **query parameters**
- ✅ Apply **rate limiting** and **logging**
- ✅ Use **prepared statements** (parameters always separate from SQL)
- ✅ Implement **row-level security** in your database

Security is applied **once, in one place**, instead of scattered across dozens of endpoints.

## Guiding Principles

### 1. SQL is the Proven Abstraction
SQL is the language of data. It's established, powerful, and universal. QueryBuilders, ORMs, REST APIs, GraphQL—they all add unnecessary complexity. s2client lets you use SQL directly, as intended.

### 2. Less Code is Better Code
Every abstraction layer adds boilerplate. s2client eliminates mountains of frontend ceremony and backend endpoint sprawl. One line replaces dozens. Less code means fewer bugs, faster development, and easier maintenance.

### 3. Security First Development
SQL in the client is powerful but must be secured. s2client encrypts SQL at build time with AES-256, ensuring your queries remain private. The compiler enforces parameterized queries only. You still validate parameters server-side, but security is centralized in one place.

### 4. Intuitive Simple API
Method signatures are clean: 2-3 arguments max. `s2client.query(sql, params)` is all you need to know. No complex builders, no configuration hell, no mental overhead. The API is self-documenting.

### 5. Intuitive Reactivity
Query results are Vue refs that work seamlessly with `watch()`, `watchEffect()`, and templates. Change a parameter, the query re-runs automatically. No manual subscriptions, no callback hell. Reactivity just works.

### 6. Database Agnostic
s2client doesn't care what database you use. PostgreSQL, MySQL, SQLite, SQL Server—write standard SQL and your backend handles execution. Switch databases without changing frontend code.

## Comparison

### Before: Multiple endpoints for each resource

```typescript
// ❌ Traditional Backend: 5 endpoints per resource
app.get('/api/users', async (req, res) => { /* ... */ })
app.get('/api/users/:id', async (req, res) => { /* ... */ })
app.post('/api/users', async (req, res) => { /* ... */ })
app.put('/api/users/:id', async (req, res) => { /* ... */ })
app.delete('/api/users/:id', async (req, res) => { /* ... */ })

// Orders - 5 more endpoints
// Products - 5 more endpoints
// That's 15 endpoints for 3 resources!
```

### After: One endpoint for everything

```typescript
// ✅ s2client Backend: ONE endpoint for ALL queries
app.post('/api/query', authenticate, async (req, res) => {
  const { payload, params } = req.body
  
  const sql = decodePayload(payload)
  const result = await db.query(sql, params)
  
  res.json({ rows: result })
})

// That's it! Handles:
// - All user queries
// - All order queries  
// - All product queries
// - ANY SQL operation

// Apply auth, rate limiting, logging in ONE place
```

## Frontend = Fullstack

With s2client, frontend developers become fullstack:

- 🚀 Write SQL directly in Vue components
- 🚀 No waiting for backend teams to create endpoints
- 🚀 No context switching between frontend/backend
- 🚀 Ship features faster with less code

## Requirements

- **Node.js** >= 18
- **Vue** 3.x
- **Vite** (build tool)
- Any SQL database (PostgreSQL, MySQL, SQLite, etc.)

## License

MIT

## Learn More

- [Full Documentation](https://github.com/timkerr222/compile-sqljs)
- [Live Examples](https://s2client-demo.vercel.app)//coming soon.
- [Why s2client?](./README1.md)
- [Technical Summary](./README2.md)

---

**Build a lot with little code.**
