Develop a Symfony App Using Svelte and Webpack Encore to Manage Your Twilio Message History
Time to read: 8 minutes
Globalization has changed more things than one can imagine – and software development is not immune. Combined with the recent trend for separating client side and server side operations, poly-repositories have become a de facto standard for application structure.
However, for all its benefits it may not be sustainable for small teams or businesses eager to break out of the conceptualization phase and deliver an MVP (Minimum Viable Product). In such scenarios, the visibility and singular source of truth offered by a mono-repository can translate to faster and smoother deployment of new features.
This article will show you how to combine the new kid on the block, Svelte with the tried-and-trusted Symfony to develop an application, all the while using Webpack Encore to bundle the Svelte app. At the end of this article, you will have built an application to interact with your Twilio message history.
Prerequisites
To follow this tutorial, you need the following:
- A Twilio account.
- PHP 8.0 or higher
- Git
- Composer globally installed
- Yarn
- The Symfony CLI
Set up the application
Start by creating a new Symfony application and changing into the new application's directory using the following commands.
Next, install the Symfony project dependencies. For this project we will require the following:
- Maker: This will help us with creating controllers, entities and the like
- Twig: Twig will be used to render our HTML templates
- Twilio PHP Helper Library: This makes it easy to interact with the Twilio API from your PHP application
- Webpack Encore: Webpack Encore is a simpler way to integrate webpack into your application.
Run the following commands to install the dependencies.
Next, install the Javascript dependencies for the project, by running the following commands.
The dependencies are as follows:
- Axios: Axios will be used to make API requests and get the appropriate response
- Carbon Components Svelte and Carbon Icons Svelte: The Carbon Design system by IBM will be used to render the UI components of the Svelte application
- Svelte: This adds Svelte to our project
- Svelte-loader: This is a webpack loader for Svelte
- Svelte-routing: A declarative Svelte routing library with SSR support.
Next, open webpack.config.js (created by the previous commands) and update it to match the following code.
With this configuration, Webpack will take all the Javascript files (and Svelte components) and bundle them into a single Javascript file. This reduces the burden on the client as it doesn’t have to make multiple requests to load static files. It also provides an avenue for further optimization techniques such as code splitting. While this article does not cover bundling of CSS files, this is another area in which Webpack excels.
To help with bundling the Svelte components, the configuration enables the earlier installed Svelte loader. In addition to this, it specifies the output directory for the bundled .js
file. This is by no means everything on offer, you can find a full list of features here. You can read more about how Svelte handles bundling here.
Add the Svelte component
Create a new folder named svelte, in the top-level directory of the project. This folder will contain all the files related to the components and helper functions required by the Svelte application.
In the svelte folder, create a new folder named components. In this folder, create a new file named App.svelte and add the following to it.
Next, in the svelte directory, create a new file named app.js and add the following code to it.
This file is what was specified as an entry point in the webpack configuration. Next, bundle your application by running the following command in the top-level directory of the project.
This command bundles your Svelte application into a single app.js file. It also watches your svelte folder for file changes - upon which it regenerates the app.js file.
Add a controller for the index page
In a new terminal, run the following command in the top-level directory of the project. This will create a new controller responsible for rendering the index page of the application.
Then, update the newly created src/Controller/IndexController.php file to match the following code.
Next, update templates/index/index.html.twig to match the following.
The encore_entry_script_tags()
function reads public/build/entrypoints.json, generated by Encore, to know the exact filename(s) to render. In addition to that, the CSS for the Carbon components UI library is imported.
View the application
To view the application as it currently is, launch the Symfony application using the following command.
By default, the project is available at localhost:8000. Open the URL in the browser where you should see it look like the following screenshot.
Having completed the foundation of the application, it’s time to build the backend and frontend features.
Building the backend
Add Twilio Credentials
The first thing to do on the backend is integrate with the Twilio API using the earlier installed Twilio PHP Helper Library. But before writing the code, you need to make a note of two key parameters:
- Your Twilio Account SID
- Your Twilio Auth Token
These can be retrieved from your Twilio Console dashboard from the "Account Info" panel.
Next, add local environment variables for your Twilio Account SID and Auth Token. First, create .env.local from the .env file by running the following command.
Next, add the following variables to the newly created .env.local file.
Finally, replace the two placeholders in .env.local with your Twilio Account SID and Auth Token, respectively.
Implement the MessageLogService
The next step is to implement a service that interacts with the Twilio API and provides the requisite functionality to carry out:
- Retrieving all messages
- Retrieving a single message
- Deleting a message
In the src folder, create a new folder named Service. Then, in the src/Service folder, create a new file named MessageLogService.php and add the following code to it.
This service has a constructor that takes your Twilio Account SID and the Auth Token as parameters with which it creates a Twilio Client.
The getMessages()
, getMessage()
, and deleteMessage()
functions make the respective function calls to the Twilio API and pass the response back where required. Per the requirements of the Twilio Client, the getMessage()
and deleteMessage()
functions require the MessageSid in order to get a single message.
Add the service definition for MessageLogService
Next, add a service definition which will bind the arguments for the MessageLogService
with the environment variables defined earlier. To do that, add the following to the services
key in config/services.yaml
With this, the service container is able to initialize and inject the MessageLogService
object wherever it is called.
Add a controller for the API routes
Next, create a new controller to handle API requests from the frontend by running the following command.
This creates a new file named APIController.php in the src/Controller folder. Open it and update the code to match the following.
In addition to making the required call to the MessageLogService
, the controller also caches message resources to prevent repeated network requests for already retrieved resources.
With this code in place, the API now has the following routes:
Endpoint | Method | Function |
---|---|---|
/api/messages | GET | Get all messages |
/api/message/{messageSid} | GET | Get a single message for the provided SID |
/api/message/{messageSid} | DELETE | Delete a single message for the provided SID |
Delegate the routing to the frontend
It is important to note that with this project structure, routing is first handled by the backend. Using Route annotations in controllers or routes declared in config/routes.yaml, Symfony determines how the incoming request should be handled. The Svelte application only takes charge of routing once the index page is loaded.
This means that upon refresh, instead of allowing Svelte to determine the appropriate component to be rendered, Symfony will render an exception page because it cannot determine how the route should be handled.
The solution is to create a subscriber for the NotFoundHttpException which is thrown when Symfony cannot find a route for the specified URL, and render the index page instead of the associated error page. Create the subscriber by running the following command.
Respond to the prompt as shown below.
A new file named RouteNotFoundExceptionSubscriber.php will be created in the src/EventSubscriber folder. Open this file and update it to match the following.
When a kernel exception event occurs, the onKernelException
function will be called with an ExceptionEvent
passed as a parameter. This subscriber should only take action if the thrown exception is an instance of NotFoundHttpException
in which case, the index/index.html.twig template is rendered and returned as a response.
Additionally, the response code is changed from 404 to 200. Changing the response code was only made possible because of the call to the allowCustomResponseCode()
function.
With this in place, any route which is unfamiliar to Symfony will be handled on the frontend. Only when the route is also unfamiliar to the Svelte application will an error page be rendered.
Build the frontend
Build the API helper functions
First, create helper functions which will handle API calls. In the svelte folder in the project's top-level directory, create a new file named Api.js and add the following code to it.
Build the DeleteMessage component
The first component to be built is the DeleteMessage
component. In the svelte/components folder, create a new file named DeleteMessage.svelte and add the following code to it.
This component renders a button which can be clicked to initiate the delete process. This button can be rendered as an icon or a regular button, depending on the value of the showAsIcon
prop.
When the button is clicked, a modal is displayed to ask for the user’s confirmation. If the user clicks the "Delete" button, a loader is displayed and an API request is made via the deleteMessage
helper function.
On a successful delete, a delete
event is dispatched by the component. This allows different parent components to use the DeleteMessage
component even though they have divergent steps to take if the operation is completed successfully.
Build the Message component
In the svelte/components folder, create a new file named Message.svelte and add the following code to it.
This component has one prop named messageSid
which corresponds to the SID of the message to be rendered. Using the getMessage()
helper API function, a request is made to get the message details once the component is mounted. These details are then rendered accordingly.
A handleDelete()
function is defined and passed as a handler for the DeleteMessage
component. In this case, the application redirects to the dashboard.
Build the Messages component
In the svelte/components folder, create a new file named Messages.svelte
Then, add the following code to the file.
On mount, this component retrieves all the messages from the backend via the getMessages()
helper function. This information is then rendered in a paginated table which also allows for sorting and filtering.
The DeleteMessage
component is also rendered (as an icon). As was done for the Message
component, a handleDelete()
function is declared to handle the dispatched event. In this case, a notification is displayed showing the user that the message was deleted successfully. Additionally, the deleted message is filtered out of the list of messages currently rendered.
Build the PageNotFound component
The last component to be built is the component to be rendered for unfamiliar routes. In the svelte/components folder, create a new file named PageNotFound.svelte and add the following code to it.
This component renders an error message, and a link which redirects the user to the dashboard.
Update the App.svelte component
Open svelte/components/App.svelte and update the code to match the following.
With these in place, the application is complete. If you stopped the yarn watch
process, bundle your application by running the following command.
Now, run your Symfony application using the symfony serve
command and navigate to localhost:8000 to see the application in action. Use a different port if Symfony bound to a port other than 8000 on your development machine.
Then, click on any of the Message SIDs in the "MessageSID" column to view a message's details. You can see an example in the screenshot below.
That's how to develop a Svelte app powered by Symfony and Webpack Encore
In this article, you have learnt how to combine a Svelte frontend with a Symfony backend in a mono-repository application. In addition to properly configuring webpack, you also learnt how to delegate routing to the frontend via an event subscriber.
While Svelte is still largely considered a new kid on the block, it has already attained maturity status. With a minimal learning curve, less boilerplate, and a smaller footprint than the more popular and well-known frontend frameworks Svelte is finding its way into the production environment of ever more organizations, and becoming a viable option for greenfield projects as well.
You can review the final codebase on Github. Until next time, make peace not war ✌🏾
Joseph Udonsak is a software engineer with a passion for solving challenges – be it building applications, or conquering new frontiers on Candy Crush. When he’s not staring at his screens, he enjoys a cold beer and laughs with his family and friends.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.