Getting Started
Install
composer create-project monad/skeleton NewApp
cd NewApp
create-project writes .env for you, from .env_example and with a
freshly generated APP_SECRET — it is generated rather than left blank because a blank
signing key does not fail, it just makes every CSRF and session token forgeable. Every other key
arrives present and empty, documented in place. An existing .env is never overwritten.
Fill in the DB_* variables in .env, then create
Monad's built-in tables and run your own migrations:
php mitosis setup
php mitosis migrate
Start the built-in development server:
php mitosis serve
This binds the PHP built-in server to http://127.0.0.1:8000, using
public/router.php. Requires PHP >=8.2.
Your first route
Routes are registered in app/routes/web.php. Route::get() takes a URI
and either a closure or a [ControllerClass::class, 'method'] pair — controller actions
are static methods, so that pair is directly callable with no container resolving it:
use Monad\Clarity\Services\Response;
use Monad\Clarity\Services\Route;
Route::get('/hello', fn () => Response::text('Hello, Monad.'));
Visit http://127.0.0.1:8000/hello and you'll see the response text. Route
also has post(), put(), patch(), delete(), and
options(), plus named parameters ({id}, {id:int},
{slug:alpha}, {id:uuid}) and route groups.
Your first middleware
A middleware is any callable of the shape function (Request $request, callable $next):
Response — call $next($request) to continue the pipeline, or return your own
Response to short-circuit it. Register one with ->middleware():
use Monad\Clarity\Services\Request;
use Monad\Clarity\Services\Response;
use Monad\Clarity\Services\Route;
Route::get('/hello', fn () => Response::text('Hello, Monad.'))
->middleware(function (Request $request, callable $next): Response {
$response = $next($request);
return $response->withHeader('X-Powered-By', 'Monad');
});
Reload /hello and check the response headers — X-Powered-By: Monad is
there. This is the same contract every built-in middleware uses (Csrf,
RateLimiter, Authentication, and the rest), so a closure like this one and
a fully-configured class both plug into ->middleware() the same way.
Next steps
- Quickstart — build a slightly larger app: a controller, a view, and a named route group.
- Routing — the full
RouteAPI: groups, named routes, typed parameters, the 404-vs-405 distinction. - Middleware Pipeline — how the pipeline is built and run, and when to use a class instead of a closure.