Intermediate30 min

Learn to build complete full-stack applications in Bolt.new with frontend, backend logic, database integration, and API routes.

Building Full-Stack Apps

Bolt.new's WebContainer technology runs Node.js in the browser, enabling true full-stack development. You can build frontend interfaces, API routes, and connect to databases—all from a single prompt.

What "Full-Stack" Means in Bolt

In Bolt.new, a full-stack application includes:

  • Frontend: React, Vue, or other UI frameworks
  • Backend: Server-side code running in WebContainers
  • API Routes: HTTP endpoints for data operations
  • Data Persistence: Database connections or file-based storage
  • Authentication: User login and session management

Framework Options

Next.js (Recommended)

Terminal
Create a Next.js 14 app with the App Router.
Include:
- Homepage
- About page
- Contact form with API route
Use Tailwind CSS for styling.

Why Next.js works well:

  • Built-in API routes
  • Server-side rendering
  • App Router for modern patterns
  • Strong AI training data

Remix

Terminal
Create a Remix app with a simple blog.
Include:
- List of posts
- Individual post pages
- Form to add new posts with action handlers

Express + React

Terminal
Create a project with:
- Express backend on port 3001
- React frontend on port 5173
- API routes for a todo list
- Proxy configured for development

Building a Complete Application

Let's build a full-stack note-taking app to understand the patterns.

Step 1: Initial Setup

Terminal
Create a Next.js 14 app for a note-taking application.

Features:
- List of notes with titles and previews
- Create new notes with title and content
- Edit existing notes
- Delete notes
- Search/filter notes

Tech stack:
- Next.js App Router
- Tailwind CSS
- Server Actions for data mutations
- SQLite database with better-sqlite3

Start with the basic project structure.

Step 2: Database Setup

Bolt will create database code. Verify the setup:

Terminal
// src/lib/db.js
import Database from 'better-sqlite3'

const db = new Database('notes.db')

// Initialize table
db.exec(`
  CREATE TABLE IF NOT EXISTS notes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    content TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`)

export default db

Step 3: API Routes

Create endpoints for CRUD operations:

Terminal
Create API routes for the notes app:
- GET /api/notes - List all notes
- GET /api/notes/[id] - Get single note
- POST /api/notes - Create note
- PUT /api/notes/[id] - Update note
- DELETE /api/notes/[id] - Delete note

Include proper error handling and validation.

Generated API route example:

Terminal
// app/api/notes/route.js
import db from '@/lib/db'
import { NextResponse } from 'next/server'

export async function GET() {
  try {
    const notes = db.prepare('SELECT * FROM notes ORDER BY updated_at DESC').all()
    return NextResponse.json(notes)
  } catch (error) {
    return NextResponse.json({ error: 'Failed to fetch notes' }, { status: 500 })
  }
}

export async function POST(request) {
  try {
    const { title, content } = await request.json()

    if (!title?.trim()) {
      return NextResponse.json({ error: 'Title is required' }, { status: 400 })
    }

    const result = db.prepare(
      'INSERT INTO notes (title, content) VALUES (?, ?)'
    ).run(title, content || '')

    const note = db.prepare('SELECT * FROM notes WHERE id = ?').get(result.lastInsertRowid)

    return NextResponse.json(note, { status: 201 })
  } catch (error) {
    return NextResponse.json({ error: 'Failed to create note' }, { status: 500 })
  }
}

Step 4: Frontend Components

Create the UI:

Terminal
Build the frontend for the notes app:

1. NotesLayout - Sidebar with note list, main area for content
2. NotesList - Displays all notes with title and preview
3. NoteEditor - Form to create/edit notes with title and content
4. SearchBar - Filter notes by title

Use React Query for data fetching and cache management.
Style with Tailwind using a clean, minimal design.

Step 5: Connect Frontend to Backend

Terminal
Wire up the components to the API:
- NotesList should fetch from GET /api/notes
- Clicking a note loads it in the editor
- Save button calls POST or PUT depending on edit/create
- Delete button calls DELETE with confirmation
- Search filters results client-side

Add loading states and error handling.

Working with External APIs

Connecting to Third-Party Services

Terminal
Add weather display to the app.
Fetch current weather from OpenWeatherMap API.
Show temperature and conditions in the header.

API key should be stored in environment variables.

Bolt creates:

Terminal
// app/api/weather/route.js
export async function GET() {
  const apiKey = process.env.OPENWEATHER_API_KEY
  const city = 'New York'

  const response = await fetch(
    `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
  )

  if (!response.ok) {
    return NextResponse.json({ error: 'Weather fetch failed' }, { status: 500 })
  }

  const data = await response.json()
  return NextResponse.json({
    temp: data.main.temp,
    conditions: data.weather[0].description
  })
}

Environment Variables

Set up secrets properly:

Terminal
Create a .env.local file with these variables:
- DATABASE_URL
- OPENWEATHER_API_KEY

Show me how to access them in the API routes.

Important: Don't commit real API keys. Use placeholders:

Terminal
# .env.local
OPENWEATHER_API_KEY=your_api_key_here

Authentication Patterns

Basic Session Auth

Terminal
Add user authentication:
- Sign up page with email/password
- Login page
- Session management with cookies
- Protected routes that require login
- Logout functionality

Use bcrypt for password hashing.
Store users in SQLite.

Auth Flow Example

Terminal
// app/api/auth/login/route.js
import bcrypt from 'bcrypt'
import { cookies } from 'next/headers'
import db from '@/lib/db'

export async function POST(request) {
  const { email, password } = await request.json()

  const user = db.prepare('SELECT * FROM users WHERE email = ?').get(email)

  if (!user || !bcrypt.compareSync(password, user.password)) {
    return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 })
  }

  // Create session
  const sessionId = crypto.randomUUID()
  db.prepare('INSERT INTO sessions (id, user_id) VALUES (?, ?)').run(sessionId, user.id)

  cookies().set('session', sessionId, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 7 // 1 week
  })

  return NextResponse.json({ success: true })
}

Real-Time Features

Server-Sent Events

Terminal
Add real-time updates to the notes app.
When one user creates or edits a note, other users see it immediately.
Use Server-Sent Events for the real-time connection.

WebSocket Alternative

Terminal
Set up a WebSocket server for real-time chat.
Include:
- Connection management
- Room-based messaging
- User presence indicators

Error Handling Patterns

API Error Handling

Terminal
Improve error handling throughout the API:
- Consistent error response format
- Specific error messages
- Appropriate HTTP status codes
- Error logging

Create an error utility function to standardize responses.

Standard error format:

Terminal
// lib/errors.js
export function apiError(message, status = 500) {
  return NextResponse.json(
    { error: message, timestamp: new Date().toISOString() },
    { status }
  )
}

// Usage in routes
import { apiError } from '@/lib/errors'

export async function GET() {
  try {
    // ... operation
  } catch (error) {
    console.error('Notes fetch error:', error)
    return apiError('Failed to fetch notes', 500)
  }
}

Frontend Error Handling

Terminal
Add error boundaries and user-friendly error states:
- Error boundary component for React errors
- Toast notifications for API errors
- Retry buttons for failed requests
- Graceful degradation when offline

Database Patterns

SQLite for Simplicity

SQLite works well in Bolt's WebContainer:

Terminal
-- Schema for a blog
CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  content TEXT,
  published BOOLEAN DEFAULT false,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE comments (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE,
  author TEXT NOT NULL,
  content TEXT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Data Relationships

Terminal
Add categories to the notes app:
- A note can belong to one category
- Categories have name and color
- Show category badge on note cards
- Filter notes by category

Testing Your Full-Stack App

Manual Testing

Test each feature:

  1. Create: Add new items
  2. Read: View lists and details
  3. Update: Edit existing items
  4. Delete: Remove items
  5. Edge cases: Empty states, validation

Using the Terminal

Terminal
# Test API directly with curl
curl http://localhost:3000/api/notes

# Check database
sqlite3 notes.db "SELECT * FROM notes;"

Common Issues

CORS Errors

Terminal
I'm getting CORS errors when calling the API from the frontend.
Can you add proper CORS headers?

Database Not Persisting

Terminal
The database resets when I refresh. Is there a way
to persist data between sessions?

API Not Responding

Terminal
The API route returns 404. Check that the file path
matches the expected route structure.

Performance Optimization

Caching

Terminal
Add caching to the notes API:
- Cache note listings for 60 seconds
- Invalidate cache when notes are created/updated/deleted
- Use React Query's stale-while-revalidate pattern on frontend

Loading States

Terminal
Improve perceived performance:
- Skeleton loaders for the notes list
- Optimistic updates when saving
- Debounced search input
- Lazy loading for long content

Summary

Building full-stack apps in Bolt.new:

  • Choose the right framework: Next.js works best for full-stack
  • Use SQLite for simple data: Works well in WebContainers
  • Create proper API routes: RESTful patterns with error handling
  • Connect frontend carefully: React Query or SWR for data fetching
  • Handle errors gracefully: Both API and UI error states
  • Test thoroughly: Manual testing of all CRUD operations

Next Steps

Your app is built—now let's learn how to deploy it so others can use it.

Mark this lesson as complete to track your progress