
Research
/Security News
11 Malicious Go Packages Distribute Obfuscated Remote Payloads
Socket uncovered 11 malicious Go packages using obfuscated loaders to fetch and execute second-stage payloads via C2 domains.
Entity-Centric Reactive Development for FastHTML
StarModel enables you to define your application's data structure and behavior in one place, minimizing configuration overhead and maximizing development speed. Build reactive web applications entirely in Python by encapsulating both backend logic and frontend interactivity around your entities.
Stop separating your data from your behavior. StarModel brings entity-driven development to web applications:
@rt
decoratorsgit clone https://github.com/ndendic/StarModel.git
cd StarModel
uv sync
python app/main.py # Visit http://localhost:5001
or install package like
pip install git+https://github.com/ndendic/StarModel.git
Below is full example you can run (uses MonsterUI for styling)
from fasthtml.common import *
from monsterui.all import *
from starmodel import *
app, rt = fast_app(
htmx=False,
hdrs=(
Theme.zinc.headers(),
datastar_script, # <-- Load datastar cdn script
),
)
class Counter(State):
count: int = 0
update_count: int = 0
@event
def increment(self, amount: int = 1):
self.count += amount
self.update_count += 1
@event
def decrement(self, amount: int = 1):
self.count -= amount
self.update_count += 1
@event
def reset(self):
self.count = 0
self.update_count += 1
@rt
def index(req: Request):
counter = Counter.get(req) # <-- Get your model/state from request or overide `get` for custom logic
return Main(
counter, # <-- auto add signals, sync and persistance
H1("🔢 Counter Demo"),
# Counter display
Card(
Div(
Span(data_text=Counter.count_signal, cls=TextT.primary + "text-7xl font-bold"),
cls="text-center mb-2"
),
Div("Total updates: ", Span(data_text=Counter.update_count_signal), cls=TextT.primary),
cls=CardT.default + "text-center my-6",
),
# Counter controls
Div(
Div(
Button("-10", data_on_click=Counter.decrement(10), cls=ButtonT.secondary),
Button("-1", data_on_click=Counter.decrement(1), cls=ButtonT.secondary),
Button("Reset", data_on_click=Counter.reset(), cls=ButtonT.secondary),
Button("+1", data_on_click=Counter.increment(1), cls=ButtonT.secondary),
Button("+10", data_on_click=Counter.increment(10), cls=ButtonT.secondary),
cls="text-center mb-6 flex gap-2 justify-center"
),
cls="mb-6"
),
# Custom increment
Div(
Form(
Input(name="amount", type="number", value="1", data_bind="$amount",cls="w-24"),
Button("+", type="submit", cls=ButtonT.secondary),
data_on_submit=Counter.increment(),
cls="mb-6"
),
cls="text-center mb-6"
),
cls="container mx-auto p-8 max-w-3xl"
)
states_rt.to_app(app) # <-- Import and add state routes
if __name__ == "__main__":
serve(reload=True, port=8080)
Your User
, Product
, Order
entities contain both data schema and business logic. No more scattering behavior across controllers, services, and frontend code.
The @event
decorator automatically creates HTTP endpoints and generates Datastar-compatible URLs. Your methods csn become interactive without routing setup. You can still add all parameters you tipically add to your @route
decorators and expect extraction of standard Starlette request
, session
, and other entities + new datastar
object that can be extracted from the request automatically with other signlas sent.
class Sale(BaseModel):
name: str
email: str
amount: int
class DashboardState(State):
sales: int = 0
subscriptions: int = 0
active_now: int = 0
total_revenue: int = 0
recent_sales: List[Sale] = []
@event
async def add_sales(self, amount: int = 0, name: str = "Unknown", email: str = "Unknown"):
self.sales += 1
self.total_revenue += amount
sale = Sale(name=name, email=email, amount=amount)
self.recent_sales.append(sale)
yield self.recent_sales_card()
yield self.sales_chart()
def recent_sales_card(self):
return Card(cls="col-span-3", id="recent-sales-card")(
Div(cls="space-y-8 px-4")(
*[
AvatarItem(n, e, d)
for (n, e, d) in (
*[(sale.name, sale.email, f"+${sale.amount}") for sale in self.recent_sales],
)
]
),
header=Div(
H3("Recent Sales"), P("You made 265 sales this month.", cls=TextPresets.muted_sm)
),
)
def sales_chart(self):
return Div(id="sales-chart")(
Apex_Chart(
chart_type=ChartT.area,
series=[
{"name": "2024", "data": [sale.amount for sale in self.recent_sales]},
],
categories=[sale.name for sale in self.recent_sales],
fill={"type": "gradient", "gradient": {"shadeIntensity": 1, "opacityFrom": 0.4, "opacityTo": 0.1}},
cls='max-w-md max-h-md',
)
),
@rt("/dashboard")
def dashboard(request):
state = DashboardState.get(request)
return Div(cls="space-y-4")(
...
Form(
Input(type="text", name="name", data_bind="$name", placeholder="Name"),
Input(type="email", name="email", data_bind="$email", placeholder="Email"),
Input(type="number", name="amount", data_bind="$amount", placeholder="Amount"),
Button("Add Sales", type="submit"),
data_on_submit=DashboardState.add_sales(),
)
...
)
Changes to your Python objects instantly update the frontend via Server-Sent Events. Two-way data binding works automatically. State
class comes with some automatic Datastar helpers that can be used in the front-end:
MyModel.myArg_signal
class atribute will return $myArg
or $MyModel.myArg
Datastar formated string based on the configured usage of namaspaceMyModel.myEvent(maybe, some, args)
will be converted to @get('/path/to/MyModel/endpoint')
also @post
, @put
and other will be used if specified in @event
methods
args.myModelInstance
has opinionated __ft__
function that deturns an empty Div
with all the Model signals, persistance options, and sync calls. Overide this method to set how your instance is shown in your FT
elements.No Redux stores, no API layer design, no frontend state management. Just define your entities and interact with them.
StarModel comes with 3 model storage options, 2 enabled by Datastar persistance in client session and cliend local storage, and one for backend server memory, but it's built to allow user to hook up any custom persistence options like Redis or Database. Just overide standard methods for get
, save
, delete
or extend with your custom logic.
from starmodel import StateStore
class UserProfile(State):
name: str = ""
preferences: dict = {}
# Choose your persistence layer
model_config = {
"store": StateStore.SERVER_MEMORY, # Server memory (default)
# "store": StateStore.CLIENT_SESSION, # Browser sessionStorage
# "store": StateStore.CLIENT_LOCAL, # Browser localStorage
# "store": StateStore.CUSTOM, # Custom logic
"persistence_backend": memory_persistence, # <-- Instance of your persistance manager class
}
You can try to combine this library with other FastHTML/Datastar/Pydantic helper libraries for even better dev experience. Here are some of them:
Run python app/main.py
to see examples of:
This project focuses on eliminating the complexity of modern web development by returning to entity-centric design patterns. Contributions welcome!
MIT License
FAQs
StarModel - Reactive State Management for FastHTML
We found that starmodel demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Socket uncovered 11 malicious Go packages using obfuscated loaders to fetch and execute second-stage payloads via C2 domains.
Security News
TC39 advances 11 JavaScript proposals, with two moving to Stage 4, bringing better math, binary APIs, and more features one step closer to the ECMAScript spec.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).