Framework
cutelyst/cutelyst avatar
cutelyst/cutelyst

cutelyst: a Qt web framework with a Catalyst shape

A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.

978 stars121 forksC++BSD-3-Clause

At a glance

What is it?
cutelyst is a C++ web framework built on Qt, taking the dispatch and controller model of the Perl Catalyst framework and implementing it with Qt's meta-object system. It ships pluggable server engines, a plugin architecture that covers sessions, authentication, CSRF and authorisation, and coroutine support in C++23.
Who is it for?
cutelyst suits a C++ team that already lives in Qt and wants a web layer with real structure, in particular anyone who needs a native desktop client and a server sharing one codebase and one toolchain.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Catalyst's model, rebuilt on Qt

The pitch is a single sentence: a web framework built on top of Qt, using the simple and elegant approach of the Catalyst Perl framework.

That inheritance is specific and worth unpacking, because it determines the shape of everything in the framework. Catalyst dispatches a request to a controller action, the action is a method, and the framework inspects the action's signature to work out what to pass it. A Perl controller declares attributes and the framework fills them in from the request, the route and the body.

Cutelyst takes that pattern and implements it with Qt's meta-object system. The readme says so directly: the meta-object system is what powers the core, and it allows the framework to introspect a controller's method signatures and generate matching actions that can be invoked later.

That is a clever solution to a problem that is not unique to Perl. In a dynamically typed language you can inspect a function at runtime and call it with whatever arguments its metadata says it wants. In C++ you normally cannot. Qt's meta-object system gives you exactly that, through the introspection data the compiler generates for signals, slots and properties, which is why a C++ framework built on it can offer a Catalyst-like dispatch model at all.

The practical appeal is for teams that already use Qt. If your desktop client and your server are in the same language and the same build system, a framework that shares them is worth a look on those grounds alone, before any question of performance.

Three server engines and two dispatchers

The engine layer is where a web framework either earns its keep or does not, and cutelyst separates it cleanly from the application layer.

There are pluggable engines, with the built-in one being a cross-platform and fast server engine. Within it, the protocol support is listed as three families. HTTP/1.1 with pipelining and keep-alive. HTTP/2 with upgrade to H2, ALPN negotiation on HTTPS, and direct H2C. And FastCGI with pipelining and keep-alive.

That list tells you the deployment options. A direct connection to the engine suits a single process serving a dedicated machine. FastCGI puts the framework behind a separate web server such as nginx, which is how you would deploy it in a shared hosting environment, and the uwsgi topic in the project metadata suggests that path is a real one. HTTP/2 with ALPN and H2C covers both TLS-terminating and plain deployments, and the fact that the HTTP/2 support is in the engine rather than in a plugin is a sign that it is a first class path rather than an afterthought.

Above the engine sits the dispatcher, and there are two of them, a chained one and a path one. A path dispatcher matches the URL path to a controller and action, which is the familiar REST shape. A chained dispatcher passes the request down a chain of handlers, which is how you build a pipeline where each stage can inspect or rewrite the request before the controller sees it. Having both means the same controller can be reached through different routing strategies, which is what makes the framework usable for both a JSON API and a set of HTML pages without duplicating the application logic.

The plugin list is the feature list

Cutelyst ships a long list of plugins, and reading it tells you what a team would otherwise have to build.

The core plugins cover session, authentication with PBKDF2, authorisation with RoleACL, status messages, a validator for user input, CSRF protection, memcached, and user agent handling. The authentication plugin using PBKDF2 for password hashing and the separate RoleACL plugin for authorisation are the two that most often get built badly in-house, and having them as separate plugins means you can use the first without the second, or replace the second with your own model.

The view plugins are separate projects. Cutelee is a Qt implementation of the Django template engine, JSON is a view plugin, and Email is a view plugin for sending mail. REST support comes from an action class called ActionREST, and asynchronous SQL comes from a companion project called ASql.

Beyond that there are WebSockets, an upload parser, JSON bodies parsed into a JSON document when the uploaded data is in that format, chunked responses through the QIODevice write API, request profiling and statistics, and QtCreator integration.

The plugin architecture is what makes the framework extensible without forking it, and the fact that the heavyweight pieces like the template engine and the async database layer are separate repositories means the core stays small and the optional parts can evolve on their own schedule.

The asynchronous warning is the most important line

One feature line in the readme is phrased as a warning, and it deserves to be read twice.

Asynchronous processing is supported, with the parenthetical instruction: just don't use local QEventLoops or it will eventually crash.

That is the kind of sentence that only exists because someone learned the hard way, and it points at the real difficulty of combining Qt's event loop with a request handling model. Qt's event loop is re-entrant in ways that are comfortable for a desktop application and awkward for a server, and a local event loop spun up inside a request handler can deadlock or crash the process in ways that are hard to diagnose.

The framework's answer is to move towards coroutines. The requirement is C++23 with a compiler of g++-14 or newer, and there is a CoroContext class with coroutines support that replaces the need for the previous asynchronous resource management class. In other words, the guidance for writing asynchronous handlers is to suspend the handler rather than to spin a nested loop.

For a team writing an application framework on top of cutelyst, this is the single most important piece of advice in the documentation, because getting it wrong produces a crash that happens under load rather than a compile error. The request profiling and statistics feature is the practical companion to it, since a handler that hangs in a local loop is exactly the thing you want to see in a profile.

Requirements, stability and what is no longer maintained

The requirements are short and strict. The build system is CMake, at version 3.28 or newer, and the core library is Qt at version 6.4 or newer. The compiler requirement is C++23 with g++-14 or newer, which is a high floor and follows from the coroutine support.

Those three constraints together define the eligible audience. A team on an older distribution Qt cannot use it, and a team with a corporate toolchain pinned to an older compiler cannot use it either, and there is no way to work around the Qt floor because the meta-object system is the foundation rather than an optional part.

The stability policy is stated as a single line. The API and ABI are stable for the v5 tags, and older vX.X.X branches are unmaintained.

That is a candid statement and a useful one. A stable ABI in a C++ framework matters, because an application links against it, and an unannounced change to a virtual table or a class layout is a source level break that a source compatible release would not otherwise reveal. Tagging v5 as ABI stable means the project is telling you that the v5 line will not break your build in that way, and it is also telling you that if you are on an older vX.X.X branch, there is no path forward other than migrating.

The release history is consistent with that policy, with 5.0.0 in July 2025, 5.0.1 in August 2025 and 5.1.0 in May 2026, so the v5 line has been taking minor releases steadily since the major version landed.

Repository layout, benchmarks and community

The tree shows a mature C++ project rather than a single library directory.

bash
Cutelyst/
cmd/
dox/
examples/
tests/
3rdparty/
EventLoopEPoll/

The core is in the Cutelyst directory, with command line tools, generated documentation sources, examples, tests, vendored third party code, and a separate EventLoopEPoll directory, which is a strong hint at how the event loop is handled on Linux, with an epoll based implementation kept apart from the rest. There is also a qtcreator directory for the IDE integration, a clang-format and clang-tidy configuration with its own checks and settings files, CMake presets, a pre-commit configuration, and a Messages.sh script for translating release notes.

The examples directory is worth a look when you evaluate the framework, and it holds two cases, a hello world and an embedded widgets example. The second one tells you something important: the framework is meant to be embedded in an application, not only run as a standalone server, which is consistent with the Qt heritage and with the idea of sharing a codebase between a client and a server.

On performance, the readme takes the honest route and points elsewhere. It says not to trust the project on being fast and to check the HttpArena results, the successor to the TechEmpower framework benchmarks, with a link to the entry source for cutelyst, and it notes that the historical TechEmpower round 23 results remain available. Pointing at an independent benchmark rather than publishing your own numbers is the right call.

The project is BSD-3-Clause licensed, the documentation is a tutorial in the wiki plus an API reference generated by Doxygen, and the community channel is a mailing list.

Editorial conclusion

cutelyst suits a C++ team that already lives in Qt and wants a web layer with real structure, in particular anyone who needs a native desktop client and a server sharing one codebase and one toolchain. It is a poor fit for a team that wants the wider Qt ecosystem, since the template engine and the asynchronous database layer are separate companion projects rather than parts of the framework, and it is a poor fit if you need a maintained long-term support branch, since older version branches are explicitly unmaintained. Before you build on it, check that your Qt version and compiler meet the stated floors, and read the asynchronous processing guidance, because mixing local event loops with the framework's coroutine model is documented as unsafe.

Frequently asked questions

What are the requirements for building cutelyst?

The build system is CMake 3.28 or newer, the core library is Qt 6.4 or newer, and the compiler requirement is C++23 with g++-14 or newer, which follows from the coroutine support in the asynchronous API.

How does cutelyst dispatch requests to controllers?

Through Qt's meta-object system, which lets the framework introspect a controller's method signatures and generate matching actions that can be invoked later. That is what allows the framework to use the Catalyst style of dispatch and controller method, in C++.

Which server protocols does cutelyst support?

The built-in Cutelyst server engine covers HTTP/1.1 with pipelining and keep-alive, HTTP/2 with upgrade to H2, ALPN negotiation on HTTPS and direct H2C, and FastCGI with pipelining and keep-alive. Engines are pluggable.

Is there anything to be careful about when writing asynchronous handlers?

Yes. The readme warns against using local QEventLoops, because that will eventually crash. The framework's own guidance is to use the coroutine support in CoroContext, which replaces the earlier asynchronous resource management class.

Official sources

  1. cutelyst/cutelyst on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cutelyst-cutelyst.svg)](https://hysenlabs.com/projects/cutelyst-cutelyst)

Was this useful?