Video chapters
- AppEngine Backend as a Service
- How the platform fits your application
- Connect with the right organization and identity
- Choose built-in models or define your own
- Store the attachment before the record
- Create and read the application record
- From a saved change to a platform action
- Configure the trigger and action
- Test the action before enabling the trigger
- Activate and prove a real event
- Read the execution record
- Inspect the same records in Appmint Studio
- Choose the event you actually intend
- Boundaries, retries and related services
- Move an application one verified slice at a time
- A connected AppEngine backend
Written guide
Companion source code
Get the course source code on GitHub. The folder includes the supplied examples and setup instructions. Keep credentials in your own environment; use the maintained API reference when adapting recorded request examples.
Build one connected backend flow: an authorized client uploads a file, creates an application record, and AppEngine runs an automation that creates a related receipt. Inspect the API result and the same records in Appmint Studio. This is a platform integration course; your application can keep its own web or mobile interface.
Verified scope: authorized staff identity, custom collections, a stored attachment reference, a real data_created event, a create_data action, execution history, Studio inspection and a create-versus-update test. These procedures were executed on the controlled training deployment on 10 October 2026. Customer access, external notifications, realtime delivery and hosted deployment require their linked courses and their own outcome checks. This lesson does not claim Firebase feature parity.
1. Understand what AppEngine supplies
Your client owns its screens and interaction design. AppEngine supplies identity-aware API operations, business models, Repository records, file storage and configured event-driven work. A record’s sk identifies one saved item; its data contains application fields. A collection’s technical name is the API datatype. Its display title is a separate label in Studio.
Built-in business models have operations that can do more than store fields. Use the customer model for a customer account and the appropriate customer APIs for identity/profile work; use product operations for product behavior. Extend a suitable built-in model when only extra fields are needed. A custom collection suits data whose meaning and rules belong to your application. Do not assume generic Repository writes reproduce all domain actions.
Our two custom collections are App submissions — Backend course (baas_submissions_1010) and Event receipts — Backend course (baas_receipts_1010). The example focuses on connecting capabilities rather than teaching an invented company’s operations.
2. Prepare the correct identity and tools
Open Appmint Studio. Use an existing training organization and an authorized staff account. Obtain its AppEngine address and organization identifier from your actual deployment. The video uses the public address convention https://appengine.appmint.io; substitute your deployed address if different.
Complete Database & Repository APIs — staff session and request setup before this procedure. It demonstrates staff sign-in, including the request and returned token. Authentication and organization access explains staff, app and customer identities. Keep the token in the authorized internal client/server that is supposed to act as that staff member. Do not put a privileged staff token into a public website or customer mobile build.
Every protected request below sends orgid and Authorization: Bearer YOUR_STAFF_TOKEN. JSON requests also send Content-Type: application/json. A clean request for the demonstrated record without a token returned HTTP 401. This is one actual refusal, not a blanket proof that every possible API and principal has identical access rules.
Prepare a small harmless submission-notes.txt file. Choose unique collection names if these course names already exist in your organization. Keep the chosen names consistent in schemas, trigger configuration, actions and requests. Use your returned IDs and file paths; the IDs shown below are evidence from the recorded exercise.
3. Choose models and create the two collections
Inspect current definitions before inventing a duplicate model:
GET /repository/collections/customer?enriched=falseGET /repository/collections/sf_product?enriched=false
Both returned actual built-in definitions in an array. A matching display label alone does not establish the API datatype or its business behavior. The Database course covers built-in extension, custom schemas, queries, history and trash in detail.
Create these two isolated course collections. These are complete request bodies, including their declared schemas. required describes the schema declaration; this lesson does not claim a universal validation guarantee for every write route.
POST /repository/create
{
"datatype": "collection",
"isNew": true,
"data": {
"name": "baas_submissions_1010",
"title": "App submissions — Backend course",
"enableVersioning": true,
"schema": {
"type": "object",
"properties": {
"reference": {
"type": "string",
"title": "Reference"
},
"title": {
"type": "string",
"title": "Title"
},
"attachmentPath": {
"type": "string",
"title": "Attachment path"
},
"status": {
"type": "string",
"title": "Status",
"enum": [
"new",
"reviewed"
]
}
},
"required": [
"reference"
]
}
}
}POST /repository/create
{
"datatype": "collection",
"isNew": true,
"data": {
"name": "baas_receipts_1010",
"title": "Event receipts — Backend course",
"enableVersioning": true,
"schema": {
"type": "object",
"properties": {
"submissionId": {
"type": "string",
"title": "Submission ID"
},
"reference": {
"type": "string",
"title": "Reference"
},
"message": {
"type": "string",
"title": "Message"
}
},
"required": [
"reference"
]
}
}
}Read each definition back with GET /repository/collections/baas_submissions_1010?enriched=false and the corresponding receipt datatype. Confirm the exact data.name, friendly data.title and properties. Do not continue with a guessed datatype identifier.
4. Upload the attachment and retain its path
POST /repository/file/upload as multipart form-data, not a JSON body. Supply a binary File part named file, a text location of course-baas-1010, and text isPrivate of true. Send the identity headers, but let your client generate the multipart Content-Type boundary.
file File submission-notes.txt
location Text course-baas-1010
isPrivate Text trueThe actual response returned a path and opening URL information. Retain the exact path. In this recording it was video-lab-mut4jkwd/course-baas-1010/submission-notes.txt. The object lives in storage; the application record stores a reference to it. Saving a field named attachmentPath does not upload any bytes or create an attachment access policy.

For a fresh timed opening link, request an explicit expiry:
POST /repository/file/signurl
{
"location": "video-lab-mut4jkwd/course-baas-1010/submission-notes.txt",
"options": {
"expiry": 900
}
}The response contained signedUrl; the course download redacts signatures. The File Storage API course demonstrates anonymous/private access, expiry, replacement and deletion checks. This platform exercise verified link generation, not every storage access case again. Your application still decides whether the current person may obtain the link.
5. Prepare the application record request
Prepare this request now, but send it only once in chapter 9 after the automation is active. That ordering is needed to test a real creation event. The recorded response shown in the explanatory chapter comes from that completed test; it is not an instruction to submit twice.
POST /repository/create
{
"datatype": "baas_submissions_1010",
"isNew": true,
"data": {
"reference": "BAAS-1010-A",
"title": "Application submission with attachment",
"attachmentPath": "video-lab-mut4jkwd/course-baas-1010/submission-notes.txt",
"status": "new"
}
}Replace attachmentPath with your upload’s returned path. isNew: true makes the create intent explicit. The saved record supplies sk; keep it as your submissionId. After the live test, read it using GET /repository/get/baas_submissions_1010/YOUR_SUBMISSION_SK. Confirm the reference, title, path and status rather than inferring persistence from a notification.
6. Understand the event-to-action mapping
A data_created trigger scoped to baas_submissions_1010 watches creation in that datatype. Its event context includes dataId and the source record’s fields in data. The configured action creates a record in a different collection, baas_receipts_1010. That destination is deliberately not the watched datatype, so receipt creation does not feed this particular trigger back into itself.

{{dataId}} becomes the receipt’s submissionId; {{data.reference}} becomes its reference. These are template expressions evaluated from event context. A literal fixed source ID would produce the wrong relationship on a later submission.
Four checks answer different questions: did configuration save, did a manual action work, did a real event execute, and did the correct destination record exist? None alone replaces all the others.
7. Save the draft automation
POST /automation
{
"name": "Backend course — submission receipt",
"trigger": {
"type": "data_created",
"config": {
"datatype": "baas_submissions_1010"
}
},
"actions": [
{
"type": "create_data",
"config": {
"datatype": "baas_receipts_1010",
"data": {
"submissionId": "{{dataId}}",
"reference": "{{data.reference}}",
"message": "AppEngine event processed"
}
}
}
]
}Retain the returned automation sk as automationId. The actual response normalized the concise trigger/action body into stored steps and returned draft status. Creating a draft does not activate its event listener.
This action creates an internal record; it does not send an email, webhook or customer notification. For deeper action configuration use Workflow and automation APIs. External providers need their own credentials, delivery and failure tests.
8. Test the action with explicit preview variables
Use your automation ID in the path:
POST /automation/6ac9e9a0f5d6b011f6e11a13/execute
{
"variables": {
"dataId": "manual-preview",
"data": {
"reference": "PREVIEW-1010"
}
}
}The observed response had success: true, an execution ID and a completed action_1. Query the receipts collection using the preview reference to confirm the result was stored:
POST /repository/find/baas_receipts_1010
{
"query": {
"data.reference": "PREVIEW-1010"
},
"options": {
"page": 1,
"pageSize": 10
}
}The saved preview had submissionId: manual-preview and reference: PREVIEW-1010. This proves the action mapping for these explicit inputs. It does not prove that a future Repository creation will activate the trigger.
9. Activate and prove a real event
Start the saved automation with POST /automation/start/YOUR_AUTOMATION_ID. The recorded endpoint returned success. Now send the chapter 5 application record request once, then retain its returned sk. Query the destination by that exact source identity:
POST /repository/find/baas_receipts_1010
{
"query": {
"data.submissionId": "6ac9e9c6f5d6b011f6e11a28"
},
"options": {
"page": 1,
"pageSize": 10
}
}Replace the recorded ID in this filter with your submission’s sk. The observed result contained one receipt with reference BAAS-1010-A and the exact source ID. Event processing can take time; wait for the specific result rather than repeatedly creating new source submissions. Distinguish an empty query caused by an incorrect ID from a failed trigger by inspecting execution history next.
10. Inspect execution history and its destination
Use the actual routes:
GET /automation/6ac9e9a0f5d6b011f6e11a13/runs
GET /automation/executions/exec_1791617478031_65seflj0f
The run list contained two completed executions: one manual execution and one event execution. Open the event execution ID. Inspect its action result and destination record ID, then independently read or query that destination. An automation being active says it is enabled; it is not proof of every later event’s success or an external provider’s delivery.
If a run fails, retain the execution ID, locate its failed step/error, and inspect the corresponding destination. Correct the particular cause. Do not replay all earlier operations blindly, because an upload or record save may already have succeeded.
11. Inspect the same data in Appmint Studio
In Database, choose All Collections → Custom. Search for App submissions — Backend course, open the collection, then Data. Locate BAAS-1010-A, choose the row’s Edit action and use Maximize. Read the saved reference and full attachment path. This is inspection; do not accidentally save unrelated changes.

Return to the collection library, choose Event receipts — Backend course, open Data, and inspect the row whose reference is BAAS-1010-A. Maximize its editor. Its submissionId must match the source record sk; the manual preview is a different row.

These screenshots were captured from the real training UI at 1920×1080 CSS pixels with device-pixel ratio 2. The displayed submission status is Reviewed, because the capture followed the update test below. The API and Studio inspect the same platform data; a separate custom interface does not require a second database.
12. Confirm create and update have different effects
With the automation active, apply this dotted partial update to your existing submission:
POST /repository/update-partial/baas_submissions_1010/6ac9e9c6f5d6b011f6e11a28
{
"data.status": "reviewed"
}The observed response was HTTP 201 with an empty body. Read the same record again; its status became reviewed. Query receipts for its ID again; the count remained one. Read the execution list; it still contained the one manual and one event run. This trigger listens for creation, not all updates.
If your application needs update-driven work, deliberately configure the appropriate update trigger and filters. Broadening the trigger just to make a test pass may repeat actions whenever an unrelated field changes. Repository partial update versus versioned full save is covered separately in the Database course.
13. Stop the exercise and keep the review evidence
Call POST /automation/stop/YOUR_AUTOMATION_ID, then read the automation. In the recorded final state its status was inactive. The submission, receipt and execution history remained available for inspection. The written examples do not require turning the course automation back on to review those results.
Do not delete the file simply because the upload chapter is over: the retained record still references it. Plan cleanup of related records and storage objects together when you no longer need the exercise.
14. Design recovery and expand by capability
An uploaded object can remain after a record save fails. A record can remain after a downstream action fails. Keep returned paths, record IDs and execution IDs so recovery acts on the intended objects. This exercise does not establish exactly-once delivery. Use explicit correlation/deduplication rules where repeated work would matter, and inspect previous results before retrying.
Additional capabilities have their own contracts:
| Capability | Continue with |
|---|---|
| Principal selection and customer access | Authentication |
| Custom queries, history and recovery | Database & Repository APIs |
| File visibility, links and lifecycle | File Storage APIs |
| Connected clients and subscriptions | Realtime APIs |
| Time-based execution | Sync and Scheduled Jobs |
| External services and callbacks | Integrations, OAuth and Webhooks |
| Application hosting and deployment | Platform Deployment |
15. Migrate one verified application slice
Map your application’s needs before copying collections. A customer identity may belong in the built-in customer model; application-specific data may belong in a custom collection. Keep an old-ID→new-ID map, including file paths and linked records. Preserve relationships deliberately instead of copying values that were only meaningful in the previous backend.

Test one real identity, one record flow, its attachment access and one downstream action. Password migration, customer ownership rules, object visibility and realtime behavior need independent checks. A similarly named API is not evidence of equivalent behavior. Keep the previous system available until your tested slice meets the actual requirements, then move traffic deliberately. This course demonstrates a connected AppEngine backend, not a universal Firebase migration tool.
16. Send the same contract from your chosen client
Use the chapter’s exact route, method and JSON body. The snippets below show request mechanics; the complete bodies above define the AppEngine operation. headers for JSON includes orgid, bearer authorization and Content-Type: application/json. For multipart, omit Content-Type and let the client supply its boundary.
TypeScript
const base = "https://appengine.appmint.io";
const route = "/repository/create";
const headers: Record<string, string> = {
orgid: organizationId,
Authorization: `Bearer ${staffToken}`,
"Content-Type": "application/json"
};
const response: Response = await fetch(base + route, {
method: "POST", headers, body: JSON.stringify(body)
});
const text = await response.text();
const result = text ? JSON.parse(text) : null;
console.log(response.status, result);
if (!response.ok) throw new Error(`AppEngine HTTP ${response.status}`);JavaScript
const response = await fetch(base + route, {
method: "POST",
headers: {
orgid: organizationId,
Authorization: `Bearer ${staffToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
const text = await response.text();
const result = text ? JSON.parse(text) : null;
console.log(response.status, result);
if (!response.ok) throw new Error(`AppEngine HTTP ${response.status}`);Python
import requests
response = requests.post(base + route, headers={
"orgid": organization_id,
"Authorization": f"Bearer {staff_token}",
"Content-Type": "application/json"
}, json=body, timeout=30)
print(response.status_code,
response.json() if response.content else None)
response.raise_for_status()curl
curl -sS -i -X POST "$BASE$ROUTE" \
-H "orgid: $ORG_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data-binary @request.jsonPostman
Set base, orgid and token in your private environment. Select the method and use {{base}} followed by the chapter’s route. Add orgid: {{orgid}} and bearer token authorization. For JSON requests, choose Body → raw → JSON and paste the complete body. For upload, choose Body → form-data, set file to type File and the remaining fields to Text. Inspect both HTTP status and body. Do not export a populated private token in a shared collection.
JSON
The complete request bodies appear next to each operation above. The video’s JSON response pane sometimes selects relevant fields for legibility; it is not always the full returned envelope. For GET requests there is no JSON body. A partial update can return an empty body; use the subsequent readback to establish its result.
Completion check
You have completed this backend slice when both definitions exist, the file upload returned a retained path, the active creation trigger produced one matching receipt, the event execution and destination agree, Studio shows the same records, and the update test did not create an extra receipt. Stop the exercise automation afterward. If one check fails, use its exact IDs and execution record to diagnose it rather than substituting a configuration screenshot for the missing outcome.