Getting Started
To show an example of how to get started with writing apps using Ludic Framework, here is a bit of an opinionated article where we create an almost ready to run web app.
First, let's say we create a small app with a file structure somewhat similar to this:
your_app
├── __init__.py
├── components
│ ├── __init__.py
│ ├── pages.py
│ └── ...
├── attrs.py
├── endpoints.py
├── models.py
├── server.py
└── ...
For bigger applications, this structure can look completely different. If you are just getting started, this is a good starting point.
Now we explain what each module represents.
pages.py
The idea is to write a couple of pages (regular components) rendering as a valid HTML document - using the <html>
root tag.
Here is a sample Page
component which represents the base for all other pages:
from typing import override
from ludic.html import head, title, style, body, html, main, script
from ludic.types import AnyChildren, Component, NoAttrs
class Page(Component[AnyChildren, NoAttrs]):
@override
def render(self) -> html:
return html(
head(
title("Ludic Example"),
style.load(cache=True),
),
body(
main(*self.children),
script(src="https://unpkg.com/htmx.org@1.9.10"),
),
)
Notice the script
element loading the htmx library.
You probably want to modify this page to include other stuff like fonts, favicon, and so on. Now you might create a Header
and Body
components and use it like this:
This would render as a valid HTML document.
server.py
The module instantiates the LudicApp
class and registers routes (by importing the endpoints module). You can also register error handlers. Here is what it can look like:
from ludic.web import LudicApp
from your_app.components import Body, Header
from your_app.components.pages import Page
app = LudicApp(debug=True)
@app.error_handler(404)
async def not_found():
return Page(
Header("Not Found"),
Body("Page you are looking for could not be found"),
)
from . import endpoints as _ # noqa
endpoints.py
Implement all your endpoints in this module:
from typing import Self
from ludic.html import b
from ludic.web import LudicApp, Endpoint
from ludic.web.exceptions import NotFoundError
from .attrs import ContactAttrs
from .models import Contact
from .components import Body, Header, Data, Form
from .components.pages import Page
app = LudicApp(debug=True)
@app.get("/")
async def homepage():
return Page(
Header(f"Welcome to {b("My App")}"),
Body("Lorem ipsum ..."),
)
@app.endpoint("/contacts/{id}")
class Contact(Endpoint[ContactAttrs]):
@classmethod
async def get(cls, id: str) -> Self:
contact = Contact.objects.get(id)
if contact is None:
raise NotFoundError("Contact not found")
return cls(**contact.as_dict())
@classmethod
async def delete(cls, id: str) -> None:
Contact.objects.delete(id)
return None
@override
def render(self) -> Data:
return Data(...)
@app.endpoint("/contacts/{id}/form/")
class ContactForm(Endpoint[ContactAttrs]):
@classmethod
async def get(cls, id: str) -> Self:
contact = Contact.objects.get(id)
if contact is None:
raise NotFoundError("Contact not found")
return cls(**contact.dict())
@override
def render(self) -> Form:
return Form(...)
You can create any components you like and combine them in the render()
method to produce an HTML representation of an entity.
attrs.py
This module can be used to define attributes for your endpoints. Note that it is probably better to keep regular component's attributes close to them, so this module serves for the definition of the endpoint's attribute only:
from typing import NotRequired
from ludic.types import Attrs
class ContactAttrs(Attrs):
id: str
name: str
is_active: NotRequired[bool]
What Next?
Now you are almost ready to run the up with uvicorn
:
In the next sections of this documentation, you can learn more about how to write components, and endpoints, and what kinds of other tools there are.