Tutorial
Welcome to the tutorial! We'll be building a simple ToDo application. We expect it to take around 10 minutes if you're following along.
Setup
Before using this tutorial, we assume that you have already read the Installation Guide and have installed Rye and cookiecutter.
Create a new project using cookiecutter, as follows:
cookiecutter https://github.com/Chaoyingz/cookiecutter-flect
# follow prompts
cd <your new project directory>
# install dependencies
rye sync
# run dev server
make dev
You should be able to visit the URL printed in the terminal:
INFO: Will watch for changes in these directories: ['/your/project/src']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [68051] using StatReload
INFO: Started server process [68053]
INFO: Waiting for application startup.
INFO: Application startup complete.
Then, open your browser and visit http://127.0.0.1:8000
. You will see the following interface:
Modify Layout
A webpage is usually composed of a header, main, and footer. In the same website, most pages have the same header and footer, so there is no need to repeat these in every page. In flect, we can define a layout function, and all its subpages will automatically reuse this layout.
Edit src/todo/app/layout.py
, as follows:
from flect import PageResponse
from flect import components as c
async def layout(outlet: c.AnyComponent = c.Outlet()) -> PageResponse:
return PageResponse(
body=c.Container(
tag="div",
children=[
c.Container(
tag="header",
class_name="h-14 border-b text-sm flex items-center px-6",
children=[
c.Link(
href="/",
children=[
c.Text(
text="Todo",
class_name="font-medium text-xl",
)
],
),
]
),
c.Container(
tag="main",
class_name="p-12",
children=[outlet]
),
c.Container(
tag="footer",
class_name="h-14 border-t text-sm flex items-center px-6",
children=[
c.Text(
text="Made with ❤️ by flect",
)
],
)
],
)
)
In the above code, we need to pay attention to a few details:
- The layout is defined in the layout method in layout.py
- The layout function must accept an
outlet
parameter, which will be the subpage - Each c.Container class will be rendered into the corresponding React component.
- The function must return a page response
After modification, the page will automatically reload. If the modification is correct, you will see following:
You can see that the header and footer are reused in the home page.
Add ToDo Form
You may have noticed that after editing the layout, two headers appear on the page. This is because we have defined an additional header in the page. Next, we will write a ToDo form.
Edit the src/todo/app/page.py
file and add the following code:
from pydantic import BaseModel
from flect import PageResponse
from flect import components as c
from flect import form
class TodoInCreate(BaseModel):
name: str = form.Input(placeholder="Enter task name...", max_length=16)
todos = {}
async def page() -> PageResponse:
return PageResponse(
body=c.Container(
tag="section",
children=[
c.Form(
model=TodoInCreate,
submit_url="/",
class_name="mb-5 border p-5",
)
],
),
)
In the above code, we have defined a ToDo creation form using a pydantic model. flect will automatically render this form. You will see the following page:
flect will parse the form validation items and add front-end validation.
Handle Form
In the previous step, we defined a form. The form's submit_url provides the form submission address. Now we need to handle the submitted form data and save the data. In flect, you can define other methods under the current URL by creating a route.py
file in the folder.
Edit the src/todo/app/route.py
file and add the following code:
from flect.actions import Notify
from flect.response import ActionResponse
from todo.app.page import TodoInCreate
async def post(form: TodoInCreate) -> ActionResponse:
return ActionResponse(
action=Notify(title="success", style="success", description=f"todo {form.name} created."),
)
In the above code, we created a post interface to handle the add ToDo request and return a notification.
You will see the following page:
Add Storage
We have defined a ToDo form, now we need to save the form data to the database.
For simplicity, we will use a json file to save the ToDo data.
Edit the src/todo/storage.py
file and add the following code:
import json
from pathlib import Path
class Storage:
JSON_URI = Path(__file__).parent / "todos.json"
def __init__(self) -> None:
if not self.JSON_URI.exists():
self.JSON_URI.touch(exist_ok=True)
def insert(self, item: dict) -> None:
with self.JSON_URI.open("a") as f:
f.write(f"{json.dumps(item)}\n")
def list(self) -> list[dict]:
with self.JSON_URI.open("r") as f:
return [json.loads(line) for line in f]
storage = Storage()
Link Storage with Page
Display existing ToDos on the page:
Edit the src/todo/app/page.py
file and add the following code:
import uuid
from pydantic import BaseModel, Field
from flect import PageResponse
from flect import components as c
from flect import form
from todo.storage import storage
class TodoInCreate(BaseModel):
name: str = form.Input(placeholder="Enter task name...", max_length=16)
class TodoInDB(TodoInCreate):
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
async def page() -> PageResponse:
todos = [TodoInDB(**todo) for todo in storage.list()]
return PageResponse(
body=c.Container(
tag="section",
children=[
c.Form(
model=TodoInCreate,
submit_url="/",
class_name="mb-5 border p-5",
),
c.Table(
datasets=todos,
)
],
),
)
Edit the src/todo/app/route.py
file and add the following code:
from flect.actions import Notify
from flect.response import ActionResponse
from todo.app.page import TodoInCreate, TodoInDB
from todo.storage import storage
async def post(form: TodoInCreate) -> ActionResponse:
storage.insert(TodoInDB(**form.dict()).model_dump())
return ActionResponse(
action=Notify(title="success", style="success", description=f"todo {form.name} created."),
)
Now our ToDo application should work properly.
Conclusion
Congratulations on completing this tutorial! You now have a working ToDo application built with the power of flect. As you continue to explore flect, remember that it can be used to build a wide variety of applications, not just ToDo lists.
In fact, this very documentation was written using flect. You can refer to the flect Documentation Repository on GitHub to see how it was done.
Additionally, applications written with flect can be directly deployed using Vercel, providing a seamless development to deployment experience. Happy coding!