npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@compilers/s2client

v1.0.2

Published

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

Downloads

448

Readme

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.

<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

// ❌ 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

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

Installation

npm install @compilers/s2client

Setup

1. Configure Vite Plugin

// 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

// 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

<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

<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

<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

<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

<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
<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

// ❌ 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

// ✅ 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


Build a lot with little code.