π REST vs GraphQL
Escolhendo a arquitetura certa
REST usa endpoints fixos que retornam estruturas predefinidas. GraphQL oferece um unico endpoint com queries flexiveis. A escolha impacta desenvolvimento, performance e manutencao.
Para dashboards, a decisao depende da complexidade. Dashboards simples com dados fixos = REST. Dashboards com muitas views diferentes = GraphQL.
βοΈ Comparacao REST vs GraphQL
| Aspecto | REST | GraphQL |
|---|---|---|
| Overfetching | Comum - recebe tudo | Nunca - pede so o que precisa |
| Underfetching | Precisa de multiplas requests | Uma query resolve |
| Caching | HTTP cache nativo | Requer Apollo/URQL |
| Complexidade | Simples de implementar | Schema, resolvers, types |
| Ideal para | APIs publicas, CRUD simples | Apps complexos, mobile |
π» Codigo: REST vs GraphQL
// 3 requests para montar dashboard
const user = await fetch('/api/users/1');
const orders = await fetch('/api/users/1/orders');
const stats = await fetch('/api/users/1/stats');
// Overfetching: recebe 50 campos
// mas precisa de apenas 5
const { data } = await client.query({
query: gql`
query Dashboard($userId: ID!) {
user(id: $userId) {
name
orders(limit: 5) { total }
stats { revenue, count }
}
}
`,
variables: { userId: 1 }
});
π‘ Dica Pratica
Para dashboards Next.js: Use REST com Route Handlers para casos simples. Para dashboards complexos com muitos componentes buscando dados diferentes, GraphQL com Apollo Client oferece melhor DX e cache automatico.
π TanStack Query / SWR
Server state management
TanStack Query (ex React Query) e SWR sao bibliotecas para gerenciar dados que vem do servidor. Elas abstraem fetching, caching, revalidacao, retries e sincronizacao.
Antes delas, cada projeto reinventava a roda com useEffect + useState. Agora loading, error, data, refetch, cache sao tratados automaticamente.
π― Recursos Principais
Dados ficam em cache e sao reutilizados entre componentes.
Atualiza dados em background quando usuario volta a aba.
Define quanto tempo dados sao considerados frescos.
Retenta automaticamente em caso de falha de rede.
π» Codigo: TanStack Query em Dashboard
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Hook para buscar metricas
function useMetrics(dateRange: string) {
return useQuery({
queryKey: ['metrics', dateRange],
queryFn: () => fetch(`/api/metrics?range=${dateRange}`).then(r => r.json()),
staleTime: 1000 * 60 * 5, // 5 minutos antes de refetch
refetchOnWindowFocus: true, // Atualiza ao voltar a aba
});
}
// Componente usando o hook
function MetricsCard() {
const { data, isLoading, error, refetch } = useMetrics('30d');
if (isLoading) return ;
if (error) return ;
return (
Receita: {data.revenue}
Clientes: {data.customers}
);
}
// Mutation para atualizar dados
function useUpdateMetric() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data) => fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify(data)
}),
onSuccess: () => {
// Invalida cache para refetch
queryClient.invalidateQueries({ queryKey: ['metrics'] });
}
});
}
β Fazer vs β Evitar
- β’ Query keys consistentes e hierarquicas
- β’ staleTime apropriado ao tipo de dado
- β’ Invalidar queries apos mutations
- β’ Prefetch em hover para UX rapida
- β’ useQuery dentro de loops
- β’ Query keys dinamicas sem memoize
- β’ Ignorar estados de loading/error
- β’ refetchOnMount em dados estaticos
β οΈ Error Handling e Retry
Resiliencia de APIs
Error handling robusto e essencial para dashboards enterprise. Falhas de rede, timeouts e erros de API vao acontecer - a questao e como seu app reage.
Um dashboard que mostra tela branca quando a API falha e inutil. Usuarios precisam ver dados stale, fallbacks ou pelo menos mensagens claras.
π Estrategias de Retry
Aumenta o intervalo entre retries exponencialmente.
Para de tentar temporariamente apos muitas falhas.
Mostra dados em cache ou defaults quando falha.
π» Codigo: Error Handling Completo
// Hook com retry e fallback
function useMetricsWithFallback() {
return useQuery({
queryKey: ['metrics'],
queryFn: async () => {
const res = await fetch('/api/metrics');
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json();
},
retry: 3,
retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 30000),
// Fallback: mantem dados antigos em erro
placeholderData: (previousData) => previousData,
// Ou dados estaticos
initialData: { revenue: 0, customers: 0 },
});
}
// Error Boundary para erros de render
function DashboardErrorBoundary({ children }) {
return (
(
Erro ao carregar dashboard
{error.message}
)}
>
{children}
);
}
β³ Loading States e Skeletons
Feedback visual durante carregamento
Skeleton screens mostram a estrutura da UI enquanto dados carregam. Diferente de spinners, skeletons mantem o layout estavel e reduzem a percepcao de espera.
Estudos mostram que usuarios percebem skeletons como 30% mais rapidos que spinners para o mesmo tempo de carga. E a diferenca entre "lento" e "fluido".
ποΈ Exemplo Visual de Skeleton
π» Codigo: Skeleton Component
// Skeleton base reutilizavel
function Skeleton({ className }: { className?: string }) {
return (
);
}
// Skeleton para card de metricas
function MetricCardSkeleton() {
return (
);
}
// Uso com React Suspense
function Dashboard() {
return (
}>
);
}
// Ou com TanStack Query
function MetricCard() {
const { data, isLoading } = useMetrics();
if (isLoading) return ;
return (
{data.label}
{data.value}
);
}
π Data Normalization
Estrutura de dados eficiente
Data normalization e organizar dados aninhados vindos da API em estruturas planas com referencias por ID. Isso evita duplicacao e facilita atualizacoes parciais.
Quando o mesmo usuario aparece em 10 lugares, atualizar o nome dele em um lugar deve refletir em todos. Sem normalizacao, voce precisa atualizar 10 lugares manualmente.
π Antes vs Depois de Normalizar
{
orders: [
{
id: 1,
user: { id: 10, name: 'Ana' },
items: [...]
},
{
id: 2,
user: { id: 10, name: 'Ana' }, // duplicado!
items: [...]
}
]
}
{
users: {
10: { id: 10, name: 'Ana' }
},
orders: {
1: { id: 1, userId: 10, itemIds: [1,2] },
2: { id: 2, userId: 10, itemIds: [3] }
},
orderIds: [1, 2]
}
π» Codigo: Normalizando com normalizr
import { normalize, schema } from 'normalizr';
// Definir schemas
const userSchema = new schema.Entity('users');
const orderSchema = new schema.Entity('orders', {
user: userSchema
});
// Dados da API
const apiResponse = {
orders: [
{ id: 1, user: { id: 10, name: 'Ana' }, total: 100 },
{ id: 2, user: { id: 10, name: 'Ana' }, total: 200 }
]
};
// Normalizar
const normalized = normalize(apiResponse, {
orders: [orderSchema]
});
// Resultado:
// {
// entities: {
// users: { 10: { id: 10, name: 'Ana' } },
// orders: {
// 1: { id: 1, userId: 10, total: 100 },
// 2: { id: 2, userId: 10, total: 200 }
// }
// },
// result: { orders: [1, 2] }
// }
π Real-time e WebSockets
Dados em tempo real
WebSockets permitem comunicacao bidirecional entre cliente e servidor. Diferente de HTTP que e request-response, WebSockets mantΓ©m uma conexao aberta para push de dados.
Para dashboards de monitoramento, dados precisam atualizar em segundos, nao minutos. WebSockets ou Server-Sent Events (SSE) sao essenciais para isso.
βοΈ Polling vs WebSocket vs SSE
Cliente pergunta "tem novidade?" a cada X segundos. Simples mas ineficiente.
Conexao persistente. Cliente e servidor enviam mensagens a qualquer momento.
Servidor envia eventos para cliente. Mais simples que WebSocket, HTTP nativo.
π» Codigo: Real-time com Socket.io
// Cliente React
import { useEffect, useState } from 'react';
import { io } from 'socket.io-client';
function useRealtimeMetrics() {
const [metrics, setMetrics] = useState(null);
const [connected, setConnected] = useState(false);
useEffect(() => {
const socket = io('wss://api.example.com', {
auth: { token: getToken() }
});
socket.on('connect', () => setConnected(true));
socket.on('disconnect', () => setConnected(false));
// Escuta eventos de metricas
socket.on('metrics:update', (data) => {
setMetrics(data);
});
// Subscribe a sala especifica
socket.emit('subscribe', { room: 'dashboard-metrics' });
return () => socket.disconnect();
}, []);
return { metrics, connected };
}
// Uso no componente
function LiveDashboard() {
const { metrics, connected } = useRealtimeMetrics();
return (
{connected ? 'β Ao vivo' : 'β Desconectado'}
{metrics && }
);
}
π‘ Dica Pratica
Para Next.js com Vercel: Use Pusher, Ably ou Supabase Realtime. Vercel serverless nao suporta WebSockets persistentes nativamente. SSE funciona mas com limitacoes. Servicos gerenciados sao a melhor opcao.