Data Connect and GraphQL

If you’ve explored Firebase Data Connect, you’ll find the GraphQL language defines types, mutations, and queries in your application schemas. With Data Connect + GraphQL, you can define your schema and query, and Data Connect generates an API for clients, type-safe client SDKs, auth integration, and tool definitions for gen-AI workflows. In this post, we’ll explore what GraphQL is, why we chose GraphQL, and how to use GraphQL for schemas, queries, and mutations.

What is GraphQL?

According to the official GraphQL documentation: “GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.”

On top of that, GraphQL is database agnostic, meaning you can use GraphQL with any database source used to retrieve data and persist changes, and it’s typically used for remote client-server communications.

You won’t have to worry about this as Firebase Data Connect connects GraphQL to a PostgreSQL database hosted on Cloud SQL, so it’s ready for you to use out-of-the-box without any GraphQL setup required. In other words, Firebase Data Connect is essentially a BaaS that uses GraphQL to interface directly with a PostgreSQL database.

Why GraphQL?

As is often the case in the technology world, Firebase had many choices when designing a SQL database solution. Why did we ultimately decide to use GraphQL for Data Connect?

GraphQL is:

  • terse, which makes code more readable and maintainable as the app and codebase grows
  • strongly typed, for the same benefits as above and additionally helps with generating type-safe SDKs and defining a clear API surface
  • language agnostic, letting us drive the experience across multiple platforms from a single source
  • customizable via directives, which allowed us to design the custom mapping from GraphQL to PostgreSQL
  • supported by a large community, so if you want to learn more or have any questions, you’ll be able to find correct information quickly
  • battle-tested, giving us reassurance that the language is in a stable, usable state without many bugs or issues
  • powerful, enabling complex relational queries, filtering, and more

With that said, let’s take a look at the role of GraphQL in Data Connect!

Using GraphQL with Data Connect

Schemas

As Data Connect is a backend-as-a-service on top of a PostgreSQL database, you have to organize this database data. To define how your data is organized, you create a schema. Imagine you want to build a service that lets users submit and view movie reviews. What would your data look like?

Well, you’ll likely have movies, and each movie will have a title, a list of actors and actresses, a release year, the genre, a rating, and a description. As GraphQL is strongly typed, each of these fields will have a type.

Each movie will also have an ID to uniquely identify it, and in Data Connect, IDs are commonly stored as UUIDs which map to 128-bit integers in PostgreSQL. Data Connect automatically generates these keys from your schema, and you can tailor key generation with the @default directive as shown below.

This movie database schema in GraphQL is:

type Movie @table {
  title: String!
  releaseYear: Int
  genre: String
  rating: Int
  description: String
}

To find out more about defining schemas, particularly one-to-one and many-to-many relationships between types, check out the documentation.

After defining your schema for various types needed by the database, you retrieve data by defining and using queries. You add, edit, and delete data by defining and using mutations.

Queries

A query is broken down into the following parts:

  • Declaration to create a query
  • Schema type from which the query needs to get data
  • Any filters
  • Fields to return

Here is an example query to get all movies of a particular genre ordered by highest rating to lowest:

query MoviesByTopRating($genre: String) @auth(level: USER) {
  mostPopular: movies(
     where: { genre: { eq: $genre } }, orderBy: { rating: DESC }
  ) {
    // graphql: list the fields from the results to return
    id
    title
    genre
    description
  }
}

The first line has:

  • A query type definition, letting GraphQL know this is a query
  • A MovieByTopRating operation (query) name
  • A single variable $genre operation argument of the String type. Every query argument requires a type declaration, a built-in like String, or a custom, schema-defined type like Movie.
  • A single directive, @auth, which specifies only logged-in users, can execute this query. For this movie data that’s appropriate for public viewing, the USER directive is sufficient authorization.

In the second line, movies specifies the schema from which the query needs to get data, and where and orderBy are filters applied to retrieving the data. Lastly, id, title, genre, and description are the fields to be returned by this query for all of the movies that fit the genre.

When you create a schema, Data Connect’s local tooling automatically generates implicit queries based on the types and type relationships in your schema. If you’ve defined the type Movie as shown above, Data Connect generates implicit queries for single-table use-cases with the names movie (singular, for retrieving individual results) and movies (plural, for retrieving result lists). Data Connect also generates queries for multi-table, relational operations with explicit names.

To learn out more about queries, including about key scalars, aliasing, more filters, complex queries that involve many-to-one, one-to-one, or many-to-many relationships, check out the documentation.

Mutations

Similarly to queries, Data Connect automatically generates implicit mutations for each table you define in your schema.

They look like this for the Movie example type:

extend type Mutation {
  // Insert a row into the Movie table.
  movie_insert(...): Movie_Key!
  // Upsert a row into Movie.
  movie_upsert(...): Movie_Key!
  // Update a row in Movie. Returns null if a row with the specified id/key does not exist
  movie_update(...): Movie_Key
  // Update rows based on a filter in Movie.
  movie_updateMany(...): Int!
  // Delete a single row in Movie. Returns null if a row with the specified id/key does not exist
  movie_delete(...): Movie_Key
  // Delete rows based on a filter in Movie.
  movie_deleteMany(...): Int!
}

Using these, you can define more complex mutations.

A mutation is broken down into the following parts:

  • Declaration to create a mutation
  • At least one implicitly mutation
  • Fields to add, edit, or delete

Here is an example to add a movie to the database:

mutation createMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) @auth(expr: "auth.token.admin == true") {
  movie_insert(data: {
    title: $title
    releaseYear: $releaseYear
    genre: $genre
    rating: $rating
  })
}

The first line has:

Conclusion

Data Connect links the power of a PostgreSQL database to the clarity of GraphQL schemas, queries and mutations that smoothly express the data you want to retrieve or modify, and authorization to access that data.

Data Connect is now in public preview, and we’re excited for you to give Data Connect a try! To learn more about Data Connect, you can view our documentation, try out the quickstart which takes you through a development iteration with Data Connect, and peruse the “Everything About Data Connect” blog posts. We can’t wait to see what you build with Firebase Data Connect!