Skip to content

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.

  1. 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-service
    composer require suvera/winter-boot
  2. Create the Directory Structure

    Winter Boot expects a config/ directory for application.yml and a src/ 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
  3. Register Your Namespace with Composer

    Winter Boot loads your classes through Composer’s autoloader, so map your namespace to src/ in composer.json:

    composer.json
    {
    "require": {
    "suvera/winter-boot": "*"
    },
    "autoload": {
    "psr-4": {
    "com\\example\\myapp\\": "src/"
    }
    }
    }

    Then regenerate the autoloader:

    Terminal window
    composer dump-autoload
  4. Write application.yml

    Create config/application.yml. The server block sets where the HTTP server listens (use address: 0.0.0.0 when running in Docker). The winter.application block gives your service a name, id, and version, shown in the startup banner and available from ApplicationContext.

    config/application.yml
    server:
    port: 8080
    address: 127.0.0.1
    winter:
    application:
    name: My Greeting Service
    id: greeting-service
    version: 1.0.0-DEV
  5. 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
    <?php
    declare(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);
    }
    }
  6. Create a REST Controller

    Annotate a class with #[RestController] and map an HTTP route with #[GetMapping]. Inject the service bean via #[Autowired]. Return a ResponseEntity to control the HTTP status code and response body.

    src/GreetingController.php
    <?php
    declare(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]);
    }
    }
  7. 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 containing application.yml and 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). A BaseDirectory that does not exist (an empty folder git did not keep, for example) is skipped with a warning instead of stopping startup.
    Application.php
    <?php
    declare(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();
  8. 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.php

    You should see output similar to:

    Http server started on 127.0.0.1:8080, pid:12345, master_pid:12344
  9. 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."}

Now that your service is running, explore these topics to build on the foundation: