All skills
firebase avatar

/firebase-data-connect-basics

@c2630ec official
by firebasefirebase/agent-skills461 stars
102

Builds and deploys Firebase SQL Connect (aka Firebase Data Connect) backends with PostgreSQL securely. Use when designing schemas with tables and relations, writing authorized queries and mutations, configuring real-time data updates, or generating type-safe SDKs. Use when you need a relational database with Firebase, or when the user mentions SQL Connect or Data Connect.

  • 17 files
  • 122.7 KB
  • Updated last week
  • GitHub

Use this Skill: https://skilld.dev/gh/firebase/agent-skills/firebase-data-connect-basics

This session only. Nothing lands on disk.

referenceoperations.md

≈2.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Operations Reference

Contents


Generated Fields

SQL Connect auto-generates fields for each @table type:

Generated Field Purpose Example
movie(id: UUID, key: Key, first: Row) Get single record movie(id: $id) or movie(first: {where: ...})
movies(where: ..., orderBy: ..., limit: ..., offset: ..., distinct: ..., having: ...) List/filter records movies(where: {...})
movie_insert(data: ...) Create record Returns key
movie_insertMany(data: [...]) Bulk create Returns keys
movie_update(id: ..., data: ...) Update by ID Returns key or null
movie_updateMany(where: ..., data: ...) Bulk update Returns count
movie_upsert(data: ...) Insert or update Returns key
movie_delete(id: ...) Delete by ID Returns key or null
movie_deleteMany(where: ...) Bulk delete Returns count

Relation Fields

For a Post with author: User!:

  • post.author - Navigate to related User
  • user.posts_on_author - Reverse: all Posts by User

For many-to-many via MovieActor:

  • movie.actors_via_MovieActor - Get all actors
  • actor.movies_via_MovieActor - Get all movies

Referencing Generated GraphQL Schema

Do not guess available queries or mutations. Review the generated schema files instead of trying to deduce them from the data model.

  1. Location: .dataconnect/schema/main/ (relative to project root).
  2. Action: Scan this directory for generated files (query.gql, mutation.gql, relation.gql, input.gql) to understand the exact shape of the API and auto-generated types.
  3. Validation: Always run firebase dataconnect:compile to verify operations against the full schema.

Queries

Basic Query

query GetMovie($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    id title genre releaseYear
  }
}

List with Filtering

query ListMovies($genre: String, $minRating: Int) @auth(level: PUBLIC) {
  movies(
    where: {
      genre: { eq: $genre },
      rating: { ge: $minRating }
    },
    orderBy: [{ releaseYear: DESC }, { title: ASC }],
    limit: 20,
    offset: 0
  ) {
    id title genre rating
  }
}

Filter Operators

Operator Description Example
eq Equals { title: { eq: "Matrix" }}
ne Not equals { status: { ne: "deleted" }}
gt, ge Greater than (or equal) { rating: { ge: 4 }}
lt, le Less than (or equal) { releaseYear: { lt: 2000 }}
in In list { genre: { in: ["Action", "Drama"] }}
nin Not in list { status: { nin: ["deleted", "hidden"] }}
isNull Is null check { description: { isNull: true }}
contains String contains { title: { contains: "war" }}
startsWith String starts with { title: { startsWith: "The" }}
endsWith String ends with { email: { endsWith: "@gmail.com" }}
includes Array includes { tags: { includes: "sci-fi" }}

Expression Operators (Compare with Server Values)

Use _expr suffix to compare with server-side values:

query MyPosts @auth(level: USER) {
  posts(where: { authorUid: { eq_expr: "auth.uid" }}) {
    id title
  }
}

query RecentPosts @auth(level: PUBLIC) {
  posts(where: { publishedAt: { lt_expr: "request.time" }}) {
    id title
  }
}

Logical Operators

query ComplexFilter($genre: String, $minRating: Int) @auth(level: PUBLIC) {
  movies(where: {
    _or: [
      { genre: { eq: $genre }},
      { rating: { ge: $minRating }}
    ],
    _and: [
      { releaseYear: { ge: 2000 }},
      { status: { ne: "hidden" }}
    ],
    _not: { genre: { eq: "Horror" }}
  }) { id title }
}

Relational Queries

# Navigate relationships
query MovieWithDetails($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    title
    # One-to-one
    metadata: movieMetadata_on_movie { director }
    # One-to-many
    reviews: reviews_on_movie { rating user { name }}
    # Many-to-many
    actors: actors_via_MovieActor { name }
  }
}

# Filter by related data
query MoviesByDirector($director: String!) @auth(level: PUBLIC) {
  movies(where: {
    movieMetadata_on_movie: { director: { eq: $director }}
  }) { id title }
}

# Filter by null relationship (e.g., top-level categories with no parent)
# Use the generated foreign key field (e.g., parentId)
query TopLevelCategories @auth(level: PUBLIC) {
  categories(where: { parentId: { eq: null } }) {
    id
    name
  }
}

Aliases

query CompareRatings($genre: String!) @auth(level: PUBLIC) {
  highRated: movies(where: { genre: { eq: $genre }, rating: { ge: 8 }}) {
    title rating
  }
  lowRated: movies(where: { genre: { eq: $genre }, rating: { lt: 5 }}) {
    title rating
  }
}

Mutations

Create

mutation CreateMovie($title: String!, $genre: String) @auth(level: USER) {
  movie_insert(data: {
    title: $title,
    genre: $genre
  })
}

Create with Server Values

mutation CreatePost($title: String!, $content: String!) @auth(level: USER) {
  post_insert(data: {
    authorUid_expr: "auth.uid",         # Current user
    id_expr: "uuidV4()",                 # Auto-generate UUID
    createdAt_expr: "request.time",      # Server timestamp
    title: $title,
    content: $content
  })
}

Update

mutation UpdateMovie($id: UUID!, $title: String, $genre: String) @auth(level: USER) {
  movie_update(
    id: $id,
    data: {
      title: $title,
      genre: $genre,
      updatedAt_expr: "request.time"
    }
  )
}

Update Operators

mutation IncrementViews($id: UUID!) @auth(level: PUBLIC) {
  movie_update(id: $id, data: {
    viewCount_update: { inc: 1 }
  })
}

mutation AddTag($id: UUID!, $tag: String!) @auth(level: USER) {
  movie_update(id: $id, data: {
    tags_update: { add: [$tag] }  # add, remove, append, prepend
  })
}
Operator Types Description
inc Int, Float, Date, Timestamp Increment value
dec Int, Float, Date, Timestamp Decrement value
add Lists Add items if not present
remove Lists Remove all matching items
append Lists Append to end
prepend Lists Prepend to start

Upsert

mutation UpsertUser($email: String!, $name: String!) @auth(level: USER) {
  user_upsert(data: {
    uid_expr: "auth.uid",
    email: $email,
    name: $name
  })
}

Delete

mutation DeleteMovie($id: UUID!) @auth(level: USER) {
  movie_delete(id: $id)
}

mutation DeleteOldDrafts @auth(level: USER) {
  post_deleteMany(where: {
    status: { eq: "draft" },
    createdAt: { lt_time: { now: true, sub: { days: 30 }}}
  })
}

Filtered Updates/Deletes (User-Owned)

mutation UpdateMyPost($id: UUID!, $content: String!) @auth(level: USER) {
  post_update(
    first: { where: {
      id: { eq: $id },
      authorUid: { eq_expr: "auth.uid" }  # Only own posts
    }},
    data: { content: $content }
  )
}

Key Scalars

Key scalars (Movie_Key, User_Key) are auto-generated types representing primary keys:

# Using key scalar
query GetMovie($key: Movie_Key!) @auth(level: PUBLIC) {
  movie(key: $key) { title }
}

# Variable format
# { "key": { "id": "uuid-here" } }

# Composite key
# { "key": { "movieId": "...", "userId": "..." } }

Key scalars are returned by mutations:

mutation CreateAndFetch($title: String!) @auth(level: USER) {
  key: movie_insert(data: { title: $title })
  # Returns: { "key": { "id": "generated-uuid" } }
}

Multi-Step Operations

@transaction

Ensures atomicity - all steps succeed or all rollback:

mutation CreateUserWithProfile($name: String!, $bio: String!) 
  @auth(level: USER) 
  @transaction {
  # Step 1: Create user
  user_insert(data: {
    uid_expr: "auth.uid",
    name: $name
  })
  # Step 2: Create profile (uses response from step 1)
  userProfile_insert(data: {
    userId_expr: "response.user_insert.uid",
    bio: $bio
  })
}

Using response Binding

Access results from previous steps:

mutation CreateTodoWithItem($listName: String!, $itemText: String!) 
  @auth(level: USER) 
  @transaction {
  todoList_insert(data: {
    id_expr: "uuidV4()",
    name: $listName
  })
  todoItem_insert(data: {
    listId_expr: "response.todoList_insert.id",  # From previous step
    text: $itemText
  })
}

Embedded Queries

Run queries within mutations for validation:

mutation AddToPublicList($listId: UUID!, $item: String!)
  @auth(level: USER)
  @transaction {
  # Step 1: Verify list exists and is public
  query @redact {
    todoList(id: $listId) @check(expr: "this != null", message: "List not found") {
      isPublic @check(expr: "this == true", message: "List is not public")
    }
  }
  # Step 2: Add item
  todoItem_insert(data: { listId: $listId, text: $item })
}

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at c2630ec. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last week
metadata
{
  "category": "Databases"
}

README badge

README badge for firebase/agent-skills/firebase-data-connect-basics