Skip to content

Manual Setup

Get a FraiseQL GraphQL API running with your own database. This guide assumes you have a PostgreSQL database with tables already set up. For the full workflow including database setup with 🍯 Confiture, see Your First API.

  • PostgreSQL database with tables created
  • Your preferred schema language installed (Python 3.10+, Node.js 18+, or Go 1.21+)

FraiseQL is a Rust binary. Install it once, use it from any project regardless of language.

Terminal window
# Install script (macOS / Linux)
curl -fsSL https://fraiseql.dev/install.sh | sh
# Homebrew (macOS)
brew install fraiseql
# From source (requires Rust toolchain)
cargo install fraiseql

Verify:

Terminal window
fraiseql --version

The schema SDK lets you define GraphQL types in your preferred language using decorators or structs.

Terminal window
uv add fraiseql
# or: pip install fraiseql

Create SQL views that follow the .data JSONB pattern. Each view has an id column and a data column:

db/schema/02_read/v_user.sql
CREATE VIEW v_user AS
SELECT
u.id,
jsonb_build_object(
'id', u.id::text,
'name', u.name,
'email', u.email,
'created_at', u.created_at
) AS data
FROM tb_user u;
db/schema/02_read/v_post.sql
CREATE VIEW v_post AS
SELECT
p.id,
jsonb_build_object(
'id', p.id::text,
'title', p.title,
'content', p.content,
'author', vu.data
) AS data
FROM tb_post p
JOIN tb_user u ON u.pk_user = p.fk_user
JOIN v_user vu ON vu.id = u.id;

Apply these views to your database (or use confiture build — see 🍯 Confiture).

Create a file called schema.py:

schema.py
import fraiseql
from fraiseql.scalars import ID, Email, DateTime
@fraiseql.type
class User:
"""A user in the system."""
id: ID
name: str
email: Email
created_at: DateTime
@fraiseql.type
class Post:
"""A blog post."""
id: ID
title: str
content: str
author: User

Create fraiseql.toml with your database connection string:

fraiseql.toml
[project]
name = "quickstart"
version = "0.1.0"
database_target = "postgresql"
[database]
url = "postgresql://localhost:5432/mydb"
[server]
port = 8080
host = "127.0.0.1"
Terminal window
# Compile the mapping between your types and SQL views
fraiseql compile
# Start the server
fraiseql run

You should see:

✓ Compiled 2 types → mapped to SQL views
✓ Built query executor
→ GraphQL API running at http://localhost:8080/graphql

Your API is now also available via REST and gRPC alongside GraphQL. REST endpoints are served at /rest/v1/ by default when you add rest_path/rest_method annotations to your schema operations. The default path is /rest/v1. Configure with [rest] path to use /api or any other value. gRPC is served on the same port via HTTP/2 content-type negotiation.

To expose an operation via REST, add transport annotations to your query decorator:

@fraiseql.query(
sql_source="v_post",
rest_path="/posts",
rest_method="GET",
)
def posts(limit: int = 10) -> list[Post]: ...

With that annotation in place, both of the following work simultaneously:

Terminal window
# GraphQL (always available)
curl -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ posts { id title } }"}'
# REST (available when rest_path annotation is present)
curl http://localhost:8080/rest/v1/posts

Open your browser to http://localhost:8080/graphql or use curl:

Terminal window
curl -s -X POST http://localhost:8080/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ users { id name email } }"}' | jq .

Response:

{
"data": {
"users": [
{
"id": "1",
"name": "Alice",
"email": "alice@example.com"
}
]
}
}

If a query references a field that doesn’t exist, FraiseQL returns a structured error:

{
"errors": [
{
"message": "Cannot query field 'phone' on type 'User'",
"locations": [{ "line": 1, "column": 28 }]
}
]
}
  1. SQL views created — You wrote views following the .data JSONB pattern
  2. Schema compiled — FraiseQL mapped your GraphQL types to those SQL views
  3. Query executor built — Pre-compiled query paths for each type
  4. API served — Every GraphQL query resolves to a single SQL statement

No resolvers. No N+1 problems. No generated SQL you can’t read.

Query the same schema — users, posts, comments, tags — against a live FraiseQL server backed by real PostgreSQL. No account required.

FraiseQL Demo API

Try the schema from this guide against a live server.

Loading Apollo Sandbox...

This sandbox uses Apollo Sandbox (the same GraphQL IDE as fraiseql serve). Your queries execute against the endpoint below. No data is sent to Apollo. Learn more about privacy →