@statezero/core
v0.2.39
Published
The type-safe frontend client for StateZero - connect directly to your backend models with zero boilerplate
Maintainers
Readme
StateZero
The Real-Time Django to JavaScript Data Bridge
Connect your Django backend to React/Vue frontends with 90% less code. No repetitive serializers, views, or tight coupling.
Why StateZero?
The Problem: Building modern web apps means writing the same CRUD logic three times - Django models, REST API serializers/views, and frontend data fetching. This creates:
- 80% of app complexity in data shuttling
- 50% of your codebase devoted to API glue
- Hundreds of hours maintaining sync between frontend and backend
The Solution: StateZero eliminates the API layer entirely. Write Django models once, query them directly from JavaScript with the same ORM syntax you already know.
Features
✨ Django ORM Syntax in JavaScript - Use .filter(), .exclude(), .orderBy() exactly like Django
⚡ Real-Time Updates - UI automatically updates when backend data changes
🔒 Django Permissions - Your existing permission classes work on the frontend
📝 Auto-Generated TypeScript - Perfect type safety from your Django models
🚀 Optimistic Updates - UI feels instant, syncs in background
🔗 Deep Relationships - Traverse foreign keys naturally: todo.category.name
Quick Example
1. Register Your Django Model
# todos/crud.py
from statezero.adaptors.django.config import registry
from .models import Todo
registry.register(Todo)2. Query From JavaScript Like Django
// Get all incomplete todos, ordered by priority
const todos = Todo.objects
.filter({ is_completed: false })
.orderBy("-priority", "created_at");
// Complex queries with relationships
const urgentWorkTodos = Todo.objects.filter({
priority: "high",
category__name: "Work",
due_date__lt: "2024-12-31",
});
// Django-style field lookups
const searchResults = Todo.objects.filter({
title__icontains: "meeting",
created_by__email__endswith: "@company.com",
});3. Real-Time Updates in One Line
<script setup>
import { useQueryset } from "@statezero/core/vue";
// This list automatically updates when todos change
const todos = useQueryset(() => Todo.objects.filter({ is_completed: false }));
</script>
<template>
<div v-for="todo in todos.fetch({ limit: 10 })" :key="todo.id">
{{ todo.title }}
</div>
</template>The Magic: Optimistic vs Confirmed
Optimistic (Instant UI)
// UI updates immediately, syncs later
const newTodo = Todo.objects.create({
title: "Buy groceries",
priority: "medium",
});
// Edit optimistically
todo.title = "Buy organic groceries";
todo.save(); // UI updates instantly
// Delete optimistically
todo.delete(); // Gone from UI immediatelyConfirmed (Wait for Server)
// Wait for server confirmation
const confirmedTodo = await Todo.objects.create({
title: "Important meeting",
});
// Wait for update confirmation
await todo.save();
// Wait for deletion confirmation
await todo.delete();Advanced Django ORM Features
Complex Filtering with Q Objects
import { Q } from "@statezero/core";
// Multiple OR conditions
const urgentTodos = Todo.objects.filter({
Q: [Q("OR", { priority: "high" }, { due_date__lt: "tomorrow" })],
});
// Nested conditions
const myImportantTodos = Todo.objects.filter({
Q: [
Q(
"AND",
{ assigned_to: currentUser.id },
Q("OR", { priority: "high" }, { is_flagged: true })
),
],
});Aggregation & F Expressions
import { F } from "@statezero/core";
// Count, sum, average like Django
const todoCount = await Todo.objects.count();
const avgPriority = await Todo.objects.avg("priority_score");
// Database-level calculations
await Product.objects.update({
view_count: F("view_count + 1"),
popularity: F("likes * 2 + shares"),
});Get or Create
// Just like Django's get_or_create
const [todo, created] = await Todo.objects.getOrCreate(
{ title: "Daily standup" },
{ priority: "medium", category: workCategory }
);Relationship Traversal
// Access related objects naturally
const todo = await Todo.objects.get({ id: 1 });
console.log(todo.category.name); // Foreign key
console.log(todo.created_by.username); // Another FK
console.log(todo.comments.length); // Reverse FK
// Filter by relationships
const workTodos = Todo.objects.filter({
category__name: "Work",
assigned_to__department__name: "Engineering",
});Testing (Backend-Mode)
StateZero supports frontend tests that run against a real Django test server (no test-only views). You opt-in via a test-only middleware that temporarily relaxes permissions and silences events when a request includes special headers.
Backend Setup (Django Test Settings)
Add the test middleware and enable test mode in your test settings:
# tests/settings.py
STATEZERO_TEST_MODE = True
STATEZERO_TEST_SEEDING_SILENT = True # default behavior, silences events during seeding
MIDDLEWARE = [
# ...
"statezero.adaptors.django.testing.TestSeedingMiddleware",
# ...
]Behavior:
X-TEST-SEEDING: 1→ temporarily allows all permissions for the requestX-TEST-RESET: 1→ deletes all registered StateZero models for the request
Auth note:
- Test mode does not bypass authentication. If you use DRF TokenAuth, you must either
create a real token for tests, or add a test-only auth class/middleware that accepts
a header like
X-TEST-USER-IDand returns a test user.
Start the test server:
python manage.py statezero_testserver --addrport 8000Optional request hook:
# tests/settings.py
STATEZERO_TEST_REQUEST_CONTEXT = "myapp.test_utils.statezero_test_context"Your factory should accept the request and return a context manager. This allows libraries like django-ai-first to wrap each test request (e.g., time control).
Frontend Setup (Vue / JS)
Use the testing helpers and the remote manager to call the backend directly without local updates.
import {
setupTestStateZero,
seedRemote,
resetRemote,
createActionMocker,
} from "@statezero/core/testing";
import { getModelClass } from "../model-registry";
import { ACTION_REGISTRY } from "../action-registry";
import { Todo } from "../models/default/django_app/todo";
import { vueAdapters } from "./statezero-adapters";
const testHeaders = setupTestStateZero({
apiUrl: "http://localhost:8000/statezero",
getModelClass,
adapters: vueAdapters,
// Optional: test-only force-auth header support (backend must opt in)
// testAuthUserId: 1,
// testAuthHeaderName: "X-TEST-USER-ID",
});
// Reset: deletes all registered StateZero models on the backend
await resetRemote(testHeaders, () => Todo.remote.delete());
// Seed: run standard ORM writes with X-TEST-SEEDING enabled
await seedRemote(testHeaders, () =>
Todo.remote.create({ title: "Seeded todo" })
);
// Optional: temporarily force-auth as a test user for a block
// await testHeaders.withAuthUserId(1, () =>
// Todo.remote.create({ title: "Seeded as user 1" })
// );
// Action mocking for frontend tests
const actionMocker = createActionMocker(ACTION_REGISTRY);
actionMocker.mock("send_notification", async () => ({ ok: true }));Notes:
Model.remote(orModel.objects.remote()) uses the normal ORM AST/serializers, but skips local store updates and returns raw backend responses.- These helpers are intended for tests that run against a live Django test server.
Installation
Backend
pip install statezero
pip install django-cors-headers pusherFrontend
npm i @statezero/coreGenerate TypeScript Models
npx @statezero/core syncWhy Choose StateZero Over...
🆚 HTMX: Use modern React/Vue with full JavaScript ecosystem while keeping backend simplicity
🆚 Firebase/Supabase: Keep your Django backend, models, and business logic. No vendor lock-in.
🆚 OpenAPI/GraphQL: Get real-time updates and Django ORM power, not just basic CRUD
🆚 Traditional REST APIs: Write 90% less boilerplate. Focus on features, not data plumbing.
Get Started
Run pip install statezero and npm install @statezero/core to begin.
