Should everything be on a single server to cut costs, at the risk of performance bottlenecks? Or should we separate responsibilities, dedicating a server to each language for better scalability?
With Upsun, you don’t even have to worry about these trade-offs. Every project hosted with us can include as many applications and services as needed. Thanks to dedicated containers for each application and service, and on-the-fly resource allocation, you remain in full control. The challenge:
Our starting point is a Symfony-based demo application, built using Symfony Demo v7.1. The website currently displays a list of blogposts using Twig templates. We will introduce a Next.js frontend to replace the current Symfony-based UI. The goal? Display blogposts on a fresh new frontend, using Next.js, and add possibility to read corresponding blogpost. Backend preparation:
We need a Symfony Demo project, hosted on Upsun. If you don’t already have this, please follow this blogpost about how to host a Symfony Demo project on Upsun. 🚀 Let’s dive in!
Assumptions:
- You already have an Upsun account. If you don’t, please register for a trial account. You can sign up with an email address or an existing GitHub, Bitbucket, or Google account. If you choose one of these accounts, you can set a password for your Upsun account later.
- You have a Symfony Demo hosted on an Upsun project. If you don’t, please follow this blogpost first
- You have the Symfony CLI installed locally.
- You have the latest version of PHP and Node.js installed locally.
Prepare your Symfony project
The initial step will be to prepare our source code architecture to welcome a new frontend application and then update our Symfony application by adding 2 new routes, delivering list of blogposts and info from a single blogpost, in a Json format.Create a preview environment
As we never work on the production environment, we will create a dedicated preview environment. To create a preview environment, use the following command line. It will create, on the fly, an exact copy of your parent environment (here, branchmain, so it’s your production environment).
Terminal
decouple-frontend and deploy the corresponding preview environment in less than a few minutes.
Sanitization of data:
As detailed in this previous blogpost, How to sanitize preview environment data, we can also sanitize data on the fly with each creation of new preview environments. This is done by adding a call to a new Symfony Command
As detailed in this previous blogpost, How to sanitize preview environment data, we can also sanitize data on the fly with each creation of new preview environments. This is done by adding a call to a new Symfony Command
php bin/console app:sanitize-data in the hooks.deploy.Update your project architecture
Each time you update and push your source code to your project, Upsun will detect changes in the definedsource.root folder for each application.
If it detects any updates in your application subtree, it will rebuild your application container.
You can also add a new application in the source code as is,
but this means that each time you update your Next.js application, it will also detect changes in the Symfony application subtree (root), and rebuild your Symfony container (which is not convenient).
backend folder.
From the root of your project source code, execute the following command lines:
Terminal
backend/ sub-folder, except itself and the.upsun folder (which needs to remain at the root of your source code).
Then, in your .upsun/config.yaml file, update the source.root parameter of your app application:
.upsun/config.yaml
Add new PostRepository functions
We will create 2 newPostRepository functions, getAllPosts and getPost that will return an array of results:
-
getAllPosts: get all posts -
getPost: get a single post, in an array -
To do so, please update your
backend/src/Repository/PostRepository.phpfile and the 2 functions above:
backend/src/Repository/PostRepository.php
Add new Symfony routes
For our frontend to fetch data from our Symfony, we will need 2 new routes:<lang>/api/get-all-posts: to get the list of Posts<lang>/api/get-post/{id}: to get info from a single post
backend/src/Controller/ApiController.php file with the following source code:
backend/src/Controller/ApiController.php
Deploy your updates
Push your updates to your Upsun project using the following command lines:Terminal
Add a new Next.js application
Then, we will add a new Next.js application, and display the list of blogposts, with links to each blogpost pages.Create a new Next.js application
To create a new Next.js application locally, use this command from the root of your project source code:Terminal
App router is required, so you can respond No to everything else:
Terminal
Configure your frontend application
To add a new application in your Upsun configuration, open your favorite IDE,
like VSCode or PHPStorm,
and copy the .upsun/config.yaml snippet (myapp configuration block) given in the Next.js Getting started guide, with few updates explained below.
Your .upsun/config.yaml file will look like this:
.upsun/config.yaml
- line 2: change
upstreamfromapp:httptofrontend:httpas our application will be displayed through thefrontendapp - line 9: change name of the app from
myapptofrontend - line 11:
source.rootneeds to point to the/frontendfolder - line 13: adjusting
container_profileparameter to get more RAM than the default Node.js profile
For the sack of this tutorial, please add the
frontend Next.js configuration block AFTER the existing app block.We will later add, in the Add a relationship section, a relationship from frontend to app,
and when the Upsun internal process enables this relationship, app needs to already exist.First deploy
We will first check that our newfrontend application deployment is responding well.
To do so, we will need to add the Next.js source code and updates in the .upsun/config.yaml file:
Terminal
Terminal

Decouple your application
Then, we will display the list of blogposts, fetched from Symfony routes, with links to each blogpost page.Add a relationship from frontend to app
As we don’t want to expose our Symfony app API over HTTP, we will define a relationship from frontend to app.
To define this relationship between applications, update your .upsun/config.yaml with the following:
.upsun/config.yaml
app data in the frontend container, via an internal api.internal route (no more HTTP).
As mention in the Configure your
frontend application section,
when the Upsun internal process will create this relationship, app needs to already exist, and so, needs to be defined before the frontend application.Update Next.js source code
We will now update our Next.js application to add 2 new components (Posts and Post), and do some design update to display the list of posts on the homepage and a single page for each post.
Create a Posts component
First, we need to create a newPosts component that will fetch data from the Symfony <lang>/api/get-all-posts route and pass it to our homepage.
To do so, create a new frontend/app/components/posts.js file with the following source code:
frontend/app/components/posts.js
Update hompepage
Now, we want to display thisPosts component on our homepage.
We will update the fontend/app/page.js file with the following source code:
fontend/app/page.js
Add styles
Please create a newfrontend/app/page.css file with the following CSS:
frontend/app/page.css
@import "~bootstrap/dist/css/bootstrap.css"; import line at the beginning.
This is due to the missing bootstrap Node module in your frontend application.
To install bootstrap module, execute the following command line from the frontend folder:
Terminal
Create a Post component
As we want to display each blogpost in a dedicated page, we need to create a newPost component that will fetch data from the Symfony <lang>/api/get-post/{id} route and pass it to the Post page.
To do so, create a new frontend/app/components/post.js file with the following source code:
frontend/app/components/post.js
Create a Post page
For Next.js to handle dynamic routes, we will create a singlepage.js file for all posts, handling the lang and id of the post.
To do so, we need a frontend/app/post/[lang]/[id]/page.js (keep the brackets for [lang] and [id]) file with the following source code:
frontend/app/post/[lang]/[id]/page.js
lang and id from the url (ex: /post/en/2), pass it to our Post component and fetch corresponding post through Symfony route <lang>/api/get-post/{id}
Test it
Let’s test our final application locally.Start your Symfony application
For more convenience, we will start the Symfony application server locally by executing following command lines:Terminal
Start your Next.js application
Terminal

Turn on Next.js production mode
As our homepage is displaying the list of blogposts, and we can navigate to read a blogpost, we can now switch Next.js to production mode. Update your.upsun/config.yaml and add the following environment variable configuration:
.upsun/config.yaml
Deploy
Let’s deploy our last updates by using the following:Terminal

Deploy to production
After checking that thedecouple-frontend interface is looking great and meets your needs, you can merge decouple-frontend branch to themain branch, using the following command lines from the root of your project:
Terminal
decouple-frontend source code into the main branch and deploy it to production, and then delete decouple-frontend environment and corresponding Git branch.
Et voilà, your application has been decoupled in less than few minutes.
Conclusion
By decoupling the frontend from the backend, you embrace a modern and modular architectural approach that offers greater flexibility in both development and deployment. This separation allows each team to focus on their area of expertise while ensuring seamless integration through robust APIs. In this tutorial, we explored how to set up a solution that leverages technologies like Symfony for the backend and Next.js for the frontend, all orchestrated on a platform like Upsun. This approach provides optimized performance, improved scalability, and a faster development cycle. Beyond the technical aspects, this methodology enhances the ability to adapt to market changes and user demands. By separating responsibilities, you gain agility, security, and better maintainability for your applications. Adopting a decoupled architecture is an investment in a strategy that promotes continuous innovation and the creation of high-quality user experiences.We would love your feedback!
Try it today and experience the difference! 👉 Explore the Docs👉 Need Help? Contact Support
👉 Join the Discussion:
👉 DEV.to
👉 Discord We’d love to hear how this tutorial improves your experience of our product!
Final source code result
If you check the final source code of this tutorial, please refer to this GitHub repositoryexamples/demo-decouple-frontend folder.