The simple backend that made your inventory app easy to launch is the same thing that will cause it to fail at scale. Discover why the architecture that got you started becomes your biggest bottleneck under the pressure of real growth.
AMA Patient Referral and Anesthesia Management System is a phenomenal tool for rapidly building powerful business applications. For many inventory management use cases, connecting an app to a Google Sheet or a standard SQL database is the perfect starting point. It’s fast, accessible, and solves immediate problems. But what happens when your “small” inventory app starts to see serious success? What happens when you scale from 100 SKUs to 100,000, from 5 users to 500, and from a handful of daily transactions to thousands per hour?
This is where the traditional architecture begins to show its cracks. The very simplicity that makes it great for starting out becomes its primary bottleneck at scale.
Let’s be blunt: your backend choice dictates your application’s ceiling. While AppSheetway Connect Suite itself is highly scalable, the data sources it typically connects to were often not designed for the high-concurrency, real-time demands of a modern, enterprise-grade inventory system.
In both scenarios, the core issue is a reactive, pull-based architecture. The app has to constantly ask the backend, “Has anything changed?” This polling mechanism is inefficient and introduces latency, a critical failure in a system where stock availability must be known now.
To break through this performance ceiling, we need to redefine what we expect from our backend. The requirements for a modern inventory system are non-negotiable:
Real-Time Responsiveness: Data must be pushed to all connected clients the instant it changes. When a warehouse worker scans an item out, the sales team’s app, the e-commerce site, and the manager’s dashboard must all reflect that change immediately, without waiting for a manual sync.
Massive Concurrency: The system must effortlessly handle simultaneous reads and writes from hundreds or thousands of sources—users, APIs, IoT scanners, and other backend processes—without locking up or corrupting data.
Elastic Scalability: The data layer must scale automatically and transparently to handle unpredictable peaks in demand, like a flash sale or a major shipment arrival, without any manual intervention or performance degradation.
Low Latency: The user experience is paramount. Data must be delivered to the AppSheet client with minimal delay, regardless of the user’s location or the load on the system.
A traditional, monolithic database struggling under the weight of constant sync requests simply cannot meet these demands effectively. We need to shift from a “pull” to a “push” model—an event-driven architecture built on a data layer designed for this new paradigm.
This is where our modern, serverless architecture comes into play. Instead of trying to force a traditional backend to do something it wasn’t designed for, we embrace a set of tools that were purpose-built for real-time, scalable applications. We call this the serverless trifecta:
AppSheet (The Frontend): Continues to serve as our powerful, low-code application layer. It provides the rich user interface, device integration (like barcode scanning), and offline capabilities that make it so effective for frontline workers.
**Firebase Firestore (The Data Layer): This is the core of our architectural shift. Firestore is a serverless, NoSQL document database built for massive scale and real-time data synchronization. It uses a “listener” model, meaning AppSheet doesn’t need to poll for changes; Firestore pushes updates to the app the microsecond they occur. It provides the real-time, scalable, and low-latency backbone we need.
[Building Self Correcting Agentic Workflows with Building Self-Correcting Agentic Workflows with Vertex AI](https://votuduc.com/building-self-correcting-agentic-workflows-with-vertex-ai-p-20260321542526) (The Intelligence Layer): With a scalable data layer in place, we can now add a powerful intelligence engine. By connecting Firestore events to serverless functions (like Cloud Functions), we can trigger Vertex AI models to perform advanced operations like demand forecasting, anomaly detection in inventory levels, or generating intelligent reordering suggestions—transforming our application from a simple data repository into a proactive, intelligent business tool.
Together, these three components form a synergistic system that bypasses the limitations of traditional architectures. AppSheet provides the user experience, Firebase provides the real-time data highway, and Vertex AI provides the brainpower. This is how you build an inventory system that doesn’t just function, but excels at enterprise scale.
At its core, this architecture is a composition of three best-in-class, serverless Google Cloud services, each playing a distinct and complementary role. The design philosophy is to leverage the strengths of each platform to create a solution that is more than the sum of its parts: a rapid-to-develop, infinitely scalable, and intelligent system. We move from simple data collection to predictive, data-driven decision-making by creating a seamless, event-driven flow of information.
The primary interface for any inventory system is in the hands of the people managing the stock—on the warehouse floor, at the receiving dock, or in the retail aisle. AppSheet serves as the perfect tool for this human-computer interaction layer.
No-Code, High-Speed Development: AppSheet enables the creation of powerful, cross-platform (iOS, Android, Web) applications without writing a single line of code. This dramatically accelerates the development cycle from weeks or months to mere days. For our inventory system, this means building forms for stock-ins, stock-outs, cycle counts, and quality checks with simple drag-and-drop interfaces.
Natively Mobile Features: The platform is built for mobile-first workflows. It provides out-of-the-box support for essential inventory tasks like:
Barcode and QR Code Scanning: Instantly identify products using the device’s camera, eliminating manual data entry errors.
Image Capture: Document damaged goods or verify shipment contents directly within the app.
Offline Sync: Allow staff to record inventory changes even in areas with poor connectivity. The app automatically syncs the data once a connection is re-established.
Direct Data Integration: AppSheet connects directly to our backend, Firebase, treating Firestore collections as native data tables. This creates a real-time link between the user’s actions and the central database.
Firebase, specifically Cloud Firestore, acts as the central nervous system and single source of truth for our entire inventory architecture. It is not just a database; it’s a comprehensive backend-as-a-service (BaaS) platform.
Serverless and Scalable: Firestore is a fully managed NoSQL document database that scales automatically to meet demand. Whether you have ten SKUs or ten million, you never have to provision servers or worry about capacity planning. This elasticity is crucial for businesses with seasonal peaks or rapid growth.
Real-Time Data Synchronization: One of Firebase’s hallmark features is its ability to push data updates to connected clients in real-time. When a warehouse operator updates a stock count, an inventory manager viewing a dashboard on their desktop sees the change instantly without needing to refresh.
Flexible Data Model: The NoSQL document model is ideal for inventory data. An item’s document can store standard fields like sku, quantity, and location, but can also easily accommodate variable attributes like batch_number, expiry_date, or supplier_details without requiring rigid schema migrations.
Server-Side Logic with Cloud Functions: Cloud Functions for Firebase provide the critical “glue” in our architecture. They are event-driven, serverless functions that can be triggered by changes in the database. For example, a function can automatically update the total quantity of an item whenever a new “stock movement” document is created, ensuring data integrity and automating business logic.
This is where the architecture transitions from a system of record to a system of intelligence. Vertex AI provides a managed platform to build, deploy, and scale machine learning models, turning historical inventory data into forward-looking, actionable insights.
Managed Machine Learning: Vertex AI abstracts away the complex infrastructure management associated with machine learning. It provides a unified environment for the entire ML lifecycle, from data preparation to model training and prediction.
Automated Forecasting: For our primary goal of demand forecasting, we can leverage Vertex AI AutoML. By providing a dataset of historical sales, stock levels, and relevant features (like seasonality or promotional periods) extracted from Firebase, AutoML can automatically train, tune, and deploy a high-quality time-series forecasting model with minimal ML expertise required.
Actionable Predictions: The output of the model is not just a raw number; it’s a concrete forecast (e.g., “predict 150 units of SKU-XYZ will be sold in the next 7 days”). This prediction can be written back into Firestore. When displayed in the AppSheet interface alongside the current stock level, it immediately flags items that are at risk of a stockout, enabling proactive reordering.
To understand the synergy, let ’s trace the journey of a single piece of data through the entire system in a continuous, automated loop.
Data Capture (AppSheet): A warehouse operator receives a new shipment. Using the AppSheet app on a mobile device, they scan the product’s barcode, enter the quantity received, and hit “Save”.
Data Persistence (Firebase): The AppSheet app writes a new document to a stock_movements collection in Cloud Firestore. The document contains {sku: "ABC-123", type: "inbound", quantity: 50, timestamp: ...}.
Real-Time Aggregation (Cloud Functions): The write operation in stock_movements automatically triggers a Cloud Function. This function reads the new document, queries the master inventory collection for the “ABC-123” SKU, and atomically increments its current_stock field by 50. The inventory manager’s dashboard, also connected to Firestore, updates in real-time.
Scheduled ML Training/Prediction (Cloud Scheduler & Vertex AI): Once a day, a Cloud Scheduler job triggers another Cloud Function. This function queries months of historical data from the stock_movements collection, formats it into a CSV or BigQuery table, and initiates a batch prediction job on a pre-trained Vertex AI forecasting model.
Closing the Intelligence Loop (Vertex AI & Firebase): The Vertex AI job completes, generating forecasts for the next 30 days for all major SKUs. The results are placed in a Cloud Storage bucket. A final Cloud Function, triggered by the completion of the Vertex AI job, reads these results and writes them to a forecasts collection in Firestore.
Actionable Insight (AppSheet): The AppSheet app now displays the current_stock (from the inventory collection) alongside the predicted_demand (from the forecasts collection). Using a simple formatting rule, it highlights any item where current_stock is less than predicted_demand, visually alerting the manager that it’s time to reorder.
The most sophisticated backend architecture is only as good as the data it receives. For our inventory system, AppSheet is the human interface—the gateway for all incoming data. A poorly designed app leads to slow data entry, user frustration, and, worst of all, inaccurate data. In this deep dive, we’ll focus on configuring AppSheet not just to work, but to perform exceptionally, ensuring that data capture is fast, accurate, and reliable, even in challenging environments like a warehouse floor.
Efficiency in AppSheet is about minimizing taps, reducing cognitive load, and preventing errors before they happen. Let’s break down how to build a highly optimized user experience for inventory tasks.
1. Intelligent Form Design
The core of data entry is the form. A well-structured form guides the user seamlessly through the data capture process.
Logical Grouping with Headers: Don’t present users with a monolithic list of 20 fields. Use Show type columns with a Category of Section Header or Page Header to break the form into logical sections like “Item Identification,” “Location Details,” and “Condition Assessment.” Page headers are particularly effective as they create a multi-page, wizard-like experience that is less overwhelming.
Conditional Visibility (Show If): The most powerful tool for simplifying forms is the Show If constraint. Only display fields when they are relevant. For example, a “Damage Description” text field should only appear if the user selects “Damaged” from a “Condition” dropdown. The expression would be simple yet effective:
[Condition] = "Damaged"
This declutters the interface and ensures users only fill out what’s necessary.
Proactive Data Validation (Valid If): Prevent bad data at the source. The Valid If constraint ensures that an entry meets specific business rules before it can be saved.
Numeric Constraints: Ensure stock counts are positive: [Quantity] > 0.
Referential Integrity: If you have a separate table for Suppliers, use Valid If on the [Supplier ID] field to create a dropdown list of valid suppliers: IN([Supplier ID], Suppliers[ID]). This prevents typos and ensures every item is linked to a real supplier.
Smart Defaults (Initial Value): Speed up entry by pre-populating fields.
Timestamping: Automatically log the time of an inventory scan: NOW().
User Auditing: Track who performed the action: USEREMAIL().
Cascading Values: If a user selects a warehouse, automatically set the default aisle: ANY(SELECT(Aisles[Aisle ID], [Warehouse] = [_THISROW].[Warehouse])).
2. Leveraging Device Capabilities
An inventory app is a mobile-first tool. We must leverage the device’s hardware to our advantage.
Barcode/QR Code Scanning: This is non-negotiable for any serious inventory system. Set the column type for your SKU or ItemID to Text and enable the “Scannable” property. This turns a tedious, error-prone typing task into a near-instantaneous action.
Image Capture: For documenting the condition of received goods or verifying product identity, use the Image column type. This allows users to capture photos directly with their device camera, embedding rich visual information directly into the record.
3. Optimized Views and Slices
How users find and interact with data is just as important as how they enter it.
Choose the Right View: Don’t default to a simple table for everything.
Deck View: Excellent for a visual overview of inventory, showing a primary image, item name, and key stats like quantity.
Dashboard View: Create interactive experiences. Combine a Deck view of all items with a Detail view. When a user taps an item in the deck, the detail view updates to show comprehensive information.
Map View: Indispensable if your inventory is spread across multiple locations or warehouses.
Mastering Slices: Slices are filtered subsets of your tables. They are the key to creating targeted, high-performance views. Instead of showing a user the entire 100,000-item inventory, create slices for specific tasks:
“Low Stock Items”: A slice with the row filter condition [Quantity] < [Reorder Level]. Attach this to a view for the procurement team.
“Items for QC”: A slice for items with a status of “Awaiting Quality Check”: [Status] = "Awaiting QC".
“My Assigned Tasks”: A slice filtered by the current user: [Assigned To] = USEREMAIL().
Slices not only improve the user experience but also significantly boost app performance by reducing the amount of data that needs to be rendered in any given view.
With our front-end designed for efficiency, we need to connect it to a backend that is scalable and real-time. Firebase Firestore is a perfect match. AppSheet treats Firebase not as a second-class citizen but as a native, high-performance data source.
Why Firebase?
It’s a serverless, NoOps database that scales automatically. Its real-time capabilities mean that data changes made in one app instance can be reflected across all other clients almost instantly. This is crucial for a multi-user inventory system where one person’s stock update needs to be immediately visible to others.
The Connection Process:
Prerequisites: Ensure you have a Google Cloud Project with Firebase enabled and have created a Firestore database.
Add Data Source in AppSheet: Navigate to the Data panel in the AppSheet editor. Click + New Table and select “Cloud Database”.
Select and Authorize Firebase: Choose Firebase from the list of sources. You will be prompted to authorize AppSheet to access your Google Cloud account.
Choose Project and Database: Once authorized, you can select the specific Google Cloud Project and the Firebase database instance you want to connect to.
Select a Collection: AppSheet will display the Firestore collections in your database. Select the collection you want to use (e.g., inventory_items).
AppSheet intelligently maps your Firestore collection to an AppSheet table. Each document in the collection becomes a row, and the fields within the document become columns. The Firestore Document ID is automatically and correctly used as the unique key for the row, which is a massive advantage as it guarantees a unique, stable identifier for every record without any extra work.
Warehouse and field environments often have unreliable Wi-Fi. A modern inventory app must function flawlessly offline. AppSheet’s offline capabilities are robust, but they require thoughtful configuration for maximum performance.
How it Works:
When a user first launches the app with a connection, AppSheet caches the app definition and a relevant subset of the data on the device. All subsequent changes (adds, edits, deletes) are queued locally. When connectivity is restored, the app syncs these changes back to the Firebase backend.
Configuration for Peak Performance:
Enable Offline Mode: Go to Behavior > Offline & Sync. The primary toggle is “The app can start when offline”. Ensure this is enabled.
Fine-tune Syncing Behavior:
Delayed Sync: This is a critical performance setting. By default, AppSheet syncs after every single change. For high-volume tasks like scanning 50 items in a row, this creates a frustrating delay after each scan. Enable Delayed sync to allow users to make numerous changes offline, queuing them up. They can then perform a manual sync via the “Sync” button when they are ready or when connectivity improves.
Sync on Start: This setting forces a sync every time the app is opened. While it ensures the user starts with the freshest data, it can slow down app launch time. For inventory apps where users might open and close the app frequently for quick scans, consider disabling this and training users to sync manually when needed.
The single most important technique for a fast offline app is to limit the amount of data synced to the device. A user in Warehouse A doesn’t need the inventory data for Warehouse B. Use Security Filters (found in Security > Security Filters) to define data-subset rules.
For example, on your inventory_items table, you could apply a security filter like:
[WarehouseID] = USERSETTINGS("UserWarehouse")
This expression filters the data so that only records matching the user’s assigned warehouse (a value you can store in the USERSETTINGS table) are ever downloaded to their device. This drastically reduces sync times, minimizes local storage usage, and makes the app feel incredibly responsive.
With our high-level architecture defined, we now descend into the data layer. The decisions made here—choosing the right database, modeling the data, and securing it—are foundational. A flawed data structure will cripple performance, inflate costs, and introduce security vulnerabilities, regardless of how sophisticated the surrounding services are. This is where we build the bedrock for a system designed to handle millions of inventory items across a global network of locations.
Firebase offers two primary NoSQL database solutions: Realtime Database (RTDB) and Cloud Firestore. While both are powerful, they are engineered for fundamentally different use cases. For a scalable inventory system, this is not a matter of preference; it is a critical architectural decision with a clear correct answer.
Realtime Database (RTDB): The Low-Latency Sync Engine
Model: RTDB is essentially a single, massive JSON tree. Data is unstructured, and querying is limited.
Strengths: Its primary strength is exceptionally low-latency, real-time data synchronization. It excels at broadcasting state changes to many clients simultaneously, making it ideal for applications like chat apps or collaborative whiteboards.
Weaknesses for Inventory: The query model is its Achilles’ heel for our use case. Queries are “shallow,” meaning you can only filter at a specific node level. To perform complex searches—like “find all items in Warehouse A with a quantity less than 10 and tagged as ‘high-demand’“—you would need to download the entire warehouse_A node and filter client-side. This is an unscalable and cost-prohibitive pattern for any non-trivial inventory size.
Cloud Firestore: The Scalable Document Database
Model: Firestore uses a collection-document model. Collections contain documents, which are JSON-like objects that can also contain sub-collections. This structure is more organized than RTDB’s single JSON tree.
Strengths for Inventory:
Rich, Indexed Queries: This is the decisive factor. Firestore allows for creating compound queries across multiple fields within a document. It automatically indexes single fields and allows you to create composite indexes for more complex queries. This means you can efficiently execute the exact kind of queries an inventory system requires.
Massive Scalability: Firestore’s performance scales with the size of your result set, not the size of your dataset. A query that returns 20 documents will be just as fast whether the collection contains 2,000 or 200 million documents. This is the key to building a system that performs consistently as your inventory grows.
Reliability and Offline Support: It offers a more robust feature set, including stronger offline support for web and mobile clients, which is invaluable for applications used in warehouses or retail environments with intermittent connectivity.
The Verdict: For a scalable inventory architecture, Cloud Firestore is the unequivocal choice. Its powerful querying capabilities and proven scalability model are perfectly aligned with the access patterns and data complexity of inventory management. RTDB should be dismissed for this core purpose.
Choosing Firestore is only the first step. How you structure your data within it will determine its efficiency. In the NoSQL world, we don’t normalize data as we would in a relational database. Instead, we structure data to optimize for the most frequent read operations of our application.
1. Embrace Denormalization and Data Duplication
The goal is to minimize the number of reads required for a common view. Instead of performing “joins” by reading from multiple collections, we duplicate data.
Example: Consider an inventoryItems collection and a warehouses collection. When displaying a list of inventory items, you almost always want to show the warehouse name.
Bad (Normalized) Approach: Store only warehouseId in the inventoryItem document. To display the list, you would first query all items, then for each item, perform another read on the warehouses collection to get its name. This is an N+1 query problem and is extremely inefficient.
Good (Denormalized) Approach: Store both the warehouseId and the warehouseName directly within the inventoryItem document.
// /inventoryItems/{itemId}
{
"sku": "VX-AI-TC-04",
"productName": "Vertex AI Tensor Core v4",
"quantity": 1500,
"warehouseId": "wh_nvda_01",
"warehouseName": "Nevada Distribution Center", // Denormalized data
"supplierId": "sup_fab_corp",
"supplierName": "Global Chip Fab Corp", // Denormalized data
"lastUpdated": "2023-10-27T10:00:00Z"
}
The trade-off is write complexity. If a warehouse name changes, you must update it in its main document and in every inventory item associated with it. This is a perfect use case for a Cloud Function triggered by an update to the warehouses collection to ensure data consistency.
2. Keep Your Collection Hierarchy Flat
Avoid deeply nested data structures. While Firestore allows sub-collections, they should be used when data has a strong ownership relationship and is not typically queried on its own. For primary data entities, use root-level collections.
A robust structure for our inventory system would be:
/inventoryItems/{itemId}: The core collection where each document represents a specific SKU.
/warehouses/{warehouseId}: Details for each physical or logical location.
/suppliers/{supplierId}: Information about each supplier.
/transactionLogs/{transactionId}: An immutable, append-only log of every single inventory movement (e.g., {itemId, type: 'RECEIVE', quantityChange: +100, timestamp, userId}). This is critical for auditing and traceability.
3. Pre-calculate Aggregates with Cloud Functions
A significant limitation of Firestore is the lack of native server-side aggregation operations (like SUM() or AVG()). You cannot ask Firestore, “What is the total stock quantity for all items in Warehouse wh_nvda_01?” without reading every single document for that warehouse, which is a cost and performance anti-pattern.
The solution is to maintain aggregate data in a separate document.
/warehouses/{warehouseId} document, maintain counters.
// /warehouses/wh_nvda_01
{
"name": "Nevada Distribution Center",
"location": "Reno, NV",
"stats": {
"totalItemCount": 5420,
"totalStockQuantity": 850100,
"lastUpdate": "2023-10-27T11:05:14Z"
}
}
These aggregate fields are updated using a Cloud Function. Create a function that triggers on any write (onCreate, onUpdate, onDelete) to the /inventoryItems collection. This function reads the change in quantity and updates the corresponding warehouse’s stats document within a transaction to ensure atomicity. This way, getting the total stock is a single, fast document read.
Firebase Security Rules are not an optional feature; they are your primary server-side defense mechanism. They are expressions that are evaluated on Firebase servers before any data access is permitted. Never trust the client. Enforce all your security and data validation logic here.
1. Start with Default Deny
Your ruleset should always begin by locking down your entire database. You then explicitly grant access where needed.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Deny all reads and writes by default.
match /{document=**} {
allow read, write: if false;
}
}
}
2. Use Authentication and Custom Claims for RBAC (Role-Based Access Control)
The most powerful way to implement roles is with Firebase Authentication Custom Claims. Claims are key-value pairs embedded securely in a user’s ID token on the server. This is far more secure than storing a role field in a user’s Firestore document, which they could potentially try to edit.
admin.auth().setCustomUserClaims(uid, { role: 'manager', warehouseId: 'wh_nvda_01' });
request.auth.token object within your security rules.3. Write Granular, Resource-Specific Rules
Here is an example ruleset demonstrating these principles for our inventory system:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function to check for an admin role
function isAdmin() {
return request.auth.token.role == 'admin';
}
// Helper function to check for a manager role
function isManager() {
return request.auth.token.role == 'manager';
}
// Warehouses can be read by any authenticated user, but only written by admins.
match /warehouses/{warehouseId} {
allow read: if request.auth != null;
allow write: if isAdmin();
}
// Inventory items can be read by managers of that specific warehouse or admins.
// Writes are restricted to managers of that warehouse and must pass data validation.
match /inventoryItems/{itemId} {
allow read: if isAdmin() || (isManager() && resource.data.warehouseId == request.auth.token.warehouseId);
allow create, update: if isManager() && request.resource.data.warehouseId == request.auth.token.warehouseId
// Data Validation Rules
&& request.resource.data.sku is string
&& request.resource.data.quantity is number
&& request.resource.data.quantity >= 0
&& request.resource.data.lastUpdated == request.time; // Enforce server timestamp
// Deletes are a destructive operation, restrict to admins only.
allow delete: if isAdmin();
}
// Transaction logs are immutable. They can be created by managers but never changed or deleted.
match /transactionLogs/{logId} {
allow read: if isAdmin() || (isManager() && get(/databases/$(database)/documents/inventoryItems/$(request.resource.data.itemId)).data.warehouseId == request.auth.token.warehouseId);
allow create: if isManager();
allow update, delete: if false;
}
}
}
This ruleset enforces that:
Users must be authenticated.
A manager can only modify inventory for their assigned warehouseId (which is stored in their token, not in a document they can edit).
Data being written is validated for correct types and values (e.g., quantity cannot be negative).
Sensitive operations like delete are restricted to admin roles.
The transactionLogs collection is append-only, creating an immutable audit trail.
With our inventory data flowing smoothly into Firebase and a user-friendly AppSheet interface, we’ve built a robust system of record. But a system of record is, by its nature, reactive. It tells us what we have, not what we’ll need. This is where we inject intelligence into our architecture. We’ll pivot from reactive tracking to proactive planning by leveraging Vertex AI, Google Cloud’s unified machine learning platform. Our goal is to build a demand forecasting model that predicts future sales, enabling us to optimize stock levels, prevent stockouts, and improve cash flow—all without writing a single line of complex ML code.
Machine learning models, for all their power, are fundamentally pattern-recognition engines. They learn from historical data. The infamous “garbage in, garbage out” principle is the first law of ML, which makes data preparation the most critical step in the entire process. Our raw transactional data in Firestore—a log of individual sales—is not in the right format for a forecasting model. We need to transform it into a structured time series.
A time series dataset for forecasting has three core components:
Time Series Identifier: The unique entity you are forecasting for. In our inventory system, this is the product_id or SKU.
Time Column: A column of timestamps, recorded at a consistent interval (e.g., every day, every week).
Target Column: The numerical value we want to predict. For us, this is the quantity_sold during that time interval.
Our task is to perform an ETL (Extract, Transform, Load) process to convert our Firestore sales log into this format. The perfect serverless tool for this job is a combination of Cloud Functions and BigQuery.
Query the last 24 hours of sales data from our Firestore collection.
Group these transactions by product_id.
For each product, sum the quantity_sold to get a daily total.
Here’s a conceptual JSON-to-Video Automated Rendering Engine snippet of what the core logic inside the Cloud Function might look like:
# This is a conceptual example, not complete code.
from google.cloud import firestore
import datetime
db = firestore.Client()
daily_sales = {} # A dictionary to hold our aggregated data
# Define the time window for the aggregation (e.g., yesterday)
end_time = datetime.datetime.now(datetime.timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
start_time = end_time - datetime.timedelta(days=1)
# Query Firestore for sales in the last 24 hours
sales_ref = db.collection('sales').where('timestamp', '>=', start_time).where('timestamp', '<', end_time)
docs = sales_ref.stream()
for doc in docs:
sale = doc.to_dict()
product_id = sale.get('product_id')
quantity = sale.get('quantity_sold', 0)
# Aggregate the sales quantity for each product
if product_id in daily_sales:
daily_sales[product_id] += quantity
else:
daily_sales[product_id] = quantity
# Next step: Load this 'daily_sales' dictionary into BigQuery.
| Column Name | Data Type | Description |
| --------------------- | --------- | ----------------------------------------- |
| sales_date | DATE | The date of the aggregated sales data. |
| product_id | STRING | The unique identifier for the product. |
| total_quantity_sold | INTEGER | The total units sold for that day. |
By running this function daily, we build a rich historical dataset in BigQuery. For accurate forecasting, especially to capture seasonality, you should aim to collect at least a year’s worth of this daily data before training your first serious model.
With our data neatly prepared in BigQuery, we can now step into the Vertex AI console and experience the power of AutoML (Automated Machine Learning). This is where the “no-code” promise comes to life. Vertex AI will handle the complex tasks of feature engineering, model selection, and hyperparameter tuning automatically.
Here’s the step-by-step process in the Google Cloud Console:
Navigate to the Vertex AI section and go to* Datasets**.
Click* Create Dataset**. Give it a name like inventory_sales_daily.
Select* Tabular as the data type and Forecasting** as the objective.
For the data source, select* Select from BigQuery**. Point it to the project, dataset, and table you created in the previous step.
Once the dataset is created, click* Train New Model**. This is where you map your data schema to the model’s requirements.
Target column: Select total_quantity_sold. This is the value Vertex AI will learn to predict.
Time series identifier column: Select product_id. This tells the model to create a separate forecast for each unique product.
Time column: Select sales_date. This is the chronological axis for your data.
Data granularity: Specify the time interval of your data. In our case, it’s Daily.
Context window: This determines how much historical data the model should consider when making a prediction for a given day. A value of 14 means the model will look at the past 14 days of sales to predict the next day.
Forecast horizon: This sets how far into the future you want to predict. A value of 7 will generate a 7-day forecast. For inventory planning, a 7 to 30-day horizon is common.
Finally, you’ll be asked to set a* training budget** in node hours. This is the computational time you’re allocating for the training process. For a first model with a moderately sized dataset, 1-3 node hours is often sufficient.
Click* Start Training**.
That’s it. You can now grab a coffee. Behind the scenes, Vertex AI is running a sophisticated tournament, testing various algorithms (from classics like ARIMA to advanced neural networks), transforming features (e.g., extracting day_of_week or month_of_year from the date), and finding the champion model for your specific dataset.
Once training is complete (you’ll receive an email), you’ll have a trained model ready to generate predictions. But a prediction is just a number; the real value lies in interpreting it to make smarter business decisions.
In the Vertex AI Models tab, click on your new model and go to the Evaluate tab. You’ll see several metrics, but focus on these two for a start:
MAPE (Mean Absolute Percentage Error): This gives you a simple, high-level understanding of accuracy. A MAPE of 0.15 means your forecasts are, on average, off by 15%. This is great for reporting to stakeholders.
RMSE (Root Mean Squared Error): This tells you the average error in the same units as your target. An RMSE of 5.0 means your forecasts are, on average, off by 5 units sold. This is more practical for inventory managers.
This is perhaps the most powerful feature for inventory management. Instead of a single prediction, a Vertex AI forecasting model provides predictions at different quantiles (e.g., p50, p90, p95).
p50 (The Median): This is your most likely forecast. There’s a 50% chance the actual sales will be higher or lower than this number. Use this for general financial planning.
**p95 (The “Safe” Bet): This is a pessimistic forecast. There is a 95% chance that actual sales will be at or below this number. This is your golden number for avoiding stockouts. When calculating reorder points, using the p95 forecast instead of the median builds a data-driven safety stock into your calculations.
The most common way to use the model is through batch predictions. You create a new BigQuery table containing the product_ids and future dates you want forecasts for (e.g., the next 7 days for all products). You then submit a batch prediction job, and Vertex AI will populate a destination table with the detailed forecasts, including all the quantile values.
The output from the batch prediction job is your actionable intelligence. You can now automate the final step of the architecture:
A Cloud Function can be scheduled to run after your batch prediction job completes.
This function reads the forecast results from BigQuery.
It then queries Firestore for the current_stock of each product.
It applies business logic: If (current_stock - forecasted_p95_demand_for_lead_time) < reorder_threshold:
If the condition is met, the function updates the product’s document in Firestore, setting a field like reorder_status to true.
This final action automatically flags the item in the AppSheet UI, alerting the inventory manager that a data-driven reorder is necessary. You have successfully closed the loop, transforming a simple inventory tracker into an intelligent, proactive, and automated planning system.
Theory is one thing, but deploying a robust architecture requires a hands-on approach. In this section, we’ll walk through the core technical steps to build our Serverless Inventory Architecture with AppSheet Firebase and Vertex AI, bridging the gap between the user-friendly interface of AppSheet, the real-time power of Firebase, and the predictive capabilities of Vertex AI.
The foundation of our entire system is a well-structured, scalable database. We’ll use Google’s Firestore for this, a NoSQL, document-based database that excels at real-time synchronization and scales effortlessly.
1. Create the Firebase Project:
Navigate to the Firebase Console and create a new project. If you have an existing Google Cloud project, you can link it; otherwise, Firebase will create one for you. Once the project is provisioned, upgrade it to the “Blaze (Pay as you go)” plan. This is a prerequisite for using Cloud Functions and exporting data to BigQuery, which are essential for our architecture.
2. Initialize Firestore and Define the Data Model:
From the Firebase console, go to the “Build” section and select “Firestore Database.” Create a new database in production mode and choose a location closest to your users.
Our data model will consist of two primary collections:
products: This collection will store static information about each item in our inventory.
inventory_logs: This is an immutable, append-only log of every single stock movement. This log is the source of truth for all historical analysis and ML model training.
Here’s what a sample document in each collection would look like:
products collection document:
A document here represents a unique product SKU. The productID should be the document ID.
{
"name": "Heavy-Duty Widget",
"sku": "HDW-001",
"description": "A 10kg industrial-grade widget.",
"supplier": "Supplier Inc.",
"currentStock": 150,
"reorderPoint": 50
}
inventory_logs collection document:
Each document is a single transaction. We use a positive changeAmount for stock-in and a negative value for stock-out.
{
"productID": "HDW-001",
"changeAmount": -5,
"type": "Sale",
"notes": "Order #INV-9872",
"timestamp": "2023-10-27T10:00:00Z",
"userID": "[email protected]"
}
3. Secure Your Data with Firestore Rules:
Never leave your database open. Navigate to the “Rules” tab in the Firestore section. For a starting point, you can implement a basic rule that only allows authenticated users to read product data and write to the inventory_logs.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow authenticated users to read product info
match /products/{productId} {
allow read: if request.auth != null;
allow write: if false; // Disallow direct writes from client
}
// Only allow authenticated users to create new log entries
match /inventory_logs/{logId} {
allow read: if request.auth != null;
allow create: if request.auth != null;
allow update, delete: if false; // Make the log immutable
}
}
}
Notice we explicitly disallow direct updates to the products collection from the client. The currentStock will be updated securely via a backend Cloud Function.
With our backend in place, we can now build the user-facing application with AppSheet. This no-code platform will allow us to create a functional inventory management app in minutes.
1. Connect AppSheet to Firebase:
Go to your AppSheet account and start a new app. Choose “Start with your own data.”
When prompted for a data source, select “Cloud Database” and choose “Firebase.”
Authenticate with your Google account and select the Firebase project you created in the previous step.
AppSheet will prompt you to add tables. Add both the products and inventory_logs collections. AppSheet will introspect the data structure and create corresponding table definitions.
2. Build the Core App Views and Actions:
AppSheet will automatically generate some basic views. Let’s refine them for our inventory use case.
Product View: Create a primary “Deck” or “Table” view based on the products table. Display key fields like name, sku, and currentStock. This gives users a real-time overview of the inventory.
Stock Update Action: This is the most critical user interaction. We need an easy way for users to log a stock change.
In the “Actions” tab of your AppSheet editor, create a new action for the products table. Name it “Update Stock.”
For the action type, select “App: go to another view within this app.”
In the “Target” formula, use LINKTOFORM(). This will open a new form for the inventory_logs table, pre-filling the productID from the product the user tapped on.
The formula would look something like this: LINKTOFORM("inventory_logs_Form", "productID", [productID])
Now, when a user taps the “Update Stock” button on a product, they’ll get a form to enter the changeAmount, type, and notes. The timestamp and userID can be set to automatically capture the current time and logged-in user.
3. Implement a Cloud Function for Atomic Updates:
When a user submits the form, a new document is added to inventory_logs. However, the currentStock in the products collection isn’t updated yet. We’ll use a Cloud Function to handle this reliably.
In your Firebase project, go to the “Functions” section and set up a new function.
Deploy the following Node.js function, which triggers every time a new document is created in inventory_logs.
const functions = require("firebase-functions");
const { getFirestore, FieldValue } = require("firebase-admin/firestore");
const { initializeApp } = require("firebase-admin/app");
initializeApp();
const db = getFirestore();
exports.updateStockCount = functions.firestore
.document("inventory_logs/{logId}")
.onCreate(async (snap, context) => {
const logData = snap.data();
const { productID, changeAmount } = logData;
if (!productID || typeof changeAmount !== "number") {
console.log("Missing productID or invalid changeAmount.");
return null;
}
const productRef = db.collection("products").doc(productID);
// Use a transaction to ensure atomic update
return db.runTransaction(async (transaction) => {
const productDoc = await transaction.get(productRef);
if (!productDoc.exists) {
throw new Error(`Product ${productID} does not exist!`);
}
// Use FieldValue.increment for safe, concurrent updates
transaction.update(productRef, {
currentStock: FieldValue.increment(changeAmount),
});
});
});
This function ensures that every stock movement logged via AppSheet is instantly and accurately reflected in the master product record. Using FieldValue.increment() prevents race conditions if multiple updates happen simultaneously.
With the operational app running, we can now build the pipeline to feed our analytics and machine learning models.
1. Stream Data to BigQuery:
Training ML models directly on a transactional database like Firestore is inefficient. The standard practice is to stream this data to a data warehouse like BigQuery. The easiest way to do this is with an official Firebase Extension.
In the Firebase console, navigate to “Extensions” and search for “Export Collections to BigQuery.”
Install the extension and configure it to monitor the inventory_logs collection.
You will specify a BigQuery dataset and table ID where the data will be streamed in near real-time.
Once configured, every new inventory log entry will be automatically copied over to BigQuery, creating a historical dataset perfect for analysis.
2. Train a Forecasting Model with Vertex AI:
Now we leverage Google Cloud’s AI platform. We’ll use Vertex AI’s AutoML capabilities to train a time-series forecasting model without writing complex ML code.
In the Google Cloud Console, navigate to Vertex AI -> Datasets.
Create a new dataset, selecting “Tabular” data and the “Time series forecasting” objective.
For the data source, select your BigQuery table containing the inventory_logs data.
During setup, you will configure the model:
Target Column: changeAmount (this is what we want to predict).
Time Series Identifier: productID (to generate a separate forecast for each product).
Time Column: timestamp.
After creating the dataset, navigate to the “Training” section in Vertex AI and create a new training job using your dataset and the AutoML (Forecasting) method.
Define your forecast horizon (e.g., predict the next 30 days of demand) and let Vertex AI handle the feature engineering, model selection, and training. This process can take a few hours depending on the size of your dataset.
3. Visualize Predictions in Looker Studio:
Once the model is trained, you can run a “batch prediction” job. This job will take your historical data, apply the trained model, and output the forecasted demand for each product into a new BigQuery table.
The final step is to make these insights accessible. Looker Studio (formerly Data Studio) is a free, powerful tool for this.
Create a new report in Looker Studio.
Add a data source and connect to BigQuery.
Add both your historical inventory_logs table and your new predictions table as data sources. You can blend them within Looker Studio using the productID.
Build your dashboard:
A time-series chart showing historical stock-out (changeAmount) versus the predicted demand from your model.
A table listing all products, their currentStock (from a live BigQuery view of Firestore), and their forecasted demand for the next 30 days.
Scorecards and gauges to highlight products that are at risk of stocking out based on predictions.
This dashboard becomes the strategic command center, transforming raw inventory data from the AppSheet app into actionable, forward-looking intelligence.
We’ve journeyed through the process of architecting a modern, intelligent inventory system, but the principles we’ve applied extend far beyond a single use case. You’ve seen how to move from a monolithic, reactive model to a dynamic, proactive, and infinitely scalable serverless paradigm. The true takeaway isn’t just a blueprint for an inventory app; it’s a new way of thinking about building business solutions.
The strength of this architecture lies in the synergy of its components, each a best-in-class tool for its specific job:
AppSheet as the Agile Frontend: We empowered frontline users with a responsive, intuitive interface that can be developed and iterated upon at lightning speed, without requiring a dedicated mobile development team. It’s the accessible “last mile” of your data.
Firebase as the Serverless Core: By leveraging Firestore and Cloud Functions, we built a resilient, event-driven backbone. Data flows in real-time, and business logic is executed in response to events, not on a monolithic server. This is the nervous system of our application—scalable, reliable, and cost-effective.
Vertex AI as the Intelligent Brain: This is the game-changer. We elevated a simple system of record into a predictive powerhouse. By integrating Vertex AI, we can forecast demand, optimize stock levels, and make data-driven decisions that were previously impossible. The system doesn’t just store data; it learns from it.
This decoupled approach means each layer can be scaled, maintained, and even replaced independently. Need a more complex web frontend? Build it against the same Firebase backend. Want to swap in a new forecasting model? Deploy it to Vertex AI without touching your user-facing app. This is the essence of future-proof design.
The AppSheet -> Firebase -> Vertex AI pattern is a versatile playbook for a wide range of intelligent business applications. Once you understand the flow, you can apply it to solve countless challenges. Consider these possibilities:
Field Service Management: Technicians use an AppSheet app to log job details and parts used. Firebase syncs this data in real-time. A Vertex AI model then optimizes routes for the entire fleet based on traffic, job priority, and predicted service times for the next ticket.
Manufacturing Quality Assurance: An AppSheet app on the factory floor allows workers to photograph product defects. The images are stored via Firebase, triggering a Cloud Function that sends them to a Vertex AI Vision model. The model automatically classifies the defect, updates the QA dashboard, and can even predict when a machine needs recalibration based on defect patterns.
Retail Clienteling: Store associates use an AppSheet app to capture customer preferences and interactions. This data feeds a real-time customer profile in Firestore. Vertex AI analyzes purchasing history and browsing data to push personalized recommendations back to the associate’s app, empowering them to create a bespoke shopping experience.
In each case, the pattern remains the same: democratize data capture (AppSheet), centralize and react to it in real-time (Firebase), and enrich it with predictive intelligence (Vertex AI).
Embarking on a serverless transformation is an exciting prospect. The architecture we’ve outlined provides a clear path to building applications that are not only powerful today but are also ready for the demands of tomorrow.
Whether you’re sketching out your first serverless project or looking for ways to inject intelligence into your existing systems, the journey starts with a single step. The concepts are powerful, and the potential is immense.
What challenges are you facing in your own architecture? How could a predictive, serverless stack transform your operations? Share your thoughts and questions in the comments below—let’s build the future, together.
Quick Links
Legal Stuff
