Directory Structure

Fast does not force a directory layout on you. It is a low-level framework, so you are free to organize a project however you like. That said, most Fast apps settle into a similar shape, and starting there will save you some decisions.

A typical project

myapp/
	bootstrap.php        # requires Fast's entry.php, sets up autoloading
	server.php           # registers routes, then calls serve()
	migrate.php          # runs database migrations from the CLI
	.env.php             # environment values, kept OUT of any static root
	app/
		Handlers/        # your invokable route handler classes
		Models/          # your flat model classes
		Middleware/      # custom middleware
	views/               # embedded-PHP templates
	database/
		migrations/      # numbered migration files
	public/
		assets/          # static files served by files()

None of these names are magic. Fast does not scan app/ or auto-load your model classes for you; that is deliberate. You wire things up explicitly, which keeps the framework predictable and keeps you in control of exactly what loads.

The two entry files

Almost every project has two small files at its root.

bootstrap.php is where you require Fast and register any autoloading for your own classes:

require __DIR__ . '/framework/src/entry.php';

// Optional: a tiny PSR-4-style autoloader for your app/ namespace.
spl_autoload_register(function (string $class): void {
	if (str_starts_with($class, 'App\\')) {
		$path = __DIR__ . '/app/' . str_replace('\\', '/', substr($class, 4)) . '.php';
		if (is_file($path)) {
			require $path;
		}
	}
});

server.php is your actual application: it requires the bootstrap, registers everything, and calls serve().

require __DIR__ . '/bootstrap.php';

use_views(__DIR__ . '/views');

get('/', fn (): string => render('home'));

serve(port: 8080, workers: 4);

Keeping secrets safe

One rule matters a lot: never put secrets inside a directory you serve as static files. Your .env.php, your keys, and your migrations should all live outside whatever folder you hand to files(). A good habit is a dedicated public/ directory that contains only assets meant for the browser, and nothing else.

If you serve static files with files('/assets', __DIR__ . '/public/assets'), then only the contents of public/assets are ever reachable over HTTP. Keep configuration and application code well away from it.

Autoloading your own code

Fast's autoloader only knows about the Fast\ namespace. Your model classes, handlers, and middleware need to be loadable too, either through a small spl_autoload_register like the one above, or by requiring them explicitly in your bootstrap. This is called out again in the Database section, because model classes in particular need to be loaded before you query them.

That covers the groundwork. Now let us get into the fun part: defining what your app actually does, starting with Routing.