Skip to content

Latest commit

 

History

History
202 lines (157 loc) · 10.2 KB

File metadata and controls

202 lines (157 loc) · 10.2 KB

vue-pinia-crud-store

One CRUD pattern, written once, served from both ends — a FastAPI router factory and a matching Vue 3 + Pinia store.

Every multi-tenant app grows the same surface for every entity: a list with filters and pagination, a get, a create, a patch, a delete, a bulk edit — and then the same thing again on the frontend, with optimistic updates so the UI feels instant. Hand-writing that per entity is where bugs and drift live: a cursor that's stable on one table and offset-based on another, a filter that forgot its column allow-list, an audit row emitted twice, a frontend that mutates local state but never rolls back when the request fails.

This library captures that pattern once, on both sides of the wire, and parameterizes it by the entity:

Backend (Python, MIT). Describe an entity with an EntitySpec (table, primary key, readable / filterable / patchable columns) and get its whole REST surface:

Route Behavior
GET /<resource> list — keyset (cursor) pagination + column-allow-listed filters
GET /<resource>/{id} fetch one (tenant-scoped; 404 hides absent and cross-tenant)
POST /<resource> create — one audit row, full post-insert diff
PATCH /<resource>/{id} partial update — (before, after) diff per touched field
DELETE /<resource>/{id} hard delete — one audit row
POST /<resource>/bulk up to 500 ids, one transaction, one audit row

The storage layer is an injectable seam (CrudStore). The default InMemoryStore is pure standard library, so the package imports and runs on bare Python 3.9 with zero third-party dependencies. FastAPI and a real DB driver are optional — they live behind the seam and are imported lazily.

Frontend (clean-room, MIT). createCrudStore({ resource }) builds a Pinia store that talks to those routes: cursor pagination (fetchFirstPage / fetchNextPage), filters, and optimistic create / patch / delete — the local list mutates immediately and rolls back automatically if the request fails. It is an original implementation authored from the REST contract alone (see NOTICE).

The moat

The hard part of CRUD is not the happy path — it is the dozen small invariants that have to hold on every entity and both ends, and that rot the instant someone hand-writes the eleventh router from memory. This library makes those invariants structural:

  • The contract is one artifact, not two codebases that drift. The same EntitySpec shape that compiles the backend's filter allow-list and cursor ordering also names the routes the frontend store calls. The list response key, the bulk operation set, the pagination token — defined once, consumed on both sides. There is no "the frontend expected next_page, the backend sent next_cursor" because the contract is the same document.
  • Injection defense is in the compiler, not in reviewer vigilance. Filter values are always driver-parameterized; filter column names — which cannot be parameterized — are checked against the entity's allow-list, and a table alias must match an identifier regex. A new entity inherits all three by construction; you cannot forget the allow-list because there is no per-entity hand-written WHERE clause to forget it in.
  • Tenant isolation and the audit trail are part of the path, not bolted on. Every handler is tenant-scoped at the seam; every mutation emits exactly one best-effort audit row (one per bulk batch, not one per id) and never fails the request if the audit write hiccups. Not-found and cross-tenant-hidden are deliberately the same 404 so the API can't be used to enumerate other tenants' ids.
  • Optimism that can't strand the UI. The frontend store applies the change locally first, then reconciles with the server response — and on failure it restores the prior value (patch) or the removed row at its original index (delete). The rollback is in the factory, so every entity's store gets it; no screen reimplements it and gets it subtly wrong.
  • The heavy dependencies are optional. The whole pattern — filter compiler, cursor codec, bulk builder, framework-free handlers — runs and is unit-tested with no web framework and no database. FastAPI is a lazy import inside the router factory; the DB is a seam. Adopting the pattern costs an EntitySpec, not a framework lock-in.

Forked into a product, the advantage compounds: every new entity is a spec, not a router and a store and a test file; the injection, tenancy, audit, and rollback guarantees come for free and identically each time; and the API the frontend consumes is provably the API the backend serves. See docs/MOAT.md.

Install

pip install -e .            # core: pure standard library, Python 3.9+
pip install -e ".[web]"     # + FastAPI, to mount real routers

Frontend (Vue 3 + Pinia peers):

cd frontend && npm install

Quickstart (backend, no dependencies)

import asyncio
from vue_pinia_crud_store import CrudHandlers, InMemoryStore, WIDGET_SPEC

async def main():
    widgets = CrudHandlers(WIDGET_SPEC, InMemoryStore())
    row = await widgets.create("tenant-A", {"name": "alpha", "status": "active"})
    await widgets.patch("tenant-A", row["id"], {"status": "archived"})
    page = await widgets.list("tenant-A", filters=[
        {"field": "status", "op": "eq", "value": "archived"},
    ])
    print(page)  # {"widgets": [...], "next_cursor": None}

asyncio.run(main())

Mount it as a FastAPI router:

from fastapi import FastAPI
from vue_pinia_crud_store import build_crud_router, InMemoryStore, WIDGET_SPEC

app = FastAPI()
app.include_router(build_crud_router(
    WIDGET_SPEC, InMemoryStore(), tenant_resolver=lambda auth: auth,
))

See examples/minimal/quickstart.py.

Quickstart (frontend)

import { createCrudStore } from 'vue-pinia-crud-store/createCrudStore';

export const useWidgets = createCrudStore({ resource: 'widgets', baseUrl: '/api' });

// in a component:
const widgets = useWidgets();
await widgets.fetchFirstPage({ filters: [{ field: 'status', op: 'eq', value: 'active' }] });
await widgets.create({ name: 'alpha', status: 'active' });  // optimistic
await widgets.fetchNextPage();                               // keyset cursor

What's here

  • vue_pinia_crud_store.EntitySpec — the neutral, entity-agnostic description
  • vue_pinia_crud_store.build_crud_router — the FastAPI router factory
  • vue_pinia_crud_store.CrudHandlers — the framework-free CRUD logic
  • vue_pinia_crud_store.CrudStore / InMemoryStore — the storage seam + default
  • vue_pinia_crud_store.compile_filters / FilterSpec — the SQL filter compiler
  • vue_pinia_crud_store.encode_cursor / decode_cursor — keyset pagination
  • vue_pinia_crud_store.build_bulk_statement — the bulk SQL builder
  • frontend/src/createCrudStore.js — the clean-room Vue 3 + Pinia store factory

Full reference: docs/API.md. How to extend it for your entities: docs/FORKING.md.

License

MIT — see LICENSE. The frontend is an original clean-room implementation; see NOTICE.


About Powerweave Skunkworks

Powerweave Skunkworks is the AI R&D division of Powerweave Software Services — a rapid-innovation lab that turns real-world product feedback into working, reusable, open-source building blocks. Working in parallel to the main engineering backlog, a lean, cross-functional team of product and technology specialists (UX, data, software engineering, and AI) fast-tracks high-priority ideas into validated modules ready for full-scale build-out.

vue-pinia-crud-store is one such building block — a de-domained, MIT-licensed, dependency-light component extracted from Powerweave's internal R&D and engineered to be forked into any SaaS or enterprise product.

About Powerweave

Powerweave Software Services Pvt. Ltd. is a digital-transformation company founded in 2001 and headquartered in Mumbai, India. With 25+ years of experience, 1,700+ professionals, and 350+ global customers, Powerweave builds platforms, processes, and teams across enterprise eCommerce, AI-powered procurement, Microsoft Dynamics ERP, business services, and sustainability — with a strong focus on cutting-edge AI automation that streamlines workflows, reduces manual errors, and accelerates decision-making. Powerweave is ISO 27001:2013 certified.

Explore Powerweave

Maintainers — Powerweave Skunkworks


Keywords: crud · fastapi · vue · pinia · multi-tenant · pagination · cursor · router-factory · Powerweave · Powerweave Skunkworks · AI R&D · open source · MIT · Python · forkable.