Example — Full-Stack App

A full-stack app with a Next.js frontend, Express API, and PostgreSQL database.

Project structure

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="Add a todo..."
          style={{ flex: 1, padding: 8 }}
        />
        <button onClick={addTodo}>Add</button>
      </div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </main>
  );
}

Console configuration

Configure the API and web apps in the console with GitHub source and a managed PostgreSQL database.

Deploy

  1. Push the code to a GitHub repository.
  2. In the console, choose Project Source as GitHub Repository.
  3. Select the main branch and create the project.
  4. Push to update the app automatically.

Expected behavior

  1. Open your app URL.
  2. Type a todo and click "Add".
  3. It saves to PostgreSQL and appears in the list.
  4. Refresh — data persists.

Next steps