Exemplo — Aplicação Full-Stack

Uma aplicação full-stack com frontend em Next.js, API em Express e banco PostgreSQL.

Estrutura do projeto

fullstack-app/
├── api/
│   ├── index.js
│   └── package.json
├── web/
│   ├── app/
│   │   └── page.tsx
│   └── package.json
└── .gitignore

API

api/package.json

{
  "name": "api",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.0",
    "pg": "^8.11.0"
  }
}

api/index.js

const express = require('express');
const { Pool } = require('pg');

const app = express();
app.use(express.json());

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

app.get('/todos', async (req, res) => {
  const { rows } = await pool.query('SELECT * FROM todos ORDER BY created_at DESC');
  res.json(rows);
});

app.post('/todos', async (req, res) => {
  const { text } = req.body;
  const { rows } = await pool.query(
    'INSERT INTO todos (text) VALUES ($1) RETURNING *',
    [text]
  );
  res.status(201).json(rows[0]);
});

app.listen(process.env.PORT || 3000, '0.0.0.0');

Frontend

web/app/page.tsx

'use client';

import { useState, useEffect } from 'react';

interface Todo {
  id: number;
  text: string;
}

export default function Home() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [text, setText] = useState('');

  useEffect(() => {
    fetch('/api/todos')
      .then(r => r.json())
      .then(setTodos);
  }, []);

  const addTodo = async () => {
    const res = await fetch('/api/todos', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text }),
    });
    const todo = await res.json();
    setTodos(prev => [todo, ...prev]);
    setText('');
  };

  return (
    <main style={{ padding: 20, maxWidth: 600, margin: '0 auto' }}>
      <h1>Todos</h1>
      <div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
        <input
          value={text}
          onChange={e => setText(e.target.value)}
          placeholder="Adicione uma tarefa..."
          style={{ flex: 1, padding: 8 }}
        />
        <button onClick={addTodo}>Adicionar</button>
      </div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </main>
  );
}

Configuração no console

Configure os apps API e web no console com origem GitHub e um banco PostgreSQL gerenciado.

Publicação

  1. Envie o código para um repositório GitHub.
  2. No console, escolha Origem de Projeto como Repositório GitHub.
  3. Selecione a branch principal e crie o projeto.
  4. Faça push para atualizar a aplicação automaticamente.

Comportamento esperado

  1. Abra a URL da aplicação.
  2. Digite uma tarefa e clique em "Adicionar".
  3. Ela é salva no PostgreSQL e aparece na lista.
  4. Atualize a página: os dados permanecem.

Próximos passos