Symfony API

Bootstrapping.

composer create-project symfony/skeleton:"7.0.*" symfony-api-test

# install dev dependencies
composer require --dev doctrine/doctrine-fixtures-bundle phpunit/phpunit symfony/browser-kit symfony/css-selector symfony/debug-bundle symfony/maker-bundle symfony/phpunit-bridge symfony/stopwatch symfony/web-profiler-bundle zenstruck/foundry

# add doctrine migrations
composer require doctrine/doctrine-migrations-bundle

# add symfony messenger for async
composer require symfony/messenger symfony/doctrine-messenger

# add security recipe
composer require security

# add UUID for public IDs
composer require symfony/uid

# add JWT Auth
composer require lexik/jwt-authentication-bundle

# add Nelmio Docs
composer require nelmio/api-doc-bundle

PHPSTAN

phpstan analyse -l 8 src/

 12/12 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%


 [OK] No errors



Commands and Run

# Run the application on Docker ([http://localhost](localhost))
docker compose up

# fixtures
php bin/console doctrine:fixtures:load --no-interaction

Application Structure

src/
├── Controller
│   ├── UserAdminController.php
│   └── UserController.php
├── DataFixtures
│   └── AppFixtures.php
├── Entity
│   ├── Trait
│   └── User.php
├── Error
│   └── Normalizer.php
├── Factory
│   └── UserFactory.php
├── Kernel.php
├── Repository
│   └── UserRepository.php
├── Response
│   └── UserResponse.php
└── Service
    └── UserService.php

For Authentication I'm used lexik/jwt-authentication-bundle which also provides the login route /api/login_check.

Users are created with different roles using fixtures and will be created during container launch.

I've decided to differentiate admin routes to user routes, so access control are simplified.

I've added Nelmio Docs, mapped only one response as an example: http://localhost/api/doc.json

Unified User Response

{
  "data": {
    "id": "b0393b4c-d213-40fa-8a7c-ab30f5a3eb99",
    "email": "sjunior.admin@gmail.com",
    "roles": [
      "ROLE_ADMIN",
      "ROLE_USER"
    ]
  }
}

Docker and tests

# run migration for test environment
docker compose exec app-default php bin/console --env=test do:mi:mi

# run phpunit
docker compose exec app-default php vendor/bin/phpunit

# run any other command
docker compose exec app-default <command>

# log into the container
docker compose exec app-default /bin/sh

Connecting to local Database

Host: 127.0.0.1

Port: 3306

Database: app_core_db

User: app_admin

Pass: 1cgMx56faAD8v2343Adf433x1ppW

What can be improved?

  • Filters
  • Pagination
  • Easy extensibility (add new endpoints)
  • Normalization of Response Codes
  • Normalization of Validations Errors
    • Required
    • Exists
    • Types
  • Nelmio Configuration and Mapping (OpenAPI Specs)
  • Global Error Handling
  • Database optmization - queries
  • Cache
  • Tests
  • Logs
  • Montitoring / Observability

Running it using CURL

Login

POST http://localhost/api/login_check

curl -X POST http://localhost/api/login_check \
 -H "Accept: application/json" \
 -H "Content-Type: application/json" \
 -d '{"email": "sjunior.admin@gmail.com", "password": "test123"}'

List Users (role admin)

GET http://localhost/api/admin/v1/users

curl -X GET http://localhost/api/admin/v1/users \
 -H "Accept: application/json" \
 -H "Content-Type: application/json" \
 -H "Authorization: Bearer <token_from_previous_request>"

Response

{
  "data": [
    {
      "id": "27e21e47-9256-4e57-aa4c-aa33f6da7b97",
      "email": "sjunior.admin@gmail.com",
      "roles": [
        "ROLE_ADMIN",
        "ROLE_USER"
      ]
    },
    {
      "id": "e975d5ed-dcf8-4fe8-8f52-d8384bc2558a",
      "email": "sjunior.user@gmail.com",
      "roles": [
        "ROLE_USER"
      ]
    }
  ]
}

From here is just change the ENDPOINT and METHOD and you can test all All Endpoints: Host: http://localhost

Method Endpoint Description Role
POST /api/login_check Login User
GET /api/admin/v1/users List Users Admin
POST /api/admin/v1/<:id>/user Create User Admin
GET /api/admin/v1/<:id>/user Read User Admin
PUT /api/admin/v1/<:id>/user Update User Admin
DELETE /api/admin/v1/<:id>/user Delete User Admin
GET /api/v1/user Read User User
POST /api/v1/user Update User User

Running it using Rest Client VSCode

Add it here Configuration files are:

  • user-admin.http
  • user.http