Quick start
Let’s build the first route end to end: create the project, add a controller, start the server and get a JSON response back. Five minutes.
1. The project
composer create-project flytachi/winter my-app
cd my-appComposer creates the directories and the .env for you. Requirements and the
by-hand build are on the Installation page.
2. The controller
Generate one with make. The leading dot is required; the Controller suffix is
added for you:
php call make -c .Greet # → main/GreetController.phpThe file appears and is autoloaded straight away. Bring it to this shape — a
GET /api/hello/{name} route returning JSON:
<?php
namespace Main;
use Flytachi\Winter\Kernel\Http\Request\Annotation\PathVariable;
use Flytachi\Winter\Kernel\Http\Response\ResponseEntity;
use Flytachi\Winter\Kernel\Http\Stereotype\Controller;
use Flytachi\Winter\Kernel\Route\Annotation\GetMapping;
use Flytachi\Winter\Kernel\Route\Annotation\RequestMapping;
#[RequestMapping('api')]
class GreetController extends Controller
{
#[GetMapping('hello/{name}')]
public function hello(#[PathVariable] string $name): ResponseEntity
{
return ResponseEntity::ok(['message' => "Hello, {$name}"]);
}
}What is happening here:
#[RequestMapping('api')]on the class — a shared/apiprefix for every method.#[GetMapping('hello/{name}')]— the routeGET /api/hello/{name}.#[PathVariable]binds the{name}segment to the$nameargument.ResponseEntity::ok([...])returns200with a JSON body.
The route needs no registration anywhere — the scanner finds it.
3. Run it
php call run devThe server listens on port 8000 on all interfaces.
The word dev turns on file watching: edit a .php and the application restarts
itself. Without it you would stop and start the server after every change.
4. The result
curl http://localhost:8000/api/hello/Winter{"message":"Hello, Winter"}The same address works in a browser — you will see the same JSON.
Route not found?
Look at what actually registered:
php call mapping show
If your route is not in the list, the controller never reached the scan. Check that
the file is not under resources/ or storage/ (both are excluded), that the class
is not abstract, and that the method is public.
5. A little more: a query parameter
Let’s add an optional ?shout=true:
#[GetMapping('hello/{name}')]
public function hello(
#[PathVariable] string $name,
#[RequestParam] bool $shout = false,
): ResponseEntity {
$message = "Hello, {$name}";
return ResponseEntity::ok(['message' => $shout ? strtoupper($message) : $message]);
}curl 'http://localhost:8000/api/hello/Winter?shout=true'{"message":"HELLO, WINTER"}Do not forget the import:
use Flytachi\Winter\Kernel\Http\Request\Annotation\RequestParam;
The default value is what makes the parameter optional — without it a request with
no ?shout would answer 400.
`#[RequestParam]`, not `#[RequestQuery]`
For one value out of the query string you want #[RequestParam]. The
similarly named #[RequestQuery] collects the whole query string into an object
or an array, and on a scalar it produces 500 rather than 400 — a configuration
error, not a data one.
Both are covered on the Requests page.
The whole thing
<?php
namespace Main;
use Flytachi\Winter\Kernel\Http\Request\Annotation\PathVariable;
use Flytachi\Winter\Kernel\Http\Request\Annotation\RequestParam;
use Flytachi\Winter\Kernel\Http\Response\ResponseEntity;
use Flytachi\Winter\Kernel\Http\Stereotype\Controller;
use Flytachi\Winter\Kernel\Route\Annotation\GetMapping;
use Flytachi\Winter\Kernel\Route\Annotation\RequestMapping;
#[RequestMapping('api')]
class GreetController extends Controller
{
#[GetMapping('hello/{name}')]
public function hello(
#[PathVariable] string $name,
#[RequestParam] bool $shout = false,
): ResponseEntity {
$message = "Hello, {$name}";
return ResponseEntity::ok(['message' => $shout ? strtoupper($message) : $message]);
}
}Next
There are two ways on from here — deeper into practice, or wider into how it works.
Keep building:
- Routing — verbs, prefixes, path parameters
- Controllers — what to return and how to get dependencies
- Requests — body, headers, files
- Validation — checking incoming data
Understand the machinery:
- Key concepts — how it all connects
- Components — web, processes, daemons, scheduler
- Philosophy — why it is built this way