---
title: 5-Minute Quick Start
description: Get started with FraiseQL in 5 minutes - create a complete GraphQL note-taking API
tags:
  - quickstart
  - tutorial
  - getting started
  - first app
  - notes API
  - beginner
---

# Quick Start Guide

Get started with FraiseQL in 5 minutes! This guide will walk you through creating a simple note-taking GraphQL API.

## Prerequisites

- **Python 3.13+** (required for FraiseQL's Rust pipeline and advanced features)
- **PostgreSQL 13+**

## Step 1: Install FraiseQL

```bash
pip install fraiseql[all]
```

## Step 2: Create Database

Create a PostgreSQL database for your notes:

```bash
createdb quickstart_notes
```

## Step 3: Set Up Database Schema

Create a file called `schema.sql` with this content:

```sql
-- Simple notes table with trinity pattern
CREATE TABLE tb_note (
    pk_note INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,  -- Internal fast joins
    id UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),         -- Public API
    identifier TEXT UNIQUE,                                     -- Optional human-readable identifier
    title VARCHAR(200) NOT NULL,
    content TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Notes view for GraphQL queries
CREATE VIEW v_note AS
SELECT
    id,
    jsonb_build_object(
        'id', id,                            -- UUID for GraphQL API
        'title', title,
        'content', content,
        'created_at', created_at
    ) AS data
FROM tb_note;

-- SQL function for creating notes (CQRS pattern)
CREATE OR REPLACE FUNCTION fn_create_note(input jsonb)
RETURNS jsonb AS $$
DECLARE
    new_id uuid;
BEGIN
    INSERT INTO tb_note (title, content)
    VALUES (input->>'title', input->>'content')
    RETURNING id INTO new_id;

    RETURN jsonb_build_object('success', true, 'id', new_id);
END;
$$ LANGUAGE plpgsql;

-- Sample data
INSERT INTO tb_note (title, content) VALUES
    ('Welcome to FraiseQL', 'This is your first note!'),
    ('Type-safe queries', 'Automatic GraphQL schema from PostgreSQL views'),
    ('Database-first design', 'Views compose data for optimal performance');
```

Run the schema:

```bash
psql quickstart_notes < schema.sql
```

## Step 4: Create Your GraphQL API

Create a file called `app.py` with this complete code:

```python
from datetime import datetime
import uvicorn
import fraiseql
from fraiseql.fastapi import create_fraiseql_app
from fraiseql.types import ID  # Use ID for entity identifiers

# Define GraphQL types
@fraiseql.type(sql_source="v_note", jsonb_column="data")
class Note:
    """A simple note with title and content.

    Fields:
        id: Unique note identifier
        title: Note title (max 200 characters)
        content: Note content in plain text
        created_at: When the note was created
    """
    id: ID  # External UUID identifier (Trinity pattern)
    title: str
    content: str | None
    created_at: datetime

# Define input types
@fraiseql.input
class CreateNoteInput:
    """Input for creating a new note."""
    title: str
    content: str | None = None

# Define success/error types
@fraiseql.success
class CreateNoteSuccess:
    """Success response for note creation."""
    note: Note
    # Note: @success auto-injects: status, message, updated_fields, id

@fraiseql.error
class ValidationError:
    """Validation error."""
    message: str
    code: str = "VALIDATION_ERROR"

# Queries
@fraiseql.query
async def notes(info) -> list[Note]:
    """Get all notes."""
    db = info.context["db"]
    # field_name auto-inferred from function name "notes"
    return await db.find("v_note", order_by=[("created_at", "DESC")])

@fraiseql.query
async def note(info, id: ID) -> Note | None:
    """Get a single note by ID."""
    db = info.context["db"]
    # field_name auto-inferred from function name "note"
    return await db.find_one("v_note", id=id)

# Mutations
@fraiseql.mutation
class CreateNote:
    """Create a new note."""
    input: CreateNoteInput
    success: CreateNoteSuccess
    failure: ValidationError

    async def resolve(self, info) -> CreateNoteSuccess | ValidationError:
        db = info.context["db"]

        try:
            # Call SQL function (CQRS pattern)
            result = await db.execute_function("fn_create_note", {
                "title": self.input.title,
                "content": self.input.content,
            })

            if not result.get("success"):
                return ValidationError(message="Failed to create note")

            # Fetch the created note via Rust pipeline
            note = await db.find_one("v_note", id=result["id"])
            return CreateNoteSuccess(note=note)

        except Exception as e:
            return ValidationError(message=f"Failed to create note: {e!s}")

# Create the app
QUICKSTART_TYPES = [Note]
QUICKSTART_QUERIES = [notes, note]
QUICKSTART_MUTATIONS = [CreateNote]

if __name__ == "__main__":
    import os

    # Database URL (override with DATABASE_URL environment variable)
    database_url = os.getenv("DATABASE_URL", "postgresql://localhost/quickstart_notes")

    app = create_fraiseql_app(
        database_url=database_url,
        types=QUICKSTART_TYPES,
        queries=QUICKSTART_QUERIES,
        mutations=QUICKSTART_MUTATIONS,
        title="Notes API",
        description="Simple note-taking GraphQL API",
        production=False,  # Enable GraphQL playground
    )

    print("🚀 Notes API running at http://localhost:8000/graphql")
    print("📖 GraphQL Playground: http://localhost:8000/graphql")

    uvicorn.run(app, host="0.0.0.0", port=8000)
```

## Step 5: Run Your API

```bash
python app.py
```

Visit `http://localhost:8000/graphql` to open the GraphQL Playground!

## Step 6: Try Your First Queries

### Get all notes:
```graphql
query {
  notes {
    id
    title
    content
    createdAt
  }
}
```

### Get a specific note:
```graphql
query {
  note(id: "550e8400-e29b-41d4-a716-446655440000") {
    id
    title
    content
    createdAt
  }
}
```

**Note**: Replace the UUID with an actual ID from your database. You can get IDs from the `notes` query above.

### Create a new note:
```graphql
mutation {
  createNote(input: { title: "My New Note", content: "GraphQL mutations made simple" }) {
    ... on CreateNoteSuccess {
      note {
        id
        title
        content
        createdAt
      }
      message
    }
    ... on ValidationError {
      message
      code
    }
  }
}
```

## What Just Happened?

🎉 **Congratulations!** You just built a complete GraphQL API with:

- **Database Schema**: PostgreSQL table and JSONB view
- **GraphQL Types**: Note type with proper typing
- **Queries**: Get all notes and get note by ID
- **Mutations**: Create new notes with success/failure handling
- **FastAPI Integration**: Ready-to-deploy web server

## 💡 Best Practices

### Import Style

**Always use namespaced imports to avoid shadowing Python builtins:**

```python
# ✅ RECOMMENDED (safe, clear)
import fraiseql

@fraiseql.type(sql_source="v_user")
class User: ...

@fraiseql.input
class CreateUserInput: ...
```

**Alternative with explicit imports:**

```python
# ✅ SAFE (explicit names)
from fraiseql import fraise_type, fraise_input

@fraise_type(sql_source="v_user")
class User: ...

@fraise_input
class CreateUserInput: ...
```

**⚠️ AVOID - Shadows Python builtins:**

```python
# ❌ DANGEROUS - shadows builtin type() and input()
from fraiseql import type, input

@type  # Now you can't use Python's type() function!
class User: ...
```

### Auto-Inference Magic

FraiseQL automatically infers parameters to reduce boilerplate:

```python
@fraiseql.query
async def users(info) -> list[User]:
    db = info.context["db"]
    # field_name auto-inferred from function name "users"
    # info parameter auto-injected from GraphQL context
    return await db.find("v_user")
```

The GraphQL response will automatically use the correct field name:
```json
{
  "data": {
    "users": [...]  // ← Matches function name
  }
}
```

## Next Steps

### Continue Learning

- **[First Hour Guide](first-hour.md)** - Progressive hands-on tutorial
- **[Core Concepts & Glossary](../core/concepts-glossary.md)** - Understand [CQRS](../core/concepts-glossary.md#cqrs), [Trinity pattern](../core/concepts-glossary.md#trinity-identifiers), and [JSONB views](../core/concepts-glossary.md#jsonb-view-pattern)
- **[Auto-Inference Guide](../core/auto-inference.md)** - Learn how FraiseQL reduces boilerplate

### Decision Support

- **[Decision Matrices](../guides/decision-matrices.md)** - Choose the right patterns (views vs projection tables, mutation patterns, etc.)
- **[Understanding FraiseQL](../guides/understanding-fraiseql.md)** - Architecture and philosophy

### Practical Guides

- **[Blog API Tutorial](../tutorials/blog-api.md)** - Build a complete API from scratch
- **[Troubleshooting](../guides/troubleshooting.md)** - Common issues and solutions
- **[Examples](../../examples/)** - More complete examples

## Need Help?

- [GitHub Discussions](../discussions)
- [Documentation](https://docs.fraiseql.com)
- [Troubleshooting Guide](../guides/troubleshooting.md)

---

Continue with the [First Hour Tutorial](first-hour.md) for hands-on examples.
