Skip to content

Template for a green-field startup covering complete app + DevOps (FE, BE, CI/CD). Following best practices.

License

Notifications You must be signed in to change notification settings

petrzivny/symfony-react-skeleton

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

91 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Table of Contents
  1. About The Project
  2. Getting Started
  3. What is included out-of-the-box
  4. How to code like a PRO
  5. Deploy to cloud (manually)
  6. Pictures
  7. Contributing
  8. Roadmap
  9. Frequently Asked Questions
  10. License

React 18.2 Symfony 6.3

About The Project

A template to jumpstart your new greenfield project. If you are a startup or an entrepreneur thinking to start a new project with php and symfony as a BE and Typescript and React as a FE, consider to use Symfony React skeleton and save weeks of development.

This project covers common (repetitive) parts of most greenfield projects. Fully functional and communicating BE and FE parts via REST, CI pipeline with robust tests and helm deployment.

On top of it you will receive BE, FE and DevOps best practices already implemented. You software development team can follow these guidelines to deliver sustainable and maintainable top quality product.

Don't forget to give the project a star!

Live Demo - FE - production environmentΒ |Β Live Demo - BE status - production environment

Live Demo - FE - dev environmentΒ |Β Live Demo - BE status - dev environment

(back to top)

Getting Started

Prerequisites

(back to top)

Installation

  1. Clone the repo. Replace {myproject} with a name of your new project/app. It is important to stay in one terminal window during all install steps, otherwise PROJECT_NAME needs to be set again.
    PROJECT_NAME={myproject}
    git clone https://github.com/petrzivny/symfony-react-skeleton.git $PROJECT_NAME && cd $PROJECT_NAME
  2. Setup environmental variables by using prepared template
    cp api/.env.local.dist api/.env.local
  3. Change name of your project (application) from symfony-react-skeleton to {myproject}. You can do it in your editor or use following command. Choose only one.
    For Linux:
    sed -i "s/\${PROJECT_NAME:-symfony-react-skeleton}/$PROJECT_NAME/g" .docker/docker-compose.yaml .docker/docker-compose-prod.yaml 
    or for MacOs
    sed -i '' "s/\${PROJECT_NAME:-symfony-react-skeleton}/$PROJECT_NAME/g" .docker/docker-compose.yaml .docker/docker-compose-prod.yaml
    or manually
    # Replace all occurrences of string "${PROJECT_NAME:-symfony-react-skeleton}" with {myproject}.
  4. Build BE docker images and run them as docker containers in dev mode
    cd .docker && docker compose --env-file ../api/.env.local up -d
    Don't worry about nginx and php Errors or Warnings, it just means docker images needs to be build for a first time. Try docker ps. 3 containers should be up and running (php, nginx, postgres), if not, try docker ps -a and docker log.
  5. Install php dependencies (inside php docker container)
    docker exec -it ${PROJECT_NAME}_php composer i
  6. Visit http://localhost:81/api/status to check if BE is running properly (you should see 200 JSON response with debug info). I recommend to use this chrome extension to format json responses.
  7. Install javascript dependencies
    cd ../fe && pnpm install
  8. Run FE hot reload dev server
    pnpm run dev
  9. There should be newly opened http://localhost:5173/ window in your browser with react as a FE framework communicating with BE Symfony. The green value is fetched from BE database. You are ready to start local development. Happy coding. Maybe time to give this project a star ⭐, thank you. πŸ˜‰

Next time you only need to perform points 4. and 8. to start developing. I recommend to set up an alias for them.

(back to top)

Linking your remote repository

At this moment your only remote repository is origin: https://github.com/petrzivny/symfony-react-skeleton.git. But you need to have your own remote repository connected too (I recommend to leave original repository if you want to contribute this public project in the future).

# rename origin: https://github.com/petrzivny/symfony-react-skeleton.git to template: https://github.com/petrzivny/symfony-react-skeleton.git
git remote rename origin template

# Create a new repository (preferably on GitHub to use all features of this template)
git remote add origin {url_of_your_repo} 
#eg: git remote add origin [email protected]/petrzivny/myproject.git

git push -u origin main

Take a look into your GitHub repository. All code should be there and your first GitHub Actions pipeline should be initiated. At this point you will need to configure self-hosted runner(s).

(back to top)

What is included out-of-the-box

  1. Docker to run complete dev environment (php + nginx + PostgreSQL)
  2. Symfony framework as a backed REST api
    • Independent on any used frontend. Communicating via REST. (best-practice πŸ’Ž)
    • Symfony tuned for best performance in prod. (performance ⚑).
    • Opcache php preloading + symfony recommended optimization (performance ⚑).
    • Php JIT not implemented. JIT increases performance only in high concurrency regime while in low concurrency it is more performant to not use JIT (performance ⚑).
    • Xdebug setup to debug both html requests and CLI commands.
    • Phpstan in a very strict level. Including shipmonk-rules.
    • PHP_CodeSniffer in a very strict level (lots of rules are my personal "taste", feel free to change/remove them). Including phpstan-strict-rules and slevomat-coding-standard
    • Psalm.
    • PHPUnit Unit tests.
    • PHPUnit Functional tests (including smoke tests).
    • Other linters (Composer, Yaml, Symfony container).
    • Php-fpm access proper logging (json format, GCP LogEntry compatible, correct severity, trying to fix https://bugs.php.net/bug.php?id=73886)
    • Symfony monolog proper logging (json format, GCP compatible using GoogleCloudLoggingFormatter)
    • DDD, TDD and BDD ready. If you want to follow DDD, create new bounded contexts in api/src/Context
  3. React framework as a frontend SPA
    • Independent on any used backend. Communicating via REST (best-practice πŸ’Ž)
    • Typescript
    • Eslint
    • Vite
    • React Query and Axios for proper data fetching including caching.
    • wsc (should be 20x faster than Babel but see the current caveats).
    • Prettier
  4. DevOps: CI pipeline to build both test and prod images, test them and push prod images to registry
    • Run all BE tests from point 2 on final (test) docker image (best-practice πŸ’Ž) (Phpstan, CodeSniffer, Psalm, PHPUnit, linters, etc ...).
    • Run helm lint and helm dry installation into minikube cluster to ensure real deployment will be without surprises.
    • If everything passes there are php and nginx environment agnostic (best-practice πŸ’Ž) containers ready to be shipped into any environment (including prod of course).
    • Pipeline expects self-hosted GitHub runner(s). See for more information.
  5. DevOps: CD
    • Helm kubernetes deploys main branch to prod and branches starting with feature* to dev environment.
    • Platform agnostic. As long as there is a Kubernetes you can use simple config files in .deploy/helm dir to deploy to your environment.
    • Separate pods for nginx and php for better scalability (best-practice πŸ’Ž).
    • Both nginx and php pods have readiness probes.
    • Both nginx and php pods have liveness probes.
    • Optional: Ingress to connect your kubernetes cluster with outside world (you can use platform Load Balancer, but it is usually billed).
    • Let's Encrypt Certbot.
  6. Security:
    • Secrets are not stored in file system, thus prevent directory traversal attack (best-practice πŸ’Ž).
    • Secrets are not stored as environment variables, thus prevent any debug or log attacks or misconfigurations (best-practice πŸ’Ž).
    • Zero trust, the least privilege and giving as minimum as possible information principles used in nginx.conf.
    • HTTPS Let's encrypt certificate. And if you use infrastructure-skeleton you receive automated creation of missing or expired certificates with cert-manager.
    • HTTP -> HTTPS 301 redirect (only for prod environments, not for eg dev.skeleton.cz).
    • Using local dev proxy between FE and BE to correctly handle CORS (and to avoid HOST_URL=localhost). (best-practice πŸ’Ž)
    • Roave/SecurityAdvisories to prevent using dependencies with known security vulnerabilities.
    • CSRF attack prevented.
    • XSS attack prevented.
    • SQL injection attack prevented (Doctrine capability if you use it correctly).
    • etc...

(back to top)

How to code like a PRO

All the following setup information are optional, but highly recommended. It should not take you more than 10 minutes of setup time, and it will save you hours of your time in a future. Senior developer uses all of them. Period.

Use PHPStan

PHPStan is configured out of the box. For a better DX configure your IDE too. See PHPStan usage for more details.

Use PHP_CodeSniffer

PHP_CodeSniffer is configured out of the box. For a better DX configure your IDE too. See PHP_CodeSniffer usage for more details.

Use Xdebug

Xdebug is configured out of the box on the container side. You need to configure your IDE too. See Xdebug usage for more details.

Setup alias for fast start of development

Setup aliases in your shell to start your local dev stack for your everyday use. For example what I have in my .zshrc file:

alias dcs="cd ~/Projects/personal/symfony-react-skeleton/.docker/ && docker compose --env-file ../api/.env.local up -d && cd ../fe && pnpm run dev"
alias des='docker exec -it symfony-react-skeleton_php sh -l'

I use dcs to start complete FE+BE dev environment and des to docker exec into php container.

Globally gitignore your IDE

Create a global .gitignore file in a parent directory for your project and add .idea line in it (.idea is for PhpStorm, if you use another editor, change the directory name accordingly). This directory created by PhpStorm in every project should not be versioned but should not be included in project's scope .gitignore file either (best-practice πŸ’Ž).

Use Linux

I know this is not favorite opinion, but if you are serious about SW development and gaining seniority in it, you should have Linux as OS in your development machine. I would recommend dual boot setup. I personally don't think WSL or other similar solutions are the right way.

(back to top)

Deploy to cloud (manually)

Use this section for debugging or if you want to see your app running in prod ASAP. Real world deployment should be setup in CD pipeline. This example is configured out-of-the-box for infrastructure-skeleton.

  1. Provision your infrastructure by using infrastructure-skeleton. Save output values from terraform apply. You will use them in following steps. You can use your own infrastructure, in that case use your own output parameters.
  2. Change parameters.application_name: parameter in api/config/services.yaml. Use app_name output from terraform apply.
  3. Build and push your prod images. For this you need artifact_registry terraform output.
    # for {IMAGE_PATH} use {artifact_registry from terraform output}/{myproject} eg.: IMAGE_PATH=europe-west1-docker.pkg.dev/basic4-2542859/all-registry-europe-west1/symfony-react-skeleton
    IMAGE_PATH={artifact_registry}/{myproject}
    cd .docker && IMAGE_PATH="${IMAGE_PATH}" docker compose -f docker-compose-prod.yaml build
    IMAGE_PATH="${IMAGE_PATH}" docker compose -f docker-compose-prod.yaml push
  4. Edit values.yaml file (use app_environment, gcp_project_id, app_gcp_service_account_name and app_k8_service_account_name outputs from infrastructure terraform apply from point 1.). Don't forget to edit also host which will be your url and letsencryptCertEmail for Let's encrypt.
  5. Change name in .deploy/helm/Chart.yaml for example use app_name output from terraform apply.
  6. Deploy your pushed images into k8 cluster created in point 1. For this you need app_k8_namespace terraform output. You can choose any string for {helm_release_name}.
    K8_NAMESPACE={app_k8_namespace}
    HELM_RELEASE={helm_release_name}
    cd ../.deploy/helm
    helm upgrade $HELM_RELEASE . --create-namespace --install --namespace "${K8_NAMESPACE}" --set image.path="${IMAGE_PATH}"
  7. Give a nginx ingress approx 5 min to load up and test your running app. {your_ip} can be grabbed here or via kubectl get ingress -A. {host} is the same you used in point 4 in values.yaml.
    curl -ivL 'https://{your_ip}/api/status' --header 'Host: {host}'
    # eg: curl --location --request GET 'http://104.155.113.172/api/status' --header 'Host: skeleton.totea.cz'

(back to top)

Pictures

ci-pipeline.png status-dev.png status-prod.png status-console.png cert.png

(back to top)

Contributing

I greatly appreciate all suggestions and contributions. Contributions are what make the open source community such an amazing place to learn, inspire, and create.

There are three options how to contribute.

  • Open an issue with the tag "enhancement".
  • Send me a message via LinkedIn or email and I will grant you write access to this repo. or
  • Follow these instructions.

Any options is fine and I am looking forward for your awesome improvements or just typo fix.

(back to top)

Roadmap

  • CI: Add linter for helm
  • CI: Add dry deploy to k8 as a test
  • Helm: Add liveness probe
  • Helm: Add readiness probe
  • Add static URL as a Live Demo link
  • Add OPCache
  • Add https certificate
  • CI: Push prod images only for main branch
  • CI: Use SHA for prod images
  • Add CI e2e tests against prod images
  • Add Prometheus
  • Add Grafana (and disable GCP logs)
  • Log deprecations
  • Collect and display console logs
  • Add Helm tests
  • Add architecture schema into README
  • Change .docker -> docker (it is more intuitive to have this folder not hidden)

See the open issues for a full list of proposed features (and known issues).

(back to top)

Frequently Asked Questions

How to run CI tests locally?

A developer can run all BE tests at once composer test or only selected BE test can be ran e.g. composer phpstan. Commands must be run inside php container. A developer can run all FE tests at once pnpm run test or only selected FE test can be ran e.g. pnpm run lint.

   docker exec ${PROJECT_NAME}_php composer test

How to run BE application imitating prod mode locally?

  1. Uncomment services.php.environment section in .docker/docker-compose-prod.yaml to be able to connect to local DB if needed.
  2. cd .docker && docker compose -f docker-compose-prod.yaml up -d
  3. Visit http://localhost:82/api/status.

(back to top)

Where should I put my new php code?

This skeleton is DDD ready. All code coupled to App (Symfony) should be placed in api/src/App. E.g. security, authentication, authorization, Entities if you want to use doctrine migrations and/or ORM, Controllers and console Commands. The rest (bounded contexts) should be independent on Symfony framework and placed in api/src/Context/MyBoundedContext

How to debug BE?

You can use /api/status endpoint to get some debug information for php-fpm or bin/console status command to get same information for CLI.

License

See the LICENSE file for license rights and limitations (MIT).

(back to top)