Vix.cpp v2.7.0 is here Read the blog
Skip to content

App Bootstrap

The backend template keeps main.cpp small and moves the startup sequence into AppBootstrap. This file is the place where the backend is assembled before it starts listening for requests.

AppBootstrap does not own every feature in the backend. Its job is to create the application, load runtime configuration, configure shared runtime paths, register middleware, register base routes, connect generated modules, and start the server. That makes the backend startup path visible without turning the entry point into a large file.

txt
src/main.cpp
  -> AppBootstrap
      -> configuration
      -> vix::App
      -> public files and views
      -> middleware
      -> routes
      -> generated modules
      -> app.run(...)

Entry point

The generated main.cpp delegates directly to the bootstrap.

cpp
#include <api/app/AppBootstrap.hpp>

int main()
{
  api::app::AppBootstrap bootstrap;
  return bootstrap.run();
}

This keeps the process entry point stable. The main function starts the backend, but it does not need to know how middleware, routes, static files, views, or modules are wired.

Bootstrap declaration

The bootstrap class is declared under the project include directory.

txt
include/api/app/AppBootstrap.hpp

The generated class exposes one main function.

cpp
namespace api::app
{
  class AppBootstrap
  {
  public:
    AppBootstrap() = default;
    ~AppBootstrap() = default;

    AppBootstrap(const AppBootstrap &) = delete;
    AppBootstrap &operator=(const AppBootstrap &) = delete;
    AppBootstrap(AppBootstrap &&) = delete;
    AppBootstrap &operator=(AppBootstrap &&) = delete;

    int run();
  };
}

The class is intentionally small. It is not a service container and it is not a general application framework. It owns the startup sequence of this backend process.

Startup implementation

The implementation lives in:

txt
src/api/app/AppBootstrap.cpp

The generated implementation includes the backend registries and the generated app module bridge.

cpp
#include <api/app/AppBootstrap.hpp>
#include <api/presentation/middleware/MiddlewareRegistry.hpp>
#include <api/presentation/routes/RouteRegistry.hpp>

#include <vix_app_modules.hpp>

#include <vix.hpp>
#include <vix/log.hpp>

The important include is:

cpp
#include <vix_app_modules.hpp>

That header is generated by Vix when the project uses app modules. It gives the backend one stable generated entry point for enabled modules.

Configuration

The bootstrap starts by loading runtime configuration from .env.

cpp
vix::config::Config cfg{".env"};
vix::App app;

This keeps runtime values outside the compiled binary. The generated .env.example documents the expected variables, and the local .env file provides the values used by the application during development or deployment.

A normal local workflow starts by copying the example file.

bash
cp .env.example .env

The backend then reads values such as the server port, public directory, templates directory, static cache settings, compression settings, storage path, database defaults, and production diagnostics.

Public files and views

The generated bootstrap reads public file and template settings from configuration.

cpp
const std::string viewsPath = cfg.getString("templates.path", "views");
const std::string publicPath = cfg.getString("public.path", "public");
const std::string publicMount = cfg.getString("public.mount", "/");
const std::string publicIndex = cfg.getString("public.index", "index.html");
const std::string publicCacheControl =
    cfg.getString("public.cache_control", "public, max-age=3600");
const bool publicSpaFallback =
    cfg.getBool("public.spa_fallback", false);

Then it configures the template directory and static files.

cpp
app.templates(viewsPath);

app.static_dir(
    publicPath,
    publicMount,
    publicIndex,
    true,
    publicCacheControl,
    true,
    publicSpaFallback);

This matters because the backend runs from the build output. Runtime directories such as public/, views/, and storage/ are declared as resources in vix.app, so they can be copied beside the built executable.

ini
resources = [
  ".env=.env",
  "public=public",
  "views=views",
  "storage=storage",
]

Static compression

The generated backend can enable compression for static responses through configuration.

cpp
const bool publicCompression = cfg.getBool("public.compression", false);
const int publicCompressionMinSize =
    cfg.getInt("public.compression_min_size", 1024);

When enabled, the bootstrap creates compression options, registers compression middleware, and installs the static response hook.

cpp
const auto compressionOptions =
    vix::middleware::performance::CompressionOptions{
        .min_size = static_cast<std::size_t>(publicCompressionMinSize),
        .add_vary = true,
        .enabled = true,
    };

auto compressionMiddleware = vix::middleware::app::adapt_ctx(
    vix::middleware::performance::compression(compressionOptions));

app.use(std::move(compressionMiddleware));

vix::App::set_static_response_hook(
    vix::middleware::performance::compressed_static_response_hook(
        compressionOptions));

The setting is disabled by default in the generated environment file.

dotenv
PUBLIC_COMPRESSION=false
PUBLIC_COMPRESSION_MIN_SIZE=1024

This gives the project a production-ready path without forcing compression during the first local run.

Middleware registration

After configuring runtime paths, the bootstrap registers global middleware.

cpp
presentation::middleware::MiddlewareRegistry::register_all(app);

The generated middleware registry owns application-wide HTTP middleware such as security headers, request logging, and API-level markers.

txt
AppBootstrap
  -> MiddlewareRegistry
      -> security headers
      -> request logging
      -> API marker header

Middleware belongs outside AppBootstrap because it can grow independently. The bootstrap only decides when middleware is added to the application startup flow.

Route registration

The bootstrap then registers base application routes.

cpp
presentation::routes::RouteRegistry::register_all(app);

The route registry connects the generated controllers.

txt
RouteRegistry
  -> HomeController
  -> HealthController

The generated backend starts with:

txt
GET /api
GET /health
GET /api/health

These routes are application-level routes. Feature-specific routes should normally move into app modules as the backend grows.

Generated module registration

The backend template is ready for application modules. After registering base routes, the bootstrap calls the generated module bridge.

cpp
vix::app_generated::register_app_modules(app);

This line is important. It lets enabled modules from vix.app register their routes without requiring AppBootstrap.cpp to include every module controller manually.

A module declaration may look like this:

ini
[module.auth]
enabled = true
path = "modules/auth"
kind = "backend"
depends = []

When the module is enabled, Vix can generate registration code that conceptually behaves like this:

cpp
void register_app_modules(vix::App &app)
{
  api::auth::AuthModule::register_routes(app);
}

The generated registration files are managed by Vix. Do not edit them directly. Change vix.app, the module files, or the module metadata, then run the normal workflow again.

bash
vix modules check
vix build

Startup log

Before running the server, the bootstrap logs the startup message.

cpp
vix::log::info("Starting api on port {}", cfg.getServerPort());

The port comes from runtime configuration. In a generated backend, the default local value is usually:

dotenv
SERVER_PORT=8080

This makes the startup behavior visible in logs and keeps the port controlled by environment configuration instead of hard-coding it in main.cpp.

Running the app

The final step is to run the Vix application.

cpp
app.run(cfg);
return 0;

At that point, the backend has loaded configuration, configured runtime directories, registered middleware, registered routes, registered enabled modules, and started the server using the loaded config.

The normal local workflow is:

bash
cp .env.example .env
vix build
vix run

Then check the generated health route.

bash
curl http://localhost:8080/health

What belongs in AppBootstrap

AppBootstrap should contain startup-level wiring.

Good responsibilities include:

txt
loading configuration
creating vix::App
configuring public files
configuring templates
registering middleware registry
registering route registry
registering generated modules
starting the app

These are application shell responsibilities. They describe how the backend starts.

What should not go in AppBootstrap

Avoid putting feature-specific business logic directly in the bootstrap.

cpp
// Avoid this shape in AppBootstrap.
app.post("/api/auth/login", [](vix::Request &req, vix::Response &res)
{
  // authentication logic
});

A better shape is to let a controller or module own that route.

txt
modules/auth/
  include/auth/AuthModule.hpp
  include/auth/controllers/AuthController.hpp
  src/AuthModule.cpp
  src/controllers/AuthController.cpp

Then the generated module bridge can connect the feature to the running application.

cpp
vix::app_generated::register_app_modules(app);

The bootstrap should not become the place where every feature is implemented. It should remain the place where the backend is assembled.

Editing the bootstrap

Edit AppBootstrap.cpp when the application startup flow changes. Good reasons include changing how configuration is loaded, adding a global middleware registry step, changing static file mounting, changing template setup, or adding a new application-level startup phase.

Do not edit it every time a feature adds a route. Feature routes should live in controllers or modules.

If you add a new source file used by the bootstrap, remember to add it to vix.app.

ini
sources = [
  "src/main.cpp",
  "src/api/app/AppBootstrap.cpp",
  "src/api/presentation/routes/RouteRegistry.cpp",
  "src/api/presentation/middleware/MiddlewareRegistry.cpp",
  "src/api/presentation/controllers/HomeController.cpp",
  "src/api/presentation/controllers/HealthController.cpp",
  "src/api/support/HttpResponses.cpp",
]

The manifest must describe every .cpp file compiled into the backend target.

Relationship with the backend manifest

The backend manifest includes the bootstrap source file and include roots.

ini
sources = [
  "src/main.cpp",
  "src/api/app/AppBootstrap.cpp",
]

include_dirs = [
  "include",
  "src",
]

That lets main.cpp include:

cpp
#include <api/app/AppBootstrap.hpp>

and lets the bootstrap include the route and middleware registries.

cpp
#include <api/presentation/middleware/MiddlewareRegistry.hpp>
#include <api/presentation/routes/RouteRegistry.hpp>

If the source list or include roots are wrong, the bootstrap may fail to compile even though the file exists.

Common mistakes

The most common mistake is adding feature routes directly to AppBootstrap.cpp. That works at first, but it makes the startup file harder to read as the backend grows. Put feature routes in controllers or modules.

Another mistake is editing generated module registration files instead of changing vix.app. The generated bridge is output. The manifest is the source of truth.

A third mistake is forgetting that runtime files must be available beside the built target. If the backend cannot find .env, public/, views/, or storage/, check the resources list in vix.app.

Keep AppBootstrap focused on the startup sequence. It should explain how the backend is assembled, not contain the implementation of every feature. Configuration, runtime paths, middleware, routes, generated modules, and server startup belong there. Feature behavior belongs in controllers, support files, or app modules.

Next step

Continue with routes and middleware to understand how the backend template separates base HTTP routes from the application startup flow.

Routes and Middleware

Released under the MIT License.