Frontend technologies that pair well with a Haskell backend

Hey everyone,

I am building a software business in the field of renewable energy systems. The backend is all Haskell of course.

One component of the business is a customer-facing interactive website. Problem is, I have never worked on web frontends. There are many options:

and no doubt there are more choices.

I want to be mindful of my complexity budget. Introducing a new language adds complexity, but the Haskell-based frameworks have their own challenges (e.g. usage of Obelisk is tied to Nix).

What are your experiences working with frontend technologies with a Haskell backend?

8 Likes

I basically always use elm

These days, elm-land is new and pretty decent at managing standard elm complexity gripes.

I’ve used it for my recent haskell-based project and had a good time.

Definitely recommend.

6 Likes

Unpopular opinion: Your backend just needs to support a format/protocol that your frontend understands.

I don’t believe in this idea of sharing the same language across the whole stack because you get the serialization/deserialization step for free.

I actually think it negatively impacts architecture, because your boundaries become weaker… just like how many projects these days generate openAPI spec e.g. through servant. Instead, you should design and write down the spec manually and then write tests that both your backend and frontend adhere to it, no other way around.

As such, it doesn’t really matter what the frontend language is. Any frontend language can interact with your Haskell backend.

If you’re not very passionate about frontend yourself, it probably makes sense to pick a widely used language so you can have someone else do it/outsource it. That would be TypeScript then, imo.

11 Likes

That’s a good suggestion. I like your idea of creating a solid boundary between frontend and backend.

I’m surprised at the dislike for openAPI specs. My initial idea was that, since the problem domain is new and evolving rapidly, generating an openAPI spec from the backend implementation is a good way to ensure that the frontend is compliant

1 Like

Not openAPI spec in its own, but generating the spec. It’s mudding the boundaries.

Of course you can go through the generated spec and say “yeah, that’s exactly how I imagined it”, but I just don’t think this is the right way process-wise. It might not really matter if your frontend is the only consumer of the backend API anyway, but starting with a spec and then verifying that your implementations adhere to it… is also how it works in formal methods. And then you have a language agnostic foundation.

1 Like

We’ve achieved significant success by using TypeScript with React and Relay on the frontend, coupled with a GraphQL server—also written in TypeScript. This setup communicates with various Haskell services. A major advantage of this architecture is its flexibility, allowing us to use other languages in the backend for use cases where Haskell may not be the best fit.

1 Like

I guess it depends on the level of interactivity you need. I find HTMX model compelling (I recommend the book too) and very easy to use with Haskell (there is even lucid2-htmx: Use htmx in your lucid templates). No new language required. But I’ve never used any other framework you mention except JQuery.

9 Likes

+1

If your frontend does not have a lot of interactivity, then just showing data without using any framework could be the best.

I think all the frameworks have a varied level of learning curves. As the UIs get complicated, they are harder to implement and modify, and the frameworks provide different ways to handle that. In my experience the APIs / protocol is usually not where you spend a significant amount of time. But as you said you are getting started, so using simpler and popular choices (like react) is recommended.

As I have a years of experience with reflex-dom / Obelisk, I will tend to use it even if I don’t have a backend app. Cos in my case it isn’t about about getting the backend and frontend to talk, the decision is more with the style of programming I am comfortable with.

1 Like

I have built some Haskell software (also other languages) for a renewable energy business. We might want to have a private chat.

1 Like

On a tangent, what is the tradeoff between using OpenAPI versus a custom API reference? I wrote Haskell integrations with a dozen different servers and every single time it’s either a custom website or, sometimes, a text document. Never had an issue with that.

This may be the same as the whole REST vs GraphQL discussion, where half the people seem to advocate for GraphQL with an expectation that a different format means the backend magically starts consuming data in a more optimal way (even though these features need to be implemented by backend developers).

1 Like

FWIW, I also think a hasura-style graphql api is basically the best idea for consuming data in the frontend; depending on a few details.

The standard argument for graphql over REST, all things being equal, is that in fact all things aren’t equal, and graphql just offers much more flexibility for arbitrary requests than rest does; especially if your entire graphql api is autogenerated (and, at least if it’s hasura v2, it’s done so in a very nice way.)

as to preferring to use an autogenerated OpenAPI thing from your api; i think i have some sympathy for @hasufell 's views about doing it by hand, i would just say why not both; it’s fairly unagruably fine to have a spec generated from the concrete api you have, and then hand-craft some key workflows that people might like to use.

I still have nightmares about a previous workplace, where the ratio of microservices to employees was 10:1. Every microservice’s interface was unspecified; I had to read the source code every time I had to do something. Bringing up OpenAPI (or equivalent, like a graphQL schema) is a reactionary mechanism at this point

2 Likes

I know of an org that uses Svelte on the frontend with Haskell on the backend. Svelte is also pretty easy to learn — may be worth checking out!

2 Likes

I think graphQL shines in situations where you need to slice your data in many different ways. Without knowing the scale of OP’s operations, it’s hard suggest a blanket style for interface design.

But the original question was rather around interactivity.

I did enjoy Purescript for a while but you still end up paying the javascript tax (e.g. when integrating widgets) as your application scales (besides, an SPA might not be what you need after all).

For my recent projects (data labeling, domain-specific search engines) I enjoy HTMX very much and even ended up writing a little extension for interactive scientific plots.

2 Likes

Tell me about it, same thing happened to me! Having 10 OpenAPI specs to read may help a bit, but I would prefer organizations to rethink whether microservice is the right fit for them, instead of blindly buying into the hype.

But to answer your question, the best solution will depend on the complexity/flashiness of your UI and whether you want to implement the web UI components yourself.

Here is my experience with various ways to implement the web frontend:

  • Plain JavaScript/Typescript:
    Easy access to vast ecosystem of UI components. But sometimes it’s hard to find a set of components that have similar level of quality.
  • JavaScript/Typescript frameworks:
    Easy access to quality, premade UI components for the big players like React, Vue, and Angular, but adds complexity to the project, and there will be times that you have to fight against the whole browser/DOM behavior if you have something that is complex/specific enough.
  • Haskell and Haskell-like frontend frameworks:
    Extremely ergonomic to stay in the Haskell functional paradigm, but you lose access to the vast ecosystem of UI components made by the community. So you will have to be willing to implement the UI components yourself, this may require a fair amount of DOM behavior knowledge.

Generally, the more you rely on premade UI components, the further up the list you go. On the other hand, if your UI components are more custom and rather unique, then you might as well move closer to the Haskell stack (unless it is out-of-scope of the framework, then you fall back on JavaScript).

I usually don’t find myself using made-from-scratch UI components, but I also don’t like the add complexity of frameworks like Angular and React, so I find myself gravitating towards the more lightweight AlpineJS and combining that with TailwindCSS.

Another thing to think about is do you need to maintain user session state?
And a related question is are you okay with state and business logic leaking out on the frontend? Which can easily happen if you use frameworks/libraries like Angular and React, as they tend to encourage that by being so featureful.

If you’d rather track user session state on the server-side, then you can simplify the frontend by using more lightweight solutions.

As a general rule of thumb for managing application states, if your requirements allows for this, it’s much simpler and robust to manage state in either the server or client side, and not both. So for stateful frontends, I have a stateless backend; and for stateful backends, I use very simple frontends to display whatever the server sends.

On the spectrum of server-side and client-side web applications, I’ve been from the server-side, to full JavaScript-based frontend applications that only use backends for database operations, and now I am back leaning on server-side again (unless I need the distributed properties of a client-side app). I feel many hyped “modern” properties of a JavaScript-based clients can be replicated on a server-side solution with just a sprinkle of JavaScript and, like others have mentioned, HTMX. And it’s much easier to debug when your backend is a statically typed language like Haskell.

I hope this is helpful and I have not confused you further, as sometimes the way web technologies work is rather convoluted.

3 Likes

Web apps are distributed systems that exist in various browsers and platforms and device shapes and as such it is very hard, never ending work to make something reasonably good. There’s a lot of mess and ugliness. Anyone who suggests otherwise is either inexperienced or selling you something.

If you have never worked on web frontends, you probably aren’t able to judge what would be most suitable for you.

Given that your only stipulation is a small complexity budget, I would:

  • Avoid anything that involves a build system (npm, elm, typescript, etc)
  • Avoid SPA frameworks like React etc. that push you to make RPC calls to your server and GraphQL over-engineered gubbins
  • Use a simple, accessible, no-frills design patterns like those of the UK gov Get started – GOV.UK Design System Read this when you find yourself considering some fancy pants component that you think will move the needle on your business bottom line (probs won’t)
  • Keep OWASP in mind as your security spirit guide https://owasp.org/
  • Store state in your database in tables, instead of keeping things in the frontend
  • Stick to old school HTML links and forms and CSS and use MDC for information MDN Web Docs

You can always write some JavaScript, or use Htmx, or $framework, when you have more experience and know the trade-offs better. But you can 100% avoid making a decision now. Your core competency is Haskell and the domain, time spent agonizing over the right framework or whatever is a waste of your talents.

18 Likes

I’m working on creating an online server for playing boardgames online. The backend is Haskell+Servant and the frontend is Purescript+Halogen. I highly recommend this setup:

  • Purescript feels like Haskell, you just need to remember a couple of things that are different between languages (like []Array, (,) → Tuple, …) but everything else feels like home.
  • Halogen is a superbly crafted web frontend for SPAs. Have a look at Real World Halogen to get an idea Real World Halogen [Draft]

My 2 cents to the discussion.

7 Likes

This is quite interesting as a project and I wish you all the best in this project!

2 Likes

This is spot on. I have built web apps using the full spectrum of front end technologies and I always regret using SPA frameworks. It’s just unnecessary for most web applications.

If your design doesn’t look like Miro, Notion or Google Docs, embrace the way web browsers are designed to work. Serve html/css from the backend and use the minimum amount of JS you need to meet design interactivity requirements. Tailwindcss will speed up your delivery of good looking components. HTMX or alpinejs will make life manageable when you need state driven interactivity over something like plain jquery, that gets painful fast.

The major upside of this is your developer and ops experience will be much better.

4 Likes

I have been productive generating html with lucid2, serving css generated by bulma and recently also using htmx to make things more interactive while needed; at the backend I use a RawM to serve the css/ other assets, my routes are servant-lucid + a newtype wrapper to be able to serve lucid2 Html ().

This is my most recent project using that tech: mangoiv/modern-hoogle - Forgejo: Beyond coding. We Forge. , hosted at https://hoogle.mangoiv.com, so this is where you can have a look at stuff :slight_smile:

11 Likes