โ†“Skip to main content
Creating a Symfony API with Vue 3 + Pinia Frontend: Step-by-Step
  1. Blog Posts/

Creating a Symfony API with Vue 3 + Pinia Frontend: Step-by-Step

3 min readยท
vue php symfony

Published on: 2023-11-22

In modern development, decoupling frontend and backend provides scalability and clean architecture. This guide walks you through building:

โœ… A Symfony API
โœ… A Vue 3 frontend using Pinia for state management
โœ… Clear integration with Mermaid visualizations

๐Ÿ› ๏ธ Project Overview

๐Ÿ”ง Part 1 Create Symfony API

1 Install Symfony Skeleton

composer create-project symfony/skeleton my_api_project

2 Install Required Bundles

composer require api symfony/orm-pack
composer require symfony/maker-bundle --dev

3 Configure Database

In .env:

DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/my_api_db?serverVersion=8.0"

Create the database:

php bin/console doctrine:database:create

4 Create Task Entity

php bin/console make:entity

Define:

  • name (string, 255)
  • completed (boolean)

Run migrations:

php bin/console make:migration
php bin/console doctrine:migrations:migrate

5 Expose API Resource

API Platform auto-exposes your entities. Start server:

symfony server:start

Navigate to:

https://localhost:8000/api

for Swagger UI auto-generated docs.

๐Ÿ–ฅ๏ธ Part 2 Create Vue 3 + Pinia Frontend

1 Create Vue 3 App

npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install

2 Install Pinia & Axios

npm install pinia axios

3 Setup Pinia in main.js

import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "pinia";

const app = createApp(App);
app.use(createPinia());
app.mount("#app");

4 Create Pinia Store

src/stores/taskStore.js:

import { defineStore } from "pinia";
import axios from "axios";

export const useTaskStore = defineStore("taskStore", {
  state: () => ({
    tasks: [],
  }),
  actions: {
    async fetchTasks() {
      try {
        const res = await axios.get("https://localhost:8000/api/tasks");
        this.tasks = res.data["hydra:member"];
      } catch (error) {
        console.error("Error fetching tasks:", error);
      }
    },
  },
});

5 Use Store in Component

Example TaskList.vue:

<script setup>
import { onMounted } from "vue";
import { useTaskStore } from "./stores/taskStore";

const taskStore = useTaskStore();

onMounted(() => {
  taskStore.fetchTasks();
});
</script>

<template>
  <div>
    <h1>Tasks</h1>
    <ul>
      <li v-for="task in taskStore.tasks" :key="task.id">
        {{ task.name }} - {{ task.completed ? "โœ…" : "โŒ" }}
      </li>
    </ul>
  </div>
</template>

6 Frontend Data Flow

๐ŸŒ Part 3: Handle CORS

Install Nelmio CORS Bundle:

composer require nelmio/cors-bundle

In config/packages/nelmio_cors.yaml:

nelmio_cors:
  defaults:
    allow_origin: ["*"]
    allow_methods: ["GET", "OPTIONS", "POST", "PUT", "DELETE"]
    allow_headers: ["Content-Type", "Authorization"]
    max_age: 3600
  paths:
    "^/api/":
      allow_origin: ["http://localhost:5173"] # Vite dev server URL
      allow_headers: ["Content-Type", "Authorization"]
      allow_methods: ["GET", "OPTIONS", "POST", "PUT", "DELETE"]
      max_age: 3600

โœ… Part 4: Run & Test

  1. Start Symfony backend: symfony server:start
  2. Start Vue app: npm run dev
  3. Navigate to your frontend, and your tasks should load from the Symfony API seamlessly.

๐ŸŽฏ Next Steps

  • Implement POST, PUT, DELETE actions in API and Pinia
  • Add JWT authentication for secure routes
  • Refactor frontend logic with Composables for reusability
  • Deploy backend and frontend with separate pipelines

Using Symfony as a decoupled API backend with Vue 3 + Pinia frontend offers a modern, maintainable, and scalable architecture for your applications. The above visualizations and step-by-step approach will guide your next production-ready builds efficiently.