After years of crispy forms and HTML-in-Python hackery, I am firmly on the side that the presentation layer shouldn't be so tightly coupled to the code that renders it.
A template language (that isn't Python) is sufficient. Even better if you can render the templates from different languages.
I'll never be defining web app presentation in Python.
I think you might be colored by having used a bad system. It's like all those people who say ORMs are bad and evil, and somehow almost all of them have been forced to use Hibernate :P
ORMs are bad and I say that as a veteran of Hibernate, Entity Framework (almost every version, including code-first and db-first), E Bean, NHibernate and, worst of all by far, ActiveRecord.
Fast enough. You get enough tools to optimize the queries. Keep in mind that Django is still a web framework. You can go nuts with Django and it starts to feel like maybe raw SQL would have been easier but I've rarely encountered this.
In that case it doesn't matter if the ORM is "slow".
Anyway, I disagree. Sometimes the conversion of raw data from the db to Python objects can be the bottleneck for a view. Of course, almost always when this happens it's not really a problem per se as the view is plenty fast enough.
That's mostly false for a nontrivial fraction of queries in most bigger projects. If your queries are doing sane things (not regularly full table scanning for each customer, ...), little things like datetime conversions as the rows are instantiated in Python easily dwarf the cost of the database (depending on your networking setup and exact queries, perhaps just in throughput instead of latency, but Python will the still at least be the "cost" bottleneck for your app).
Yes!! I recently found a 12% overall latency win (_much_ higher in just the datetime code I changed) at $WORK replacing some of the native datetime conversions with something more intelligent, tailored to the specific <redacted work stuff> context at hand.
And yeah, you are just reinforcing the GP's point, because all of those are atrocious.
I am at the ORMs are bad club, but if you try any of the systems that encode the logic paradigm into their interface, they are a completely different kind of beast. (MS has brought some of it into C# with linq so you can use with the Entity Framework. It's not a fully logic system, and has so many attrition points with the Entity Framework that it's mostly not useful at all.)
I'm very familiar with Linq and Linq to SQL. It's as close as any ORM got to being "good enough" and was abandoned by Microsoft very early in its lifecycle. Linq in Entity Framework is a minefield of N+1's and excessive eager loading.
The N+1 problem is solved in iommi (again, I am one of the authors) by simply:
1. keeping a log of queries per request with stack traces
2. at the end, checking if there are N+1 problems
3. print a big warning with example SQL and stack trace to the console
During dev if I miss a prefetch_related/select_related the console will start to print gigantic stack traces telling me where the problem is. I don't have these problems in production because I get alerted to them in dev.
These are not only solvable problems but EASILY solvable problems!
(Also, in iommi tables, most of the times it can automatically figure out and do the prefetch/select related for you, further diminishing the problem)
If you represent your data as objects, you'll create a Book class with an attribute of type Author. If you now want to run the query above, you'd say (in c# likelihood, but not a real Entity query):
Database.Set<Book>().filter(author.id = 5).all()
But that instructs the ORM to fetch the author attribute from all books, and only then filter it by id. So, you will end up running the following query:
select * from book join author on book.author_id = author.id where author.id = 5
There is no reasonable way to represent the difference between this query and the one on top with an object representation. And even though both have exactly the same semantics, databases have been historically bad at optimizing them so the second one can be orders of magnitude slower.
EF Core compiles a query for the entirety of the expression provided. I wonder if it actually outputs select * from book join author on book.author_id = author.id where author.id = 5 as you described - this seems like a pit of failure a framework would work hard to prevent from happening.
You grab all Albums. Then you make a table of the albums with name and artist name. The artist name is in a separate table linked with a foreign key.
So the code might be:
for album in Album.objects.all():
print(album.name, album.artist.name)
Django will need to query from the foreign key ID (album.artist_id) into the Artist table to get the name of the artist. This means every loop step does a query.
So "N+1" because it's N queries (every album) plus 1 (the original "all albums" query).
The fix in Django is to do `Album.objects.all().select_related('artist')`. Then there will be just one query.
Thankfully, not an issue as of today. EF Core has client-side evaluation disabled by default. It was an enormous footgun in EF so to bring that behavior back you have to explicitly opt into it.
ORMs are bad at some things and good at others. Some people are stubborn and feel like if they’re using an ORM then everything has to go through the ORM, for example.
Not every web app needs to survive HN levels of traffic. Empowering the <insert profession here> that already knows Python to make an app to automate their team’s toil is a great thing.
Exactly. If your team is fully fluent in Python and has a need for a web UI, easier to adopt something like FastUI than try to quickly upskill in JavaScript for a one-off project.
Perhaps, but it is a false economy. Anyone writing a UI for the web, should be fluent in HTML/CSS and so on. In that case, other templating approaches make more sense.
I say this with absolute conviction; you're just deferring costs, technical debts with compounding interest. When you come to pay it off, it'll bankrupt you.
I'm not the least bit ashamed to say that I can't grok CSS today any better than I could when I first tried to learn it about 20 years ago. Whether or not alternative abstractions are "bad" or "wrong" or whatever is entirely irrelevant to me. The worst case scenario is that I end up finding them as painful as CSS.
Usually we've been prototyping with streamlit, but found that at times to be clunky. FastUI still has rough edges, but we made it work for our lightweight app. Note: we generally found it snappier than streamlit.
Can you share anything specific about what was clunky/ not snappy about streamlit? I've been exploring streamlit, and it took a little bit to figure out how to use it well, and deploy it appropriately.
That said, I’ve found it to be clunky in a few ways:
1. State management is not intuitive — anything that involves rendering complex state/reacting at it changes requires a ton of session state type stuff.
2. I’ve very often had to go into css/markdown hacks
3. The control flow of when it’s running/executing makes for clunky interactions
4. Some API decisions are painful. For instance, a block str comment in the file will render as st.write
5. Long running (or even not instant tasks) make for some annoying engineering challenges — the UI is tied to the compute, so you have to be smart about juggling it
6. Caching is not smart — it’s quite useful, but it’s easy to get lost in caching/state
I’ve been dreaming about streamlit but a little more state/function oriented. E.g. react-based. Not far from fastUI
These are both great tools for prototyping, but I’ve found that I’ll pull up tailwind + some UI framework when I want something slick and it’ll take a few times as long.
The first point you made is what myself and my team have struggled with. We have built simple apps for customers to upload a document to interact with and having the state be accurate to the document that the user uploaded was a major pain. Felt easier at that point to build something from scratch than to keep messing around with streamlit's session states
Would you consider giving Shiny (for Python) a try? https://shiny.posit.co/py/ It's (I hope) pretty close to Streamlit in ease of use for getting started, but reactive programming runs all the way through it. The kind of app you're talking about are extremely natural to write in Shiny, you don't have to keep track of state yourself at all.
If you decide to give it a try and have trouble, please email me (email in profile) or drop by the Discord (https://discord.gg/yMGCamUMnS).
Haven't used Shiny on Python yet, but we use the R version a lot. It's been a good experience: solid, reliable framework, good set of widgets, decent styling out the box.
The Python version is a port from its R stablemate (well, maybe more than a port: a re-engineering). If it's as solid as its R parent then it'll be a very useful addition to the Python UI landscape.
I guess building UIs in this case means building html forms in python, although the examples look more complicated then just building a web page and responding to http requests.
I don't know why someone would say "Build UIs" and not mention html, it seems pretty myopic to think a UI automatically means a web page.
Yeah I was originally wondering which toolkit it was using, and how it would compare against PyQt for example… but it seems a completely different thing.
This isn't a new concept, other examples include Solara (React-based) and NiceGUI (Vue-based). It's very practical for internal apps, if you accept that all controls have a small delay due to events being processed server-side.
Thanks for sharing! I wasn't aware of these frameworks.
As for the events being processed server-side, this is exactly where FastUI differs from any other framework I've stumbled so far - the widgets are sent to the client, and it knows how to render them, so just like any other js framework there's no roundtrip for every keystroke.
I'm currently writing an app with Django. It's such a breeze. Everything just works and I only have to think about the problem I want to solve. The documentation is as fantastic as it was 10-15 years ago.
I'm not even really fan of Python and I really miss static typing (renaming things is painful) but oh well, I'm so productive with it that I can live with this.
Honestly, I think it's the only framework that I've ever used (never tried RoR though) which allows total flexibility while never bothering you with technical thoughts.
It's still as shitty to deploy as it was a decade ago but now we have containers so who cares.
I'm writing this app during a pause in my career (mainly frontend) after a burnout and it's reigniting my love for writing web apps.
Django is truly amazing. It’s as you said - you don’t have to think about anything else other than the business problem. Authentication, session, databases, RBAC, everything is handled for you.
Unfortunately, if you want to use react, it usually means you’ll write APIs that are consumed by a browser-rendered app.
The holy grail for me would be a Django backend that renders react on the server. A Django nextjs.
Phoenix looks really cool too but I’d have to learn Elixir and while that’s something I want to do (already started some years ago), my main prerequisite for this project was not to struggle and going comfy. Building something out of my hand with no roadblocks was more important to me than to learn something new.
It's a very simple language. I've worked on half a dozen projects where I helped an experienced dev new to Elixir ramp up to 80%-90% productivity within a few days.
Learning Phoenix (and a few other libraries like Ecto and now LiveView) is what would eat up your time.
Phoenix used to be very easy for Rails/Django/Laravel devs to pick up a few years ago, but now that LiveView is thoroughly built-in and Phoenix.View has been replaced by Phoenix.Component, it's much less familiar to those learners.
I've been a Laravel guy mostly, but tried Flask for a project last year, and it was so incredibly easy to get up and running.
Still not really sure how the two compare, but there was just something about the extremely tried and battletested code. Nothing got in my way, the code told you what it did, it was fast etc.
Flask is the hard one to get up and running alternative, where you must look at every detail, that lets you create an application exactly as weird as you need.
The fact that you found it so simple coming from another framework speaks loudly about your previous one. But they do compare in that Django is the easy one to start where most choices are already done for you.
Actually, FastUI is repeating the same thing. It's cool but don't we get the same thing if we return HTML instead of JSON, combined with HTMX? We rely on the browser and battle-tested Django forms. No need to invent the wheel.
For me, the main technical difference with Pydantic over the host of other similar libs is runtime validation. Which, with modern typed Python, you often don't need and which you pay for in speed: even Pydantic v2 is still relatively slow.
"Pydantic models for UI components" seems odd to me. Surely runtime validation is unnecessary in this application and static analysis is a better fit. I am guessing it's the close integration with FastAPI that really holds the value here. For me, ideally this wouldn't use Pydantic but given the author and their situation, using Pydantic is probably the main reason this project actually exists.
This remindes me to java server faces, which was not a great experience in the long run. IMHO, trying to put all the subtleties of frontend development into server side abstractions would only work for a limited set of applications, such as admin UIs.
Yes that's exactly what it does, and it's possible some people don't know that because they haven't had to think about that stuff at work. But I do think the ergonomics of HTMX are better. I would argue that lowers the barrier to doing exactly what you want, which will lead to less over fetching.
I recieved a "secure patient portal message" in email from my doctor. Navigating to the actual message took 5 clicks, and each click involved waiting 3 to 12 seconds for pages to load, while all sorts of react-y things happened like hydration, spinning loading icons, placeholders. This is on gigabit internet with 50 ms ping. Shipping javascript to provide snappy UI has utterly failed, please let us go back to 1 round trip to the server for each client interaction.
1. It can save a lot of repetitive boilerplate code
2. You can keep validation rules in one spot.
I've been working with some toolkits like these after years of 'backend api, frontend vue/react/angular' and it definitely can save a lot of time. It's not always the best fit, but there isn't one approach to all applications that is the best fit. Everything has tradeoffs.
The ingenuity here is exactly not needing a roundtrip for every interaction.
You request a page -> fastapi sends the client a json defining the components -> the frontend knows how to render this json into react components that run locally just like any other js framework.
I have trouble finding the FastX project you mentioned. Can you tell me its website?
The top results I get are for the Fast and Furious movie. After specifying the context ("API", "web", "framework") I get hits for remote desktop software which also has API:
It’s amazing how many front end frameworks now exist that require running a backend server to even render basic HTML. This, remix, next, Astro, and so many others.
Do the features offered by these really warrant the complexity?
> It’s amazing how many front end frameworks now exist that require running a backend server to even render basic HTML. This, remix, next, Astro, and so many others.
---
> This
Yeah, this uses Python and is not specifically an SSG, so it makes sense for a server to be needed. Making an SSG that uses this would probably be possible, though.
> Remix
Remix is a fullstack app framework with a focus on data flow, directly going against the JAMStack current. Makes sense that it needs a backend server.
Astro started out as an SSG, and even now that server rendering is supported first-class, static site generation is the default and on-demand rendering needs to be opted into by the developer. https://docs.astro.build/en/basics/rendering-modes/#server-o...
Making initial page loads significantly faster, especially on intermittent mobile connections (which is an extremely common situation often underestimated by web developers) is worth a little bit of complexity, yes. And the entire point of these frameworks is to abstract away most of that complexity.
That's the elevator pitch, but as someone with a middle-tier Android phone and in an area with not that stable mobile connection, those supposely "optimized" pages work much worse than plain old HTML. It's even funny how my phone struggles to even render the home pages of these projects.
Developers always find ways to down-compensate gains due to Moore's law and its infrastructure equivalents.
Of course it's slower than plain HTML being delivered from the server. That's not what we're comparing to, and frankly if that approach fits the needs of your project then go ahead and use it.
The real comparison point is client rendered SPAs, which is the status quo in web app development that SSR frameworks are competing with. If that's your starting point, SSR is a strict optimization and a significant improvement for almost all usecases besides internal admin tools that are only meant to be used on desktop.
Not OP, but I think the point might be that we get served more and more SPAs weighting millions of lines of JS for anything and everything, because it's trendy in a resumé-driven industry, while overlooking the end-users needs and constraints
That's not a relevant or useful point at all. Obviously there will always be people using the wrong solutions for their problems. Doesn't mean that other people don't have perfectly valid problems that need a solution.
There's a big difference between serving up static documents (I wouldn't call this "rendering") and dynamic documents. Then there's also a difference between using a templating system for dynamism or a more programmatic and less portable page generation system that's anchored in a specific language like this.
Flet (https://flet.dev) might be easier for backend devs as it doesn't assume any web experience at all. You use controls (aka widgets) with events (not request/response) and can build not only web, but desktop and mobile apps - all with a single codebase. Disclaimer: I'm Flet developer.
It looks like Flet is for client-side code. It lets you write Flutter apps with Python instead of Dart.
> Simple Architecture - No more complex architecture with JavaScript frontend, REST API backend, database, cache, etc. With Flet you just write a monolith stateful app in Python only and get multi-user, realtime Single-Page Application (SPA). -- https://flet.dev
If I'm writing Python that runs on the mobile device, it must talk to a server to read & write data. Doesn't this still require an API backend, database, cache, etc?
For web you can package Flet app to client-side (with pyodide, all Python logic runs in the browser, see an example here: https://gallery.flet.dev/todo/) and run as a server-side app (or server-driven) with Python logic running on the server (example: https://flet-controls-gallery.fly.dev/layout - notice faster loading compared to client-side one).
> If I'm writing Python that runs on the mobile device, it must talk to a server to read & write data. Doesn't this still require an API backend, database, cache, etc?
That's correct. Any backend service which provides Python API can be used when running Flet app on a mobile: FastAPI, Firebase, Supabase, Pocketbase, etc, but you use Python to call that which is awesome especially for beginner and non-web developers.
I discovered Flet via HN a few months ago, and it has been really fun to play with (although it's a bit tricky to figure out the right path to make updates happen to nested controls).
Thanks for your work on it, and (shameless nudge!) I'm really looking forward to iOS camera access, and easy iOS deployment!
Unfortunately Flutter (which seems to be the backend for Flet), is IIRC rendered in a HTML5 canvas, which yields bad accessibility and is overall a pretty bad way to build web apps.
It's kind of unbalanced, as web tech, on the other hand, is pretty good at building desktop apps.
SEO - maybe, but IFAIK accessibility in Flutter web app shouldn't be an issue (I just saw this comment from someone on Flutter team: https://news.ycombinator.com/item?id=24922849). They've been recently doing a great job in terms of performance (compiling app to WebAssembly) and size. I agree that Flutter web is not good for building websites, but for web apps, especially those "internal" apps used by a small group of users, it's a pretty decent solution.
The XML+XSLT equivalent here would be an XSLT stylesheet with a bunch of predefined rich parametrized templates that you can just <xsl:call-template>. Hand-coding XSLT to produce HTML (or whatever) is still rather tedious.
Did this extensively in the mid-naughties. Huge site, lots of data and traffic.
Performance exceeded what was available at the time, scaled super well, but not something I’d ever go back to.
- xslt is not a programming language. As soon as you start using xslt:functions you’re doomed.
- it is not a fit for interactive UIs. It’s good for static content, trying to mix in js goes wrong very quickly.
My opinion is that these days infra is cheap enough that you get all the same benefits by doing standard templating in your language of choice from your data in json or whatever. Which I know is essentially the same thing without the specialist software and dsl. And that’s kind of the point.
Once you get to XSLT 2.0+, it is a programming language, just a very verbose one. I did a lot of XSLT/XQuery back in mid-00s, albeit not in web frontend context (we used it for code generation).
Unfortunately no browser has ever supported anything past XSLT 1.0.
Similar story for me. Conceptually I still like the idea, but not with XSL. JS operating on JSON as you say can do the same, but without all the needless pain. Though I'd still love a more declarative option - but I'll sacrifice that before I use XSL again.
I used to believe in the use case of projects like these but I think AI is making their case significantly weaker. The idea of "hey, you're a backend dev and can just quickly generate a UI in your own language" is quite a valid one, but these days you spend a a couple hours on ChatGPT and get a pretty decent UI out of it, without needing to really know frontend.
I'm far from being one of those people that's like all in on AI, but I've definitely been super impressed with its abilities to generate basic boilerplate code when you're working on project from scratch.
Most of the cost of a software project is from ongoing maintenance. When we make it easy to write the first version and make maintenance harder, the total cost of the project goes up.
Projects like FastUI allow maintenance of a project with fewer skills, directly reducing the cost of maintenance: owning teams spend less time learning and maintaining their skills and there are fewer bugs due to engineers making mistakes because of low skill-level in a technology.
Also, projects like FastUI make the project's tech stack much simpler. Simplicity helps reliability, incident resolution, security, testing, adding features, removing dead code, refactoring, etc. Highly-productive teams apply these ideas and keep their systems as simple as possible. As tools like FastUI mature, they will become standard in healthy engineering orgs.
I write my side projects in dart/flutter. Least friction, less hassle. If I needed to write a web app and flutter wouldn't cut it, I'd probably go htmx.
EDIT: my main issue is figuring out how to simply deploy a website.
Netlify is my goto for deployment, they make it super simple. Link your repo and set your production branch, build command, and directory to publish and whenever you commit it'll automatically deploy.
I'm just here to fullfill the prediction by someone, I don't remember who, that every time Netlify is mentioned in the future [this thread](https://news.ycombinator.com/item?id=39520776) is mentioned.
Maybe someone will even recommend Cloudflare Pages or a $5 VPS.
This seems to mainly be useful for spinning up quick and dirty internal tools.
But for that use-case, isn't it easier to use something visual and established like Retool (https://retool.com/) or that generates nice react code, like MUI Toolpad (https://mui.com/toolpad/)?
React code is basically the opposite of nice. Bad language and horrible ecosystem :D
Seriously, I first tried it in 2013, used it for work from 2014-2016 and here and there for odd projects later, and the amount of churn and complexity of the ecosystem has been surprising every time I check back on it.
My Obj-C from even longer ago pretty much all runs fine and is fairly understandable even for most iOS devs who started after Swift was the default.
Chiming in to not let you feel alone in this. React feels like you're doing low-level assembly in an esoteric functional language that ought to be simulated in javascript, but due to limitations of the latter you have to simulate it by hand, by following the manual. Looks absolutely delusional to everyone who catched peak ui phases like Cocoa and UIKit. I remember writing custom components in Obj-C and never thinking about all the nonexistent issues which React claims to be a perfect solution for every few years.
Once upon a time there was Google Web Toolkit (GWT), ASP.NET WebForms. What approach do they use, the same as FastUI? Now there is Blazor for C#, how does its approach differ from FastUI? I think they all promise to write UI for Web without writing JavaScript code.
It's essentially worse. The target audience is people like data scientists who are only familiar with Python, but it won't replace isomorphic javascript anytime soon or ever.
The stated goal/outcome seems pretty good. Glad to see more of this in Python in particular.
To those dissing it for production use case over a hand build frontend by a frontend dev: yeah, no shit. But not everything needs such work put in. This is perfect for internal tools, and there's plenty of indie hackers out there proving that you don't need an immaculate UI to get customers anyway. Use it for what it is good for.
We need more tools like this, because it cuts the development effort in half when it hits its stride. That's very valuable for internal tools especially, where you need something functional first and pretty second.
That being said, as a biased Laravel developer, I can highly recommend Filament both as an admin panel builder and declarative UI component kit like FastUI. It's Livewire based instead of React/Vue/etc and so firmly within the Laravel ecosystem, if that bothers you. https://filamentphp.com/
> where you need something functional first and pretty second.
Yet Filament manages to be fairly 'pretty' out of the box. I did a POC for a client project, and replicated about 10% of their current system in about a month, and got a fair amount of praise from folks at both the professional look/style/feel, yet... I did 0 on the styling, just used the defaults.
Yes, Filament has some rough edges when you try to color outside the lines, as with any toolkit. However, I don't think I've found anything else that comes remotely close to the combination of productivity and decent styling out of the box. Not just in PHP, but in any stack.
> If you're a frontend developer — you can concentrate on building magical components that are truly reusable, no copy-pasting components for each view.
Please tell me this isn’t common. It’s been a solved problem for at least 15 years now. At least since PHP had any form of prominence on the backend in the late 90s early 2000s. At a minimum going all the way back to knockout.js, handlebars, YUI and ember.
If this is common in 2024 I have lots of questions
Oh No. Is it that time again? Someone who isn't really a front-end developer reinventing front-end development.
I can't wait to be handed an application by my data scientists that uses this and is a complete trash fire that I need to rebuild from scratch... Again. I guess streamlit has competition.
It comes from a place of necessity, not want. In many product engineering teams, a solution doesn't exist until there is a UI experience to interact with it.
It allows data scientists to showcase the benefit of their model as a full experience.
I wouldn't use it for a large scale app. But tools like these are excellent for quickly mocking up a sample UI to sell an idea to your leadership. Too often leadership does not get the benefits of a ML/AI/DataScience driven feature if they can't see it inside something that resembles a UX.
Front end people don't get this pain, because their work has immediate visible impact. Backend work doesn't need to be sold because it naturally emerges out of the needs of supporting a certain number of users. ML work, lacks visibility just like backend but isn't as self evident as backend work.
This is probably the best summarization I've seen that describes this space. Even outside of ML, there's a real use case for building small, contained UIs for non-technical people to use in place of CLI tools.
The focus of these products should be 0 to 1 rather than 1 to Inf.
While the sentiment is relatable, I think it’s too harsh. Frontend devs would better thought why people ignore their mainstream and use simpler tools instead. For what’s worth, the scenario you described assumes they did something useful behind that trash fire and for some reason avoided dealing with a frontend team/guy. Explanation through pure incompetence fails to see and meet their needs.
I’m not a frontend dev and even I don’t understand why would you use this over vanilla html + css + JS. You don’t need to be a front end dev to do a basic UI like this.
> In the abstract, FastUI is like the opposite of GraphQL but with the same goal — GraphQL lets frontend developers extend an application without any new backend development; FastUI lets backend developers extend an application without any new frontend development.
I know a lot of backend developers who are backend developers specifically because they don't enjoy frontend work and prefer to leave that work to people who actually enjoy it. And I know a lot of frontend developers who feel the same way, only from the other side.
The tagline of FastUI is "Build Better UIs Faster," but I think this will likely end up being "Build Passable UIs Faster," because yes, for simple cases, you can represent HTML components as Python or Javascript or whatever other language you want, but once you need to build something complex on the frontend, it becomes very irritating very fast when you have to work like this -- and good luck getting your frontend buddies to help you, because now they have to get up to speed on a new framework to understand what it's doing.
But, unlike frontend devs, designers and product managers believe these complex things are needed, they almost never are and actually bring a lot of issues with them instead. I rarely see a complex component/combination (in a SaaS, let alone on some landing/public page) and think, "well, that adds real value over just a standard component". If you can point some out, please do; there are very obvious cases where something else than standard is needed, but that also depends on your definition of standard; if I can buy complex off the shelve somewhere, you are not building something complex in the frontend and so you can use simple means to hook them up without the complexity.
A lot of custom front end components that I’ve built were more about aesthetics than functionality, so I agree that it’s often unnecessary. In terms of functionality, you can get really far using only native HTML elements.
One common front end component that you can’t build with vanilla HTML is an autocomplete/typeahead input. I’ve had to build a few of these in JavaScript and I do think they’re genuinely helpful. It’s helpful to get suggested options as you’re typing. I also think search results that get filtered in real time as you type are helpful and can’t be accomplished with pure HTML.
Django admin is really extensible. I'm not seeing here how to offer multiple forms against the same model. Eg if a user can edit some of their profile but not the subscription plan field, while a sales team can.
I’m primarily a backend developer these days, because that’s just how it’s played out where I work. But we’ve made the choice to do most things with Typescript since it lets us share resources, libraries and cooperate better with code reuse/reviews and such. So I can fairly easily switch to working on our React frontend, what I can’t do, is to make quick frontends for the services which will sort of need an “excel” type frontend. We even have a fairly generic component for it, but it very rarely does the job because our business is… well demanding things they might not really need.
This looks like a good thing. In that regard. Maybe not for us, but definitely a very useful tool for all developers because it can potentially let you deliver business value rapid.
Which is what I read from the lines you quote. I see no backend vs frontend discussion here, I only see a useful tool.
Even if you want to eventually build it into your React frontend, a tool like this will offer immense value for internal testing by users who won’t be able to do it through something like Postman, which is basically 99% of the employees in non-tech enterprise.
My approach to what they are doing in my last job was to augment our models with more fine-grained information than what the database schema held, as well as with metadata about which views made sense or were needed, and then output schema information in our API. The raw base case was generated directly from the live database schema, so even without a line of code you had something once you'd enabled it (default off for anyone but people with devops level admin rights because giving a default on view of a raw table is a recipe for leaking data), and most of what the models provided were just more structured forms of what the models already needed, such as validations.
Then we built a set of generic frontend components for that in React once.
And we then had an instant UI for 50+ models, but the frontend devs could trivially selectively override both the whole page for models where the default was too simplistic, or how to render specific types of fields or for specific fields of specific models.
The "instant UI" was flexible enough that for most "backend" views we never customised it on the frontend. But being able to have the frontend devs customise the user visible things was essential, and thanks to having the ability to replace things bit by bit they could focus on the things that didn't look or work right, instead of "everything". And a lot of their work then made it back into augmenting the backend with info to let the generic frontend apply those improved views elsewhere as well.
I have only had a cursory look at FastUI. If they do something similar to that, then great. If they actually try to have the backend generate all or most of the UI and serve that up instead of serving up metadata, then I fully agree with your issues.
"The framework" in this case was a very thin layer underneath a bunch of components for which one of the inputs was a prop that included the relevant part of the schema from the server. So at a top level you got everything. The default top-level component knew to look up a view definition, and type, and a function that knew how to render that type, and that was pretty much it.
Then it'd just apply that recursively. For a simple model there'd only be two levels: The view, containing a bunch of columns, and the columns themselves, and the view type knew to iterate over the columns in the view and look up their type and tell them to render themselves. For more complex views frontend devs might e.g. provide a custom view type that'd group columns based on certain attributes, but still delegate to the generic mechanism to render those columns.
So replacing a part just meant writing a component that knew how to render itself from its props, just like any other component. Just that some of those props would be coming from the backend.
We'd generally try to avoid replacing components if the only reason for a component was to override how something was rendered, and to focus on why it should be rendered differently and consider if we could reflect that 'why' in the data, or other ways.
E.g. were certain fields grouped together because they were semantically related? Then reflect that with an attribute in the "meta schema" for those columns, and make the frontend component render related groups according to the type of that group (let's say a bunch of fields reflects a user address). Incidentally this tended to be very simple, as with a relational model when there were groups like that the right thing was often for them to be a separate table/model anyway, and we 'just' needed to distinguish between type handlers for rendering an object 'as the page' vs. 'as a reference' vs. 'as a group within the page', and that was easy enough to distinguish "well enough".
At a top level, we'd look for a registered view-level component (or fall back on the default), within a view if the backend returned the model instance "in-line" we rendered it as a group, and if it returned an id, we'd render it as a reference.
So a "User" object for example would render as a profile view if it was not your user and you went to a page where the associated API call returned the User as the top level model, you personalised profile page w/optional edit view if it was your user, or a "pill" linking to the user profile if it was just a reference.
The idea was that frontend components should focus on how to render specific types of data in a specific context, rather than specifying how specifically to render the page, because it'd make no sense to have the frontend e.g. try to render a field that no longer exists, or try to render a field as a different type it is when the meta data about which fields were available and what type they are was already available.
That's the way I've been doing things for a long time as a freelancer also.
Especially for business oriented apps, most of the models only need a list view with a table, and a detail view displaying some properties and related lists of models.
On accessibility: I see more custom components that nobody cares to make it properly working with keyboard interaction compared to native HTML input components.
On standards - mixed feelings: we had XHTML, now we don’t. We had IE and jQuery to deal with it, now we don’t need it, but I still see many places that have the old “works best with” message, now with Chrome instead.
On maintainable code: it’s a different shit show, we exchanged spaghetti code for billions of npm dependencies that break if you dare to try and upgrade something.
The ultimate RAD was Windows Forms, in my opinion. It's been downhill ever since.
Remember how you could just "add" a database connection to your project, plop a data source on a form, then a datagrid or a bunch of text/check/comboboxes and the standard control, wire it all up in a couple clicks, and things just magically worked?
That was a lovely time to work on any kind of line-of-business app.
> Remember how you could just "add" a database [...] and things just magically worked?
You can do these in both Delphi and Lazarus, except with a much better GUI framework (comparing GUI only here, not everything you'd get with .NET). If anything having worked with Windows Forms for a while i think it was abandoned before it reached even what you could do in the midlate 90s in Delphi.
Data source on the form idea originated from Borland Delphi. Later, Anders Hejlsberg quit Borland to work at Microsoft, where he implemented the idea of visual and non-visual components and property editors in .net
You would be even more impressed with Delphi, because after connecting your data source, your form fills with data at design-time. Lazarus and Typhon does the same.
C# 1.0 was, to a significant extent, Delphi with a Java-like syntax (which is no wonder given who designed it), and WinForms is so much like VCL, often the only difference is the presence or absence of the "T" prefix.
By now C# is a much more powerful language, though.
You can compile Qt to target the browser instead of native apps. But besides seeing a demo I don't know how good it is with performances, accessibility and so on.
Interestingly, you could accomplish a similar thing with GraphQL if the frontend uses the type introspection GraphQL provides and the backend graphql schema implements HATEOAS-like principles to let the frontend become a UI that's agnostic to different backends. That might not be how most GraphQL implementations are used, but it's kind of a cool pattern.
We didn't use GraphQL, but in my last company that was essentially what we did - every API call returned schema information about the parts of the model the current user had access to, with detailed type information and which validations to apply, and metadata that affected presentation, and the frontend built a UI from that.
We then let designers/frontend devs override that on a case by case basis instead of building from scratch, and where possible tried to roll that into the generic UI as components triggered by specific metadata from the backend. It was not entirely agnostic, but it was largely driven by metadata from the backend, so e.g. schema changes rarely needed much, if any, UI work unless they fundamentally changed how the page worked.
The types are no less protectable by authorization policies than the data, although authorization is hard to get right anyways, all else the same this architecture doesn't worsen it much--perhaps just less reverse engineering required to exploit vulnerabilities you already had.
In Django I had code in every view ensuring that the user was not accessing another user's data. If I create an abstraction like a utility function or "model manager" to handle this, I have to remember to call it from every view. In Graphene, this logic would be in a resolver which is used across multiple views.
If serving up metadata about what the site already makes accessible via the API will get their data stolen, then the actual problem is they're not applying access controls to the data, not the introspection.
This. In my experience frontend is not for everybody. There is a reason why FE and BE are often separated - because it makes sense in most non-trivial apps. Related, I never understood the appeal of isomorphic apps either. It's not like the code for UI and data access is very similar.
It looks to me like FastUI is literally the BFF pattern with a pre-supplied front-end. If that's true, then complaints about it should be viewed through the lens of who has control over the application, who is gaining control, and who is losing it.
I've had quite vocal arguments from front-end devs which, when we stripped back what they were actually complaining about, came down to the front end being in service of the back end, rather than vice versa as they were used to.
It seems the big selling point is building websites without JS.
To each their own but typescript is one of the best programming languages I’ve used. I do enjoy python a lot but whenever I use it I feel like I’m going a decade into the past, especially with their tooling (lint, types, formatters, package manager, venv, etc).
I love TypeScript, and I think React is mostly very elegant, and I still find front end work to be a real chore. Fucking around with forms and CSS and responsive breakpoints is not my idea of a good time!
You shouldn't need breakpoints 98% of the time. Use flex box and grid. Failing that, try container queries.
A really good form is still a PITA. I've been trying to perfect it for years but haven't found anything to sync frontend and backend validation that doesn't suck/involve a lot of boilerplate.
Well, for my latest iteration, I'm using Bun+Elysia on the server which uses Typebox for request validation+schema, which is effectively JSON Schema so you could do something similar in other languages. I can pass that schema down to the client/browser, and I could use AJV to run that schema against the request before I send it to the server, but there's a couple tricky parts.
1.Json schema/AJV return not very human friendly results and they're hard to associate with a specific field to display the error nicely
2. If you want to validation as the user is typing to clear errors as they're satisfied, you have to run the entire schema, not just for that one field
3. Any async validations like checking if a username is unique is harder if not impossible with this approach
No, hadn't heard of that. Looks like it's doing a bit too much. I'm not sold on ORMs or auto-genned scaffolding apps. The tRPC lib they're using OTOH looks interesting. Kind of. Looks like I'd have to give up my server lib/router for theirs, but then it looks like they botched the client-side error formatting which is the one thing I haven't solved: https://trpc.io/docs/server/error-formatting How is this good UX?
Formik (https://formik.org/docs/api/errormessage) does OK with the "touched" and "errors" objects, that helps you render out the error messages nicely, next to the inputs, but for my very first use-case I wanted to validate a File input and it seems to struggle with that a bit. Can't do it without suppressing some TS errors but maybe it's an edge case. And it of course doesn't handle the server half of the equation. So.. yeah.
This looks excellent, thank you. I'm iterating a lot of startups at the moment, and sort of home-growing a framework, but each new product I want to replace bits of it with off-the-shelf tooling, and this is an excellent selection of components.
I don't think the main problem backend developers have with frontend work is the language. As you say, Typescript is a really good language - in many ways better than Python. I don't think it's even the tooling, although becoming basically competent with two very large tooling stacks is not a trivial matter either.
I think there's a fundamental verbosity to the way most SPAs do data management - I've seen it said elsewhere that when you build a SPA, you basically end up having to reinvent the database, by which it is meant that an inordinate amount of your code is dedicated to extracting data from the backend via an API and then keeping track of that state using mountains of front end code.
At some point, you have to decide - is this really two separate applications (a backend and a frontend), or is it actually just one? If you don't have a need for multiple different clients, and you already have a backend written in Python (or some other backend language, e.g. Elixir, Kotlin), then maybe you can get away with writing a whole lot less code if you can find a framework that fills in that whole in-between layer for you and lets you stick with the language you already have.
Ah! A real criticism of FE development, I agree with your problem statement.
When you jump into the world of single-page applications, things get complex pretty quickly, because the use case for needing an SPA pushes the web app into a full desktop application.
Ultimately, for a highly interactive and dynamic "desktop-class" user experience, there is added complexity. I think that's why so much movement within the FE world has moved away from "SPA for everything" and into these mixed dynamic apps. Islands, React Server Components, NextJS, they all help create a middleground between a document-based website with no dynamic elements with a full blown desktop app experience. They all have real tradeoffs, in particular adding an entirely new backend service to serve the front end.
For many projects, react + react-query is probably enough.
Having said that, my argument from https://bower.sh/dogma-of-restful-api still stands: when you build an API that is RESTful (1:1 mapping between endpoint and entity) you are unknowingly pushing the complexity of data synchronization to the FE, which requires a well thought out ETL pipeline.
This probably doesn't help my case but I've been building a simplified middle-layer for react to bridge the gap between react-query and full blown SPA: https://starfx.bower.sh
It is all an illusion. Unicode, ASCII, base 10 numbers, are all lies. Our CPUs know binary registers of certain sizes, ints and floats, and nothing else.
Everything else is a lie that we make work by throwing a lot electricity at a lot of fancy sand and out the other end comes something that looks reasonable.
That said, TS has a really nice type system that is absurdly flexible and at times quite fun to use.
I say the phrase "TypeScript crimes" a lot, and it is almost always a positive.
Most programmers I work with and have worked with are not wielding the type system in the same way, and that's usually okay--because I tend to be the one building the libraries they then use, and the compiler keeps folks from doing things with my code that I didn't plan for.
I have some bad news for you about the types in Python--which, for the record, I really like and have made me willing to write Python!--if your objection is that the types are "just an illusion". Much like Pydantic, good use of TypeScript types involves shape validation on input.
(I have worse news for you about C or C++ or anything that isn't typed exclusively based on how wide its register is, but that's nitpicking.)
Honestly, if I'm going to nitpick myself, even that isn't a true statement. You might have an integer/float divide for your registers, like amd64 does, but even past that it's squishy and becomes human bookkeeping or compiler magic. Is %rax an 8-byte integer, or is it a pointer? Or is it garbage because you used %eax (the lower 4 bytes of %rax) for storing a 4-byte integer, and the top half is whatever(tm)? Ditto again for %ax (2-byte) or %al (1-byte). You have widths, but me calling them types was really even a stretch there.
And then everything else is just built from that, really. Strings/arrays? Either length+bytes/charpoints (hope you counted correctly) or "bytes/charpoints until you hit a NUL" (hope you put the NUL in or now your buffers are spewing somewhere they shouldn't). Structs? Pile of bytes, each of them special-cased by the compiler. (A polymorphic class would then have some virtual function tables somewhere too.) And if your language has a fixed-point decimal class, it's time to get weird.
Types are a really convenient, really helpful concept, but to sniff at them being compiler-only in something like TypeScript or typed Python is IMO misguided--because compilers have gotten pretty smart and it allows us to reasonably make flexibility tradeoffs because those compilers are good at doing things. Java will scream at runtime if I cast an object to something it can't be, and that's great, but also, I just won't do that if I validated my inputs in Python with something like Pydantic or TypeScript with something like Zod or Typebox or whatever, because an illegal action with a type becomes a compiler error. So, IMO, let's get over the argument and get back to making stuff. (Even the Golang people. All is forgiven, mostly.)
The problem with Python specifically is that the type system is such a massive bolted-on hack. It has to be, because it's such a dynamic language that all declarations and imports are executable statements - and it's hard to reconcile it with type declarations, which often need to be mutually recursive.
It is kind of hacky for sure, but I've gotten way more productive with it than I've ever been with Python before. Like I went my entire career (and time before that) where the words that came out of my mouth after "Python" were "is bullshit", but this year due to both AI jank and other unrelated tools having no path but Python, I've made my peace with it--and yeah it's spotty, though with Pylance I think it's fine, and it saves me from so many category errors that make old-Python infuriating.
Unfortunately for me, as an unreconstructed Ruby dork, Sorbet does not scratch the same itch. :(
Hate to break it to you but that's how all languages work. And if they don't they are just wasting CPU cycles checking types anywhere but the edges of the app.
Well FastAPI was originally based on pydantic 1.x, which was extremely slow (so slow in fact that they removed the benchmarks from the website).
I am the author of typedload, a similar library written in pure python.
Mostly for fun I started to see how much I could improve performances, benchmarking against other libraries.
I was very surprised to find out that pydantic2, despite the rewrite in rust, wasn't overwhelmingly faster… in fact it is still slower than my pure python library in some cases.
But, I guess having "fast" in the name sounds good!
I am certainly not in love with how the FastAPI team handles comparisons or development. Hiding superior benchmarks. Immediately closing Github issues (they refile as Discussions to keep the issue count low). So on and so forth.
I suspect that part of the problem is that they got VC money to do the rewrite in rust, and it took very long and the results aren't as amazing as expected.
But that's just my own conspiracy. I have no idea. Perhaps things changed since I last ran the benchmarks.
Benchmarking isn't easy. I know that my software using my library mostly loads Unions, so that's a focus in my benchmarks. However I have absolutely no idea of what the other users are doing with it, and my benchmarks might not be relevant to them.
In the end I wrote them for myself mostly, to check for possible regressions in performances.
The Pydantic project has a long history of misrepresenting their benchmarks, and ignoring the existence of much faster alternatives. I'm not at all surprised that their supposedly super-fast rewrite isn't really as fast as they made it out to be.
You’re under the wrong impression, I’m a network engineer who happens to _use_ FastAPI for some projects. The library linked I made at work, but open sourced and wrote docs for in my own time. The other libraries I’ve written are all in my spare time.
My comment is my own opinion, and a quick search for your name and pydantic backs it up. It’s just boring to read the same thing, every time.
I’m assuming “Fast” here is the same as in “FastAPI”, meaning fast for developers comfortable with python to build with and not fast in the sense of performance.
A template language (that isn't Python) is sufficient. Even better if you can render the templates from different languages.
I'll never be defining web app presentation in Python.