Building the Same App Using Various Web Frameworks
Eugene Yan сравнивает четыре веб-фреймворка — FastAPI, FastHTML, Next.js и SvelteKit — построив на каждом из них одно и то же CRUD-приложение для загрузки, просмотра, редактирования и удаления данных CSV. FastAPI и FastHTML позволяют обойтись минимумом файлов (Jeremy Howard продемонстрировал приложение на FastHTML всего в 50 строках кода), тогда как Next.js и SvelteKit требуют заметно больше кода, но предлагают более зрелую экосистему и встроенные инструменты вроде Tailwind CSS. Автор также затрагивает важный вопрос: LLM-ассистенты для кода лучше справляются с популярными фреймворками (React, Next.js), что может усиливать их доминирование и усложнять внедрение новых инструментов вроде FastHTML и Svelte. Создатели этих фреймворков уже начали публиковать специальную документацию, оптимизированную для LLM (llms.txt, llms-ctx.txt), чтобы решить проблему холодного старта.
Building the Same App Using Various Web Frameworks
Создание одного и того же приложения на разных веб-фреймворках
[ learning engineering python 🛠 ] · 8 мин. чтения
Recently, I’ve been wondering if I should migrate from my current web app stack (FastAPI, HTML, CSS, and a sprinkle of JavaScript) to a modern web framework. I was particularly interested in FastHTML, Next.js, and Svelte.
В последнее время я задумывался, не стоит ли мне мигрировать с текущего стека для веб-приложений (FastAPI, HTML, CSS и немного JavaScript) на современный веб-фреймворк. Особенно меня интересовали FastHTML, Next.js и Svelte.
FastHTML: Многие начали создавать на нём проекты с тех пор, как Jeremy Howard запустил его месяц назад. Его цель — дать возможность создавать современные веб-приложения на чистом Python. Next.js: Я встречал несколько приложений, построенных на нём, например cal.com и roomGPT. У него обширная экосистема, и он популярен для создания продакшен-приложений. SvelteKit: Этот легковесный фреймворк завоевал популярность среди разработчиков (Stack Overflow, TSH, State of JS) и моего друга Swyx (Why I Enjoy Svelte) за последние несколько лет.
To learn more about these frameworks, I built the same web app using each of them. The app, which I’m calling “Look at Your Data”, allows users to:
Чтобы лучше разобраться в этих фреймворках, я создал одно и то же веб-приложение на каждом из них. Приложение, которое я назвал «Look at Your Data», позволяет пользователям:
Upload a CSV file to initialize an SQLite database tableView the table in the web browserUpdate individual fields in the tableDelete individual rows in the tableDownload the updated table data as a new CSV fileЗагрузить CSV-файл для инициализации таблицы в базе данных SQLite Просмотреть таблицу в браузере Обновить отдельные поля в таблице Удалить отдельные строки из таблицы Скачать обновлённые данные таблицы в виде нового CSV-файла
By implementing these CRUD (Create, Read, Update, Delete) operations in each framework, I hope to get a sense of each framework’s unique features and the associated developer experience. To keep things simple, I’ll use SQLite as the database. As a baseline, I’ll start by building the app with what I’m familiar with—FastAPI.
Реализовав эти операции CRUD (Create, Read, Update, Delete) в каждом фреймворке, я надеюсь получить представление об уникальных особенностях каждого из них и соответствующем опыте разработки. Для простоты в качестве базы данных я буду использовать SQLite. В качестве отправной точки я начну с того, что мне хорошо знакомо — FastAPI.
FastAPI + Jinja + HTML + CSS + JavaScript
FastAPI + Jinja + HTML + CSS + JavaScript
Building the app with FastAPI is fairly straightforward (code). The key components are:
Создание приложения на FastAPI — достаточно простая задача (код). Ключевые компоненты:
main.py: Routes for uploading/downloading data, updating fields, deleting rowsindex.html: The HTML document that defines the scripts, table, and buttons.style.css: Visual styling such as column widths, word wrap, scrolling.script.js: Client-side functionality for uploading the CSV, loading data for display, updating/deleting rows, and downloading the updated data as CSV.main.py: Маршруты для загрузки/скачивания данных, обновления полей, удаления строк index.html: HTML-документ, определяющий скрипты, таблицу и кнопки. style.css: Визуальное оформление: ширина столбцов, перенос слов, прокрутка. script.js: Клиентская логика для загрузки CSV, отображения данных, обновления/удаления строк и скачивания обновлённых данных в формате CSV.
Here’s what the web app looks like. While it’s not much to look at aesthetically, it meets our requirements above. I’ve deliberately kept visual styling to a minimum (for the current and later apps) to keep the focus on the frameworks and functionality instead of design.
Вот как выглядит веб-приложение. Хотя с эстетической точки зрения оно не впечатляет, оно соответствует нашим требованиям, перечисленным выше. Я намеренно свёл визуальное оформление к минимуму (как для текущего, так и для последующих приложений), чтобы сосредоточиться на фреймворках и функциональности, а не на дизайне.
FastHTML
FastHTML
To learn FastHTML, I started by consulting the docs and building a simple ToDo app via this walkthrough. For help with unfamiliar components, I relied on Cursor by providing links to relevant docs such as ft components, htmx, pico.css as context. With FastHTML, I could implement the entire app within a single main.py and a small style.css (code).
Для изучения FastHTML я начал с документации и создания простого ToDo-приложения по этому руководству. Для помощи с незнакомыми компонентами я использовал Cursor, подключая в качестве контекста ссылки на соответствующую документацию: ft-компоненты, htmx, pico.css. С FastHTML я смог реализовать всё приложение в одном файле main.py и небольшом style.css (код).
Here’s how the app looks.
Вот как выглядит приложение.
After my first iteration above, Hamel graciously offered to pair-program with me to build the app from scratch. He also invited Jeremy Howard—the creator of FastHTML himself—to join us. They taught me several tricks, such as providing Cursor with LLM-friendly documentation for FastHTML (llms-ctx.txt) and FastLite (html.md). They also shared a great resource on building simpler apps with htmx and Hyperview. Jeremy even took the time to demonstrate how to build the app in just 50 lines of code!
После первой итерации Hamel любезно предложил совместное парное программирование, чтобы собрать приложение с нуля. Он также пригласил Jeremy Howard — создателя FastHTML — присоединиться к нам. Они показали мне несколько приёмов, например, как предоставить Cursor LLM-оптимизированную документацию для FastHTML (llms-ctx.txt) и FastLite (html.md). Они также поделились отличным ресурсом по созданию более простых приложений с htmx и Hyperview. Jeremy даже нашёл время продемонстрировать, как собрать приложение всего в 50 строках кода!
from fasthtml.common import *
db = database(':memory:')
tbl = None
hdrs = (Style('''
button,input { margin: 0 1rem; }
[role="group"] { border: 1px solid #ccc; }
'''), )
app, rt = fast_app(live=True, hdrs=hdrs)
@rt("/")
async def get():
return Titled("CSV Uploader",
Group(
Input(type="file", name="csv_file", accept=".csv"),
Button("Upload", hx_post="/upload", hx_target="#results",
hx_encoding="multipart/form-data", hx_include='previous input'),
A('Download', href='/download', type="button")
),
Div(id="results"))
def render_row(row):
vals = [Td(Input(value=v, name=k)) for k,v in row.items()]
vals.append(Td(Group(Button('delete', hx_get=remove.rt(id=row['id'])),
Button('update', hx_post='/update', hx_include="closest tr"))))
return Tr(*vals, hx_target='closest tr', hx_swap='outerHTML')
@rt
async def download():
csv_data = [",".join(map(str, tbl.columns_dict))]
csv_data += [",".join(map(str, row.values())) for row in tbl()]
headers = {'Content-Disposition': 'attachment; filename="data.csv"'}
return Response("\n".join(csv_data), media_type="text/csv", headers=headers)
@rt('/update')
def post(d:dict): return render_row(tbl.update(d))
@rt
def remove(id:int): tbl.delete(id)
@rt("/upload")
async def post(csv_file: UploadFile):
global tbl
if not csv_file.filename.endswith('.csv'): return "Please upload a CSV file"
tbl = db.import_file('test', await csv_file.read(), pk='id')
header = Tr(*map(Th, tbl.columns_dict))
vals = [render_row(row) for row in tbl()]
return Table(Thead(header), Tbody(*vals))
serve()
from fasthtml.common import * db = database(':memory:') tbl = None hdrs = (Style(''' button,input { margin: 0 1rem; } [role="group"] { border: 1px solid #ccc; } '''), ) app, rt = fast_app(live=True, hdrs=hdrs) @rt("/") async def get(): return Titled("CSV Uploader", Group( Input(type="file", name="csv_file", accept=".csv"), Button("Upload", hx_post="/upload", hx_target="#results", hx_encoding="multipart/form-data", hx_include='previous input'), A('Download', href='/download', type="button") ), Div(id="results")) def render_row(row): vals = [Td(Input(value=v, name=k)) for k,v in row.items()] vals.append(Td(Group(Button('delete', hx_get=remove.rt(id=row['id'])), Button('update', hx_post='/update', hx_include="closest tr")))) return Tr(*vals, hx_target='closest tr', hx_swap='outerHTML') @rt async def download(): csv_data = [",".join(map(str, tbl.columns_dict))] csv_data += [",".join(map(str, row.values())) for row in tbl()] headers = {'Content-Disposition': 'attachment; filename="data.csv"'} return Response("\n".join(csv_data), media_type="text/csv", headers=headers) @rt('/update') def post(d:dict): return render_row(tbl.update(d)) @rt def remove(id:int): tbl.delete(id) @rt("/upload") async def post(csv_file: UploadFile): global tbl if not csv_file.filename.endswith('.csv'): return "Please upload a CSV file" tbl = db.import_file('test', await csv_file.read(), pk='id') header = Tr(*map(Th, tbl.columns_dict)) vals = [render_row(row) for row in tbl()] return Table(Thead(header), Tbody(*vals)) serve()
And here’s how Jeremy’s app looks:
А вот как выглядит приложение Jeremy:
Next.JS
Next.JS
To learn Next.js, I did the React Foundations and Next.js tutorials. The latter teaches the basics of Next.js through bite-sized, hands-on lessons that build up to a dashboard app. With 16 chapters in the Next.js tutorial, learning and coding along can take some time. Nonetheless, I recommend persisting till at least Chapter 12 on transforming data (in the Next.js tutorial), and enjoyed the gentle learning curve and practical projects.
Для изучения Next.js я прошёл туториалы React Foundations и Next.js. Второй обучает основам Next.js через компактные практические уроки, из которых шаг за шагом складывается приложение-дашборд. С 16 главами в туториале Next.js на изучение и написание кода может уйти немало времени. Тем не менее, рекомендую дойти как минимум до 12-й главы о трансформации данных (в туториале Next.js) — мне понравилась плавная кривая обучения и практические проекты.
Here’s how I created the Next.js app template:
Вот как я создал шаблон приложения на Next.js:
npx create-next-app@latest
npx create-next-app@latest
Building the same app in Next.js requires considerably more code than the Python versions (code). Nonetheless, I found its organization intuitive:
Создание того же приложения на Next.js требует значительно больше кода по сравнению с Python-версиями (код). Тем не менее, его организация показалась мне интуитивной:
api: Routes for the data table (GET, PUT, DELETE) and file upload/downloadpages.tsx and layout.tsx: Page-specific and common user interface componentscomponents: Reusable React components like table and upload/download buttonslib: Utility functions; in this case, there was a single function for SQLiteapi: Маршруты для таблицы данных (GET, PUT, DELETE) и загрузки/скачивания файлов pages.tsx и layout.tsx: Компоненты пользовательского интерфейса для конкретной страницы и общие компоненты components: Переиспользуемые React-компоненты, такие как таблица и кнопки загрузки/скачивания lib: Утилитные функции; в данном случае — единственная функция для SQLite
And here’s how the web app looks. The built-in Tailwind CSS integration makes the app look a bit more polished compared to the barebones FastAPI and FastHTML apps.
А вот как выглядит веб-приложение. Встроенная интеграция с Tailwind CSS придаёт приложению чуть более отполированный вид по сравнению с аскетичными версиями на FastAPI и FastHTML.
SvelteKit
SvelteKit
To learn Svelte, I went through part of their tutorial that comes with an online interpreter. The tutorial has four parts: (i) Basic Svelte, (ii) Advanced Svelte, (iii) Basic SvelteKit, and (iv) Advanced SvelteKit. I completed the sections on basic Svelte and basic SvelteKit and jumped into building the app (code).
Для изучения Svelte я частично прошёл их туториал, включающий онлайн-интерпретатор. Туториал состоит из четырёх частей: (i) Основы Svelte, (ii) Продвинутый Svelte, (iii) Основы SvelteKit и (iv) Продвинутый SvelteKit. Я прошёл разделы по основам Svelte и основам SvelteKit и приступил к созданию приложения (код).
To create the SvelteKit app template, I ran the following:
Для создания шаблона приложения SvelteKit я выполнил следующее:
npm create svelte@latest my-app
npm create svelte@latest my-app
Like Next.js, the template for SvelteKit has several directories and moving parts:
Как и у Next.js, шаблон SvelteKit содержит несколько каталогов и составных частей:
components: Reusable Svelte components such as data table and upload buttonsapi.ts and db.ts: Functions for the API to fetch, update, and delete data (api.ts) as well as query and run updates on the SQLite database (db.ts)routes: Routes for table (GET), rows (PUT, DELETE), and upload/download+page.svelte: Main page of the applicationapp.html: Entry point and main HTML filecomponents: Переиспользуемые Svelte-компоненты, такие как таблица данных и кнопки загрузки api.ts и db.ts: Функции API для получения, обновления и удаления данных (api.ts), а также запросы и обновления базы данных SQLite (db.ts) routes: Маршруты для таблицы (GET), строк (PUT, DELETE) и загрузки/скачивания +page.svelte: Главная страница приложения app.html: Точка входа и основной HTML-файл
Here’s how the app looks. One slight deviation: I played with combining the “choose file” and “upload” functionality into a single button, thus removing the “Upload CSV” button.
Вот как выглядит приложение. Небольшое отклонение: я поэкспериментировал с объединением функций «выбрать файл» и «загрузить» в одну кнопку, убрав тем самым отдельную кнопку «Upload CSV».
FastAPI + Svelte
FastAPI + Svelte
I also took a stab at building an app with FastAPI as the backend and Svelte for the frontend (code). All functionality and APIs resided in main.py while frontend UI and API interactions were handled by +page.svelte and api.ts respectively. To run the app, I had to start both the FastAPI server and the Svelte development server.
Я также попробовал создать приложение с FastAPI в качестве бэкенда и Svelte для фронтенда (код). Вся функциональность и API располагались в main.py, а пользовательский интерфейс и взаимодействие с API обрабатывались файлами +page.svelte и api.ts соответственно. Для запуска приложения нужно было поднять оба сервера — FastAPI и Svelte.
And here’s what the web app looks like. (I reverted the upload functionality to match the original FastAPI app that had a separate “Upload CSV” button.)
А вот как выглядит веб-приложение. (Я вернул функцию загрузки к исходному варианту FastAPI-приложения с отдельной кнопкой «Upload CSV».)
The main challenge here was coordinating communication between both servers during development. In a production setting, the Svelte app would be compiled and served statically with API requests sent to the FastAPI backend.
Основная сложность заключалась в координации взаимодействия между двумя серверами в процессе разработки. В продакшен-среде приложение Svelte компилировалось бы и отдавалось статически, а API-запросы направлялись бы к бэкенду FastAPI.
• • •
• • •
Aside: How will coding assistants influence builders?
Отступление: как ассистенты для написания кода повлияют на разработчиков?
This exercise got me thinking about how coding assistants—powered by LLMs trained on internet data—could influence the choices we make as builders. For example, would LLM-based coding assistants be as effective with niche or newer frameworks such as Svelte and FastHTML? While the tweet below may be an exaggeration, it raises a valid concern.
Это упражнение заставило меня задуматься о том, как ассистенты для написания кода — основанные на LLM, обученных на данных из интернета — могут влиять на выбор, который мы делаем как разработчики. Например, будут ли LLM-ассистенты столь же эффективны с нишевыми или новыми фреймворками, такими как Svelte и FastHTML? Хотя приведённый ниже твит может быть преувеличением, он поднимает обоснованную проблему.
It brings me no pleasure to say this, but Svelte is dead because LLM base models are better at writing React. — Jess Martin
Мне не доставляет удовольствия это говорить, но Svelte мёртв, потому что базовые LLM-модели лучше пишут на React. — Jess Martin
Given React and Next’s wider use and longer history, it’s likely most LLMs are trained on more React and Next code than Svelte code. Ditto for FastHTML. This could lead to coding assistants being more effective when working with and suggesting code for established frameworks such as FastAPI, React, and Next.js.
Учитывая более широкое распространение и более долгую историю React и Next, скорее всего большинство LLM обучены на большем количестве кода React и Next, чем кода Svelte. То же касается и FastHTML. Это может приводить к тому, что ассистенты для написания кода будут эффективнее работать с устоявшимися фреймворками, такими как FastAPI, React и Next.js, и предлагать для них более качественный код.
As an anecdote, I had an easier time using Cursor + Claude to build the app in FastAPI and Next.js, and a harder time with FastHTML and SvelteKit. Since FastHTML is barely a couple weeks old (at the time of writing), its code and docs likely hasn’t made its way into the training data of most LLMs yet, explaining their limited proficiency with FastHTML.
Из личного опыта: мне было проще использовать Cursor + Claude для создания приложения на FastAPI и Next.js и сложнее — с FastHTML и SvelteKit. Поскольку FastHTML существует всего пару недель (на момент написания), его код и документация, вероятно, ещё не попали в обучающие данные большинства LLM, что объясняет их ограниченное владение FastHTML.
To address this issue, Jeremy Howard (creator of FastHTML) has made the effort to provide llms.txt and llms-ctx.txt that have been optimized for in-context learning. Similarly, Rich Harris (who works on Svelte at Vercel) plans to publish more LLM-friendly documentation for Svelte. Victor Dibia has also written about how coding assistants may affect developer habits and choices, and how we need to write docs for both humans and machines.
Чтобы решить эту проблему, Jeremy Howard (создатель FastHTML) подготовил файлы llms.txt и llms-ctx.txt, оптимизированные для обучения в контексте. Аналогично, Rich Harris (работающий над Svelte в Vercel) планирует публиковать более дружественную для LLM документацию по Svelte. Victor Dibia также писал о том, как ассистенты для написания кода могут влиять на привычки и предпочтения разработчиков и почему необходимо создавать документацию как для людей, так и для машин.
Time will tell how effective these efforts are in addressing the cold-start problem for newer or more niche frameworks in coding assistants.
Время покажет, насколько эффективны эти усилия в решении проблемы холодного старта для новых или нишевых фреймворков в ассистентах для написания кода.
• • •
• • •
This was a fun exercise to gain familiarity with FastHTML, Next.js, and SvelteKit. All the code can be found here. (I’m a beginner in frontend so please forgive any bad practices!) Personally, I’m looking forward to building more with TypeScript, which I haven’t used extensively since building ApplyingML.com years ago.
Это было увлекательное упражнение для знакомства с FastHTML, Next.js и SvelteKit. Весь код можно найти здесь. (Я новичок во фронтенде, так что простите за возможные ошибки!) Лично я с нетерпением жду возможности больше работать с TypeScript, который я активно не использовал с тех пор, как создавал ApplyingML.com несколько лет назад.
What resources have you found useful in learning how to build with Next.js or Svelte? Please comment below or dm me!
Какие ресурсы оказались вам полезны при изучении Next.js или Svelte? Пишите в комментариях или в личные сообщения!
If you found this useful, please cite this write-up as:
Если этот материал оказался вам полезен, пожалуйста, цитируйте его следующим образом:
Yan, Ziyou. (Sep 2024). Building the Same App Using Various Web Frameworks. eugeneyan.com. https://eugeneyan.com/writing/web-frameworks/.
Yan, Ziyou. (Sep 2024). Building the Same App Using Various Web Frameworks. eugeneyan.com. https://eugeneyan.com/writing/web-frameworks/.
or
или
@article{yan2024frameworks,
title = {Building the Same App Using Various Web Frameworks},
author = {Yan, Ziyou},
journal = {eugeneyan.com},
year = {2024},
month = {Sep},
url = {https://eugeneyan.com/writing/web-frameworks/}
}
@article{yan2024frameworks, title = {Building the Same App Using Various Web Frameworks}, author = {Yan, Ziyou}, journal = {eugeneyan.com}, year = {2024}, month = {Sep}, url = {https://eugeneyan.com/writing/web-frameworks/} }
Join 11,800+ readers getting updates on machine learning, RecSys, LLMs, and engineering.
Присоединяйтесь к 11 800+ читателям, получающим обновления о машинном обучении, рекомендательных системах, LLM и инженерии.