Quickstart: Build Your First Winter Boot Microservice
This guide walks you through building a minimal but complete Winter Boot microservice from scratch. By the end you will have a running HTTP server, a managed service bean, and a REST endpoint you can hit with curl.
-
Install Winter Boot
Set up PHP, the required extensions, and Composer by following the Installation guide. Then create your project and add the framework:
Terminal window mkdir my-service && cd my-servicecomposer require suvera/winter-boot -
Create the Directory Structure
Winter Boot expects a
config/directory forapplication.ymland asrc/directory for your PHP classes. A minimal layout looks like this:my-service/├── composer.json├── config/│ └── application.yml├── src/│ ├── GreetingService.php│ └── GreetingController.php└── Application.php -
Register Your Namespace with Composer
Winter Boot loads your classes through Composer’s autoloader, so map your namespace to
src/incomposer.json:composer.json {"require": {"suvera/winter-boot": "*"},"autoload": {"psr-4": {"com\\example\\myapp\\": "src/"}}}Then regenerate the autoloader:
Terminal window composer dump-autoload -
Write application.yml
Create
config/application.yml. Theserverblock sets where the HTTP server listens (useaddress: 0.0.0.0when running in Docker). Thewinter.applicationblock gives your service a name, id, and version, shown in the startup banner and available fromApplicationContext.config/application.yml server:port: 8080address: 127.0.0.1winter:application:name: My Greeting Serviceid: greeting-serviceversion: 1.0.0-DEV -
Create a Service Bean
Annotate a class with
#[Service]to register it as a managed bean. Winter Boot creates it, injects its dependencies, and makes it available for#[Autowired]injection anywhere in the application.src/GreetingService.php <?phpdeclare(strict_types=1);namespace com\example\myapp;use dev\winterframework\stereotype\Service;#[Service]class GreetingService{public function greet(string $name): string{return sprintf('Hello, %s! Welcome to Winter Boot.', $name);}} -
Create a REST Controller
Annotate a class with
#[RestController]and map an HTTP route with#[GetMapping]. Inject the service bean via#[Autowired]. Return aResponseEntityto control the HTTP status code and response body.src/GreetingController.php <?phpdeclare(strict_types=1);namespace com\example\myapp;use dev\winterframework\stereotype\Autowired;use dev\winterframework\stereotype\RestController;use dev\winterframework\stereotype\web\GetMapping;use dev\winterframework\stereotype\web\RequestParam;use dev\winterframework\web\http\ResponseEntity;#[RestController]class GreetingController{#[Autowired]private GreetingService $greetingService;#[GetMapping(path: '/api/v1/greet')]public function greet(#[RequestParam(required: false)] string $name = 'World'): ResponseEntity {$message = $this->greetingService->greet($name);return ResponseEntity::ok()->withJson(['message' => $message]);}} -
Create the Application Entry Point
The entry-point class carries the
#[WinterBootApplication]attribute, which tells the framework where to find your configuration and which namespaces to scan for beans.configDirectory— directories containingapplication.ymland any other config files.scanNamespaces— pairs of[NamespacePrefix, BaseDirectory]to scan for beans. The classes must be loadable by Composer’s autoloader (see Register Your Namespace with Composer above). ABaseDirectorythat does not exist (an empty folder git did not keep, for example) is skipped with a warning instead of stopping startup.
Application.php <?phpdeclare(strict_types=1);use dev\winterframework\stereotype\WinterBootApplication;use dev\winterframework\core\app\WinterWebSwooleApplication;require_once __DIR__ . '/vendor/autoload.php';#[WinterBootApplication(configDirectory: [__DIR__ . '/config'],scanNamespaces: [['com\\example\\myapp', __DIR__ . '/src']])]class Application{public static function main(): void{(new WinterWebSwooleApplication())->run(Application::class);}}Application::main(); -
Run the Application
Start the server by running the entry point with PHP. It starts worker processes and accepts HTTP connections on the port set in
application.yml.Terminal window php Application.phpYou should see output similar to:
Http server started on 127.0.0.1:8080, pid:12345, master_pid:12344 -
Test with curl
In a separate terminal, send a request to the greeting endpoint:
Terminal window curl "http://127.0.0.1:8080/api/v1/greet?name=Alice"Expected response:
{"message": "Hello, Alice! Welcome to Winter Boot."}Test the default parameter:
Terminal window curl "http://127.0.0.1:8080/api/v1/greet"{"message": "Hello, World! Welcome to Winter Boot."}
What’s Next?
Section titled “What’s Next?”Now that your service is running, explore these topics to build on the foundation:
application.yml, #[Value], and additional property sources.#[Bean] factories, and #[PostConstruct] lifecycle hooks.#[Transactional].#[Async] and #[Scheduled].