Super-Hub: Aplicações Full Stack & Engenharia de Software • Trilha Progressiva em 4 Níveis


🧭 Navegação Pedagógica


🎯 Nível 1: Fundamentos

Problema 10.1 — Endpoint Assíncrono no FastAPI com Injeção de Dependência

Contexto: Crie um endpoint @app.get("/items") no FastAPI que receba a sessão do banco assíncrono via db: AsyncSession = Depends(get_db) e retorne uma lista de itens em formato JSON.

Resultado Esperado

from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List

app = FastAPI()

@app.get("/items", response_model=List[ItemResponse])
async def listar_itens(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(ItemModel))
    return result.scalars().all()

📤 Instruções de Entrega (Microsoft Teams)

  1. Salve o arquivo como: Atividade_10_1_SeuNome.py
  2. Envie na tarefa: Atividade Cap 10 - Stack FastAPI e Vue 3
🔑 Gabarito de Código & Solução Comentada
O sistema de dependências do FastAPI gerencia o ciclo de vida da conexão com o banco abrindo e fechando a sessão automaticamente.

🎯 Nível 2: Prática

Problema 10.2 — Store do Pinia no Vue 3 com TypeScript

Crie uma Store Pinia useProdutosStore com TypeScript utilizando a sintaxe de setup (defineStore('produtos', () => { ... })) contendo o estado reativo produtos, a propriedade computada totalProdutos e a action assíncrona carregarProdutos().

🔑 Gabarito de Código & Solução Comentada
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import axios from "axios";

export interface Produto { id: number; nome: string; preco: number; }

export const useProdutosStore = defineStore("produtos", () => {
  const produtos = ref<Produto[]>([]);
  const carregando = ref(false);

  const totalProdutos = computed(() => produtos.value.length);

  async function carregarProdutos() {
    carregando.value = true;
    try {
      const response = await axios.get<Produto[]>("/api/produtos");
      produtos.value = response.data;
    } finally {
      carregando.value = false;
    }
  }

  return { produtos, carregando, totalProdutos, carregarProdutos };
});

🎯 Nível 3: Integração

Problema 10.3 — Componente Vue 3 SFC com Script Setup e Consumo de API

Implemente um componente Vue 3 (Catalogo.vue) utilizando <script setup lang="ts"> que utilize a store useProdutosStore, renderize o catálogo com formatação de moeda brasileira e exiba um indicador de carregamento enquanto a requisição estiver ativa.

🔑 Gabarito de Código & Solução Comentada
<script setup lang="ts">
import { onMounted } from "vue";
import { useProdutosStore } from "../stores/produtos";

const store = useProdutosStore();

onMounted(() => {
  store.carregarProdutos();
});
</script>

<template>
  <div class="catalogo-container">
    <h2>Catálogo de Produtos ({{ store.totalProdutos }})</h2>
    
    <div v-if="store.carregando" class="spinner">Carregando catálogo...</div>
    
    <div v-else class="grid">
      <div v-for="item in store.produtos" :key="item.id" class="card">
        <h3>{{ item.nome }}</h3>
        <p class="preco">R$ {{ item.preco.toFixed(2) }}</p>
      </div>
    </div>
  </div>
</template>

🎯 Nível 4: Desafio

Problema 10.4 — Integração Full Stack com Autenticação OAuth2 / JWT no FastAPI e Vue

Implemente o endpoint /auth/login no FastAPI utilizando OAuth2PasswordRequestForm que retorne o token de acesso (access_token), e no Vue 3 configure um interceptor do Axios que anexe automaticamente o header Authorization: Bearer <token> em todas as requisições subsequentes.

🔑 Gabarito de Código & Solução Comentada
# Backend FastAPI:
from fastapi.security import OAuth2PasswordRequestForm

@app.post("/auth/login")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    usuario = await autenticar_usuario(form_data.username, form_data.password)
    if not usuario:
        raise HTTPException(status_code=401, detail="Credenciais inválidas")
    token = criar_jwt_token(usuario.id)
    return {"access_token": token, "token_type": "bearer"}
// Frontend Vue 3 (Axios Interceptor):
import axios from "axios";

axios.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});