Cloud/ Google Cloud / Google Cloud Datastore: The Predecessor to Firestore and When It Still Makes Sense

GCP Google Cloud Platform 25 guides ยท updated 2026

Guides to BigQuery, Vertex AI, GKE, Dataflow, and the rest of Google's data- and AI-first cloud โ€” written for engineers shipping real workloads.

Google Cloud Datastore: The Predecessor to Firestore and When It Still Makes Sense

Cloud Datastore was Googleโ€™s managed NoSQL document store for App Engine applications before Firestore existed. In 2017, Google merged Datastoreโ€™s capabilities into Firestore and Firestore became the recommended choice for new projects. Datastore mode in Firestore is how existing Datastore applications continue to run.

Understanding Datastore matters if you inherit a project built on it, are migrating an App Engine application, or need to decide whether to stay on Datastore or upgrade to Firestore Native mode.


The Data Model: Kinds, Entities, and Keys

Datastore uses different terminology from Firestore, but the concepts are similar:

Firestore term โ†โ†’ Datastore term
Collection Kind
Document Entity
Document ID Key name / Key ID
Nested collection Entity group (ancestor path)

An entity in Datastore:

Kind: Product
Entity key: /Product, 1234567
Entity properties:
name: "Wireless Keyboard" (String, indexed)
price: 49.99 (Float, indexed)
in_stock: True (Boolean, indexed)
description: "..." (String, unindexed - too long)
tags: ["electronics", "input"] (Array of Strings)
created_at: 2025-01-10T09:00:00Z (DateTime)

Keys are either auto-assigned numeric IDs or user-specified string names. Keys can include an ancestor path, which defines the entityโ€™s position in a hierarchical entity group.


Entity Groups and Ancestor Queries

Datastoreโ€™s ancestor relationship groups related entities in a logical hierarchy. This is important for two reasons: ancestor queries return results with strong consistency, and Datastore transactions are limited to entities within the same entity group.

Entity group structure:
/Order,order_001
โ””โ”€โ”€ /Order,order_001/OrderItem,1
โ””โ”€โ”€ /Order,order_001/OrderItem,2
โ””โ”€โ”€ /Order,order_001/OrderItem,3
Ancestor query: "give me all OrderItems where ancestor = /Order,order_001"
โ†’ strongly consistent result
โ†’ all items returned are current as of query time
Non-ancestor query: "give me all OrderItems where total > 50.00"
โ†’ eventually consistent (may miss very recent writes)

This distinction drove a lot of Datastore application design โ€” developers structured entity groups to ensure strong consistency for related data within a transaction scope.


Querying with GQL and the Python Client

Datastore has a query language called GQL (Google Query Language) that resembles SQL but has important limitations:

GQL examples:
SELECT * FROM Product WHERE in_stock = TRUE AND price < 100.0
-- Works: equality and inequality on indexed properties
SELECT * FROM Product WHERE tags = 'electronics'
-- Works: multi-valued property filter
SELECT * FROM Product WHERE price < 100.0 ORDER BY name
-- Fails without a composite index on (price, name)
SELECT * FROM Product WHERE description CONTAINS 'keyboard'
-- Fails: Datastore does not support text search

Using the Python client library:

from google.cloud import datastore
client = datastore.Client(project="my-project")
# Create an entity
product_key = client.key("Product", "keyboard-001")
product = datastore.Entity(key=product_key)
product.update({
"name": "Wireless Keyboard",
"price": 49.99,
"in_stock": True,
"tags": ["electronics", "input"],
})
client.put(product)
# Read an entity by key
result = client.get(product_key)
if result:
print(f"{result['name']}: ${result['price']}")
# Query with filters
query = client.query(kind="Product")
query.add_filter("in_stock", "=", True)
query.add_filter("price", "<", 100.0)
query.order = ["price"]
for entity in query.fetch(limit=10):
print(entity["name"], entity["price"])

Indexes in Datastore

Datastore maintains two types of indexes:

Built-in indexes: Automatically created for every property of every entity, enabling single-property filters and orders.

Composite indexes: Required for queries that filter on multiple properties or combine a filter with an order on a different property. Defined in index.yaml:

indexes:
- kind: Product
properties:
- name: in_stock
direction: asc
- name: price
direction: asc
- name: name
direction: asc

Without the composite index, queries that require it return an error rather than a result. This is a common source of frustration for developers new to Datastore.


Transactions

Datastore transactions are serializable but limited to entities in the same entity group (by default). Cross-group transactions support up to 25 entity groups per transaction but are slower.

with client.transaction():
order_key = client.key("Order", "order_001")
order = client.get(order_key)
if order["status"] != "pending":
raise ValueError("Order already processed")
order["status"] = "confirmed"
order["confirmed_at"] = datetime.utcnow()
client.put(order)
# This will succeed or the whole transaction rolls back
confirmation_key = client.key("OrderConfirmation", "order_001")
confirmation = datastore.Entity(key=confirmation_key)
confirmation["order_id"] = "order_001"
confirmation["sent_at"] = datetime.utcnow()
client.put(confirmation)

Datastore Mode vs Firestore Native Mode

When you create a new Firestore database, you choose Native mode or Datastore mode. The underlying storage is the same (Firestore), but the API and capabilities differ:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Feature โ”‚ Datastore Mode โ”‚ Native Mode โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ API โ”‚ Datastore API โ”‚ Firestore API โ”‚
โ”‚ Real-time listeners โ”‚ No โ”‚ Yes โ”‚
โ”‚ Offline client sync โ”‚ No โ”‚ Yes (mobile/web SDK) โ”‚
โ”‚ Security rules โ”‚ No (IAM only) โ”‚ Yes โ”‚
โ”‚ Consistency model โ”‚ Eventual (non-ancestor)โ”‚ Strong consistency โ”‚
โ”‚ Mobile/web SDK โ”‚ No โ”‚ Yes (Firebase SDK) โ”‚
โ”‚ Maximum entity size โ”‚ 1 MB โ”‚ 1 MB (document) โ”‚
โ”‚ Collection group queries โ”‚ Equivalent (kind query)โ”‚ Yes โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Migrating from Datastore to Firestore Native Mode

Migration cannot happen in-place โ€” you cannot upgrade a Datastore Mode database to Native mode. The migration path is:

Step 1: Export Datastore data
gcloud datastore export gs://my-bucket/export
Step 2: Create a new Firestore database in Native mode (different project or region)
Step 3: Import data into Firestore (Datastore export format is importable)
gcloud firestore import gs://my-bucket/export
Step 4: Update application code
- Replace Datastore API calls with Firestore API calls
- Terminology changes: kinds โ†’ collections, entities โ†’ documents
- Query changes: ancestor queries replaced by collection group queries
- Remove entity group constraints
- Add Security Rules if replacing IAM-based access
Step 5: Test thoroughly, then cut over traffic

When to Stay on Datastore Mode

Despite Firestore Native mode being the recommended choice for new projects, some situations favor keeping Datastore mode:

The practical answer: if youโ€™re building something new, use Firestore Native. If youโ€™re maintaining something old on Datastore that works, evaluate the migration cost before committing.


Summary

Cloud Datastore is the server-side NoSQL document store that App Engine applications have relied on since 2008. Its key concepts โ€” kinds, entities, keys, ancestor paths, and composite indexes โ€” remain relevant for the millions of applications built on it. Firestore in Datastore mode maintains backward compatibility while Firestore Native mode adds real-time listeners, offline sync, client-side security rules, and strong consistency for all queries. New development should use Firestore Native mode. Existing Datastore applications can continue running in Datastore mode indefinitely, migrating only when the new capabilities of Native mode justify the effort.