Quick Start
Get a FraiseQL GraphQL API running in 5 minutes. 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.
Prerequisites
Section titled “Prerequisites”- PostgreSQL database with tables created
- Your preferred schema language installed (Python 3.10+, Node.js 18+, or Go 1.21+)
Step 1: Install FraiseQL
Section titled “Step 1: Install FraiseQL”FraiseQL is a Rust binary. Install it once, use it from any project regardless of language.
# Install script (macOS / Linux)curl -fsSL https://fraiseql.dev/install.sh | sh
# Homebrew (macOS)brew install fraiseql
# From source (requires Rust toolchain)cargo install fraiseqlVerify:
fraiseql --versionStep 1b: Install the Schema SDK
Section titled “Step 1b: Install the Schema SDK”The schema SDK lets you define GraphQL types in your preferred language using decorators or structs.
uv add fraiseql# or: pip install fraiseqlnpm install fraiseql# or: pnpm add fraiseqlgo get github.com/fraiseql/fraiseql-goStep 2: Write Your SQL Views
Section titled “Step 2: Write Your SQL Views”Create SQL views that follow the .data JSONB pattern. Each view has an id column and a data column:
CREATE VIEW v_user ASSELECT u.id, jsonb_build_object( 'id', u.id::text, 'name', u.name, 'email', u.email, 'created_at', u.created_at ) AS dataFROM tb_user u;CREATE VIEW v_post ASSELECT p.id, jsonb_build_object( 'id', p.id::text, 'title', p.title, 'content', p.content, 'author', vu.data ) AS dataFROM tb_post pJOIN tb_user u ON u.pk_user = p.fk_userJOIN v_user vu ON vu.id = u.id;CREATE VIEW v_user ASSELECT u.id, JSON_OBJECT( 'id', u.id, 'name', u.name, 'email', u.email, 'created_at', u.created_at ) AS dataFROM tb_user u;CREATE VIEW v_post ASSELECT p.id, JSON_OBJECT( 'id', p.id, 'title', p.title, 'content', p.content, 'author', vu.data ) AS dataFROM tb_post pJOIN tb_user u ON u.pk_user = p.fk_userJOIN v_user vu ON vu.id = u.id;CREATE VIEW v_user ASSELECT u.id, json_object( 'id', u.id, 'name', u.name, 'email', u.email, 'created_at', u.created_at ) AS dataFROM tb_user u;CREATE VIEW v_post ASSELECT p.id, json_object( 'id', p.id, 'title', p.title, 'content', p.content, 'author', vu.data ) AS dataFROM tb_post pJOIN tb_user u ON u.pk_user = p.fk_userJOIN v_user vu ON vu.id = u.id;CREATE VIEW dbo.v_userWITH SCHEMABINDING ASSELECT u.id, ( SELECT u.id, u.name, u.email, u.created_at FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) AS dataFROM dbo.tb_user u;CREATE VIEW dbo.v_postWITH SCHEMABINDING ASSELECT p.id, ( SELECT p.id, p.title, p.content, vu.data AS author FOR JSON PATH, WITHOUT_ARRAY_WRAPPER ) AS dataFROM dbo.tb_post pJOIN dbo.tb_user u ON u.pk_user = p.fk_userJOIN dbo.v_user vu ON vu.id = u.id;Apply these views to your database (or use confiture build — see 🍯 Confiture).
Step 3: Define Your GraphQL Schema
Section titled “Step 3: Define Your GraphQL Schema”Create a file called schema.py:
import fraiseqlfrom fraiseql.scalars import ID, Email, DateTime
@fraiseql.typeclass User: """A user in the system.""" id: ID name: str email: Email created_at: DateTime
@fraiseql.typeclass Post: """A blog post.""" id: ID title: str content: str author: User published: boolCreate a file called schema.ts:
import { type } from 'fraiseql';import { ID, Email, DateTime } from 'fraiseql/scalars';
@type()class User { /** A user in the system. */ id: ID; name: string; email: Email; createdAt: DateTime;}
@type()class Post { /** A blog post. */ id: ID; title: string; content: string; author: User; published: boolean;}Create a file called schema.go:
package schema
import "github.com/fraiseql/fraiseql-go/scalars"
// User represents a user in the system.type User struct { ID scalars.ID `fraiseql:"type"` Name string Email scalars.Email CreatedAt scalars.DateTime}
// Post represents a blog post.type Post struct { ID scalars.ID `fraiseql:"type"` Title string Content string Author User Published bool}Step 4: Configure Your Project
Section titled “Step 4: Configure Your Project”Create fraiseql.toml with your database connection string:
[project]name = "quickstart"version = "0.1.0"
[database]type = "postgresql"url = "postgresql://localhost:5432/mydb"
[server]port = 8080host = "127.0.0.1"[project]name = "quickstart"version = "0.1.0"
[database]type = "mysql"url = "mysql://localhost:3306/mydb"
[server]port = 8080host = "127.0.0.1"[project]name = "quickstart"version = "0.1.0"
[database]type = "sqlite"url = "sqlite://./mydb.db"
[server]port = 8080host = "127.0.0.1"[project]name = "quickstart"version = "0.1.0"
[database]type = "sqlserver"url = "sqlserver://localhost:1433;Database=mydb;Trusted_Connection=true"
[server]port = 8080host = "127.0.0.1"Step 5: Compile and Serve
Section titled “Step 5: Compile and Serve”# Compile the mapping between your types and SQL viewsfraiseql compile
# Start the serverfraiseql runYou should see:
✓ Compiled 2 types → mapped to SQL views✓ Built query executor→ GraphQL API running at http://localhost:8080/graphqlStep 6: Query Your API
Section titled “Step 6: Query Your API”Open your browser to http://localhost:8080/graphql or use curl:
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 }] } ]}What Just Happened?
Section titled “What Just Happened?”- SQL views created — You wrote views following the
.dataJSONB pattern - Schema compiled — FraiseQL mapped your GraphQL types to those SQL views
- Query executor built — Pre-compiled query paths for each type
- API served — Every GraphQL query resolves to a single SQL statement
No resolvers. No N+1 problems. No generated SQL you can’t read.
Try It Without Installing
Section titled “Try It Without Installing”Query the same schema — users, posts, comments, tags — against a live FraiseQL server backed by real PostgreSQL. No account required.
Next Steps
Section titled “Next Steps”- Your First API — Build a complete blog API with 🍯 Confiture
- Developer-Owned SQL — Why you write the views
- TOML Configuration — All configuration options
- Schema Definition — Advanced schema patterns