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 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
EntitySpecshape 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 expectednext_page, the backend sentnext_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
WHEREclause 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.
pip install -e . # core: pure standard library, Python 3.9+
pip install -e ".[web]" # + FastAPI, to mount real routersFrontend (Vue 3 + Pinia peers):
cd frontend && npm installimport 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.
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 cursorvue_pinia_crud_store.EntitySpec— the neutral, entity-agnostic descriptionvue_pinia_crud_store.build_crud_router— the FastAPI router factoryvue_pinia_crud_store.CrudHandlers— the framework-free CRUD logicvue_pinia_crud_store.CrudStore/InMemoryStore— the storage seam + defaultvue_pinia_crud_store.compile_filters/FilterSpec— the SQL filter compilervue_pinia_crud_store.encode_cursor/decode_cursor— keyset paginationvue_pinia_crud_store.build_bulk_statement— the bulk SQL builderfrontend/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.
MIT — see LICENSE. The frontend is an original clean-room implementation; see NOTICE.
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.
- 🧪 Powerweave Skunkworks on GitHub — https://github.com/skunkworks-powerweave
- 🌐 Powerweave — https://powerweave.com
- 💼 Powerweave on LinkedIn — https://www.linkedin.com/company/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
- 🌐 Website — https://powerweave.com
- 💼 LinkedIn — https://www.linkedin.com/company/powerweave
- 𝕏 Twitter / X — https://twitter.com/powerweave
▶️ YouTube — https://www.youtube.com/channel/UCE1t_rg38z4n5BAg29PDZFA- 📘 Facebook — https://www.facebook.com/PowerweaveSoftwareSolutions/
- 🛒 Enterprise eCommerce — https://www.powerweave.com/solutions/enterprise-ecommerce/
- 📦 AI-Powered Procurement — https://www.powerweave.com/solutions/procurement/
- 🧮 Microsoft Dynamics ERP — https://www.powerweave.com/solutions/microsoft-dynamics-erp/
- 🌱 Snowkap — Sustainability — https://www.snowkap.com/
- 🎨 Powerweave Studio — https://www.powerweavestudio.com/
- 🧑💼 About Us & Leadership — https://www.powerweave.com/about-us/
- 🚀 Careers — https://www.powerweave.com/careers/
Keywords: crud · fastapi · vue · pinia · multi-tenant · pagination · cursor · router-factory · Powerweave · Powerweave Skunkworks · AI R&D · open source · MIT · Python · forkable.