Skip to main content

Tutorial: Launch a Realtime Backend

The realtime-backend stack gives you a complete application backend in one launch. You get your own PostgreSQL database, an instant real-time GraphQL API over it (Hasura), end-user authentication as a service, and an object-storage bucket for uploads, all provisioned, wired together, and fronted by an edge domain with a valid certificate.

This is a "backend in a box". Instead of standing up a database, bolting a GraphQL layer onto it, running an identity provider, opening a bucket, and then connecting all four by hand, you launch one stack and a few minutes later you have a live, authenticated, real-time API. There is no backend code to write.

This tutorial walks through launching the stack from the console and from the API, opening the Hasura console at the edge domain, turning a table into a GraphQL API, running a query, a mutation, and a subscription, and setting a row-level permission tied to your end-user auth.

Who this is for

This stack is for anyone who wants a real backend without building and operating one:

  • Frontend and mobile teams that need data, an API, sign-in, and file uploads on day one.
  • Indie developers and small teams shipping a product without a backend engineer.
  • Prototypes and internal tools that need an authenticated, real-time data layer immediately.

You should be comfortable with SQL and basic GraphQL. You do not need to know Hasura, OIDC, or S3 beforehand. The stack wires those together for you.

What gets composed

The stack is pure composition over four FoundryDB primitives, provisioned in dependency order and wired together for you.

ResourceKindWhat it isWhy it is here
dbdatabasePostgreSQL 17, tier-1 (1 vCPU, 2 GB), 25 GB standard storageThe system of record. Your tables, your data, owned by your account. It also holds your end-user identities (in a managed _mdb_auth schema).
storagefilesAn object-storage bucket, 10 GB soft quotaHolds user uploads and assets. The API can read and write it directly.
apiappA Hasura application, tier-1, attached to db and storage, with auth enabled and an edge domainIntrospects PostgreSQL and serves the instant real-time GraphQL API. Protected by end-user auth, fronted by an edge domain with auto-cert.

Each piece earns its place:

  • PostgreSQL is the durable home for your application data. It is an ordinary FoundryDB managed database you can inspect, back up, scale, and connect to directly. Hasura does not own your data; PostgreSQL does.
  • Files gives you a place for uploads (avatars, attachments, exports) without standing up storage separately. The bucket is attached to the API, so the app boots already holding the S3 credentials.
  • Hasura reads the database schema and projects a GraphQL API onto it, with queries, mutations, real-time subscriptions, and fine-grained permissions, with no resolver to maintain.
  • End-user auth (auth: true) enables FoundryDB authentication as a service: a managed OpenID Connect issuer that signs your users in and writes their identities into your own PostgreSQL.
  • The edge domain (domain: edge) fronts the API with a custom edge hostname and an automatically issued certificate, so the GraphQL endpoint and console are reachable over HTTPS from the moment the stack is Running.
realtime-backend stack · compose, wire & introspect
RUNNING Protected, EU-resident realtime backend
Stack Templaterealtime-backendlaunch ⇉PostgreSQLtables · :5432Hasurawired · auth · edgeFilesobject bucketintrospect →GraphQL APIqueries · mutations · subscriptions
TemplatePostgreSQLHasuraFiles bucketEnd-user authEdge domainGraphQL API (introspected)wiring (env injected)

Prerequisites

  • An API token. See Authentication.
  • Set it in your shell so the examples below work as written:
export FOUNDRYDB_TOKEN="your-api-token"

Unlike the rag-chatbot stack, realtime-backend does not require a configured inference provider. It composes a database, a bucket, and an app.

Launch it from the console

  1. Open the Stacks catalog in the console.
  2. Pick Launch a realtime backend.
  3. Review the cost preview, broken down per resource, and accept it.
  4. Click Launch.

The stack provisions asynchronously. When it reaches Running, the detail view shows the live endpoint, the auth issuer URL, and links to the Hasura console.

Launch it from the API

The launch flow is always preview, then accept, then launch, then poll. You never create a billable resource without first accepting an up-to-date cost.

1. Preview the cost

curl -X POST https://api.foundrydb.com/stacks/preview \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"template_name": "realtime-backend"}'

The response breaks the monthly cost down by resource:

{
"template_name": "realtime-backend",
"currency": "EUR",
"monthly_total": 31.00,
"line_items": [
{
"symbolic_name": "db",
"kind": "database",
"description": "PostgreSQL tier-1 (1 vCPU, 2 GB) + 25 GB standard storage",
"monthly_cost": 14.00,
"is_ceiling": false
},
{
"symbolic_name": "storage",
"kind": "files",
"description": "Files bucket, 10 GB soft quota",
"monthly_cost": 3.00,
"is_ceiling": false
},
{
"symbolic_name": "api",
"kind": "app",
"description": "Hasura tier-1 (1 vCPU, 2 GB) + 10 GB storage, auth + edge domain",
"monthly_cost": 14.00,
"is_ceiling": false
}
]
}

The monthly_total is the figure you pass to the launch request. The numbers above are illustrative; always use the value returned by your own preview call, since pricing is computed fresh on every request.

2. Launch

Pass the monthly_total from the preview as accepted_monthly_cost:

curl -X POST https://api.foundrydb.com/stacks \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-backend",
"template_name": "realtime-backend",
"accepted_monthly_cost": 31.00
}'

The response is 201 Created with the stack in Pending status. Capture the stack ID for polling:

export STACK_ID="the-id-from-the-response"

If accepted_monthly_cost is omitted, or if it has drifted from the current estimate by more than EUR 0.01, the launch is rejected and you re-preview. See Launch a Stack for the full cost gate behaviour.

3. Poll until Running

The stack provisions asynchronously. Poll GET /stacks/{stack-id} until status reaches Running:

curl https://api.foundrydb.com/stacks/$STACK_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

Typical progression is PendingProvisioningWiringRunning. Most stacks complete within 5 minutes. Once the stack is Running, the endpoint_url carries the live Hasura URL on its edge domain, and the resources array reports each child's status:

{
"id": "...",
"name": "my-backend",
"status": "Running",
"endpoint_url": "https://my-backend.foundrydb.com",
"resources": [
{ "symbolic_name": "db", "kind": "database", "status": "Running", "service_id": "..." },
{ "symbolic_name": "storage", "kind": "files", "status": "Running", "service_id": "..." },
{ "symbolic_name": "api", "kind": "app", "status": "Running", "service_id": "..." }
]
}

The endpoint_url serves both the Hasura console and the GraphQL endpoint. The GraphQL endpoint is at /v1/graphql on that host. The auth issuer URL (the OIDC provider your clients sign in against) is reported on the api resource's auth configuration, in the form https://auth-<id>.foundrydb.com.

First run: from a table to an authenticated, real-time API

Now turn the empty backend into a working one.

1. Open the Hasura console

Open the endpoint_url from the previous step in your browser. Because the API uses an edge domain, this is a real foundrydb.com hostname with a valid certificate, no self-signed warning. The Hasura console gives you a schema explorer, a data tab, a permissions editor, and an interactive GraphQL playground (GraphiQL).

2. Create a table

In the console, go to the Data tab and create a table. You can use the UI or run SQL directly from the SQL runner:

CREATE TABLE notes (
id serial PRIMARY KEY,
owner_id text NOT NULL,
title text NOT NULL,
body text,
created_at timestamptz NOT NULL DEFAULT now()
);

The owner_id column holds the end-user identifier that auth will provide on each request. Track the table when prompted (the SQL runner has a Track this checkbox; the Data tab lists untracked tables with a Track button). Tracking is what tells Hasura to project a table into the GraphQL schema.

3. Watch it become GraphQL

As soon as the table is tracked, Hasura exposes it. Open the API tab (GraphiQL) and run a query:

query GetNotes {
notes {
id
title
created_at
}
}

You wrote no resolver. Introspection is live, so when you create or alter a table and track it, the GraphQL schema updates without a redeploy. Foreign keys become nested relationships automatically, so a child table referencing notes would give you notes { comments { body } } for free.

4. Run a mutation

Insert a row through the API:

mutation AddNote {
insert_notes_one(object: { owner_id: "user-1", title: "First note", body: "Hello" }) {
id
title
}
}

Expected response:

{
"data": {
"insert_notes_one": {
"id": 1,
"title": "First note"
}
}
}

5. Subscribe in real time

Subscriptions are live queries over a WebSocket. Run this in one GraphiQL tab, then insert a note in another, and watch the result update on its own:

subscription LiveNotes {
notes(order_by: { created_at: desc }, limit: 10) {
id
title
created_at
}
}

This is the same query syntax as a normal read, but the client receives a new result set whenever the underlying rows change. No polling.

6. Tie a permission to your end users

This stack ships with auth, so the GraphQL API can enforce per-user access at the database layer. In the Permissions tab for the notes table, add a user role and a select permission with a row filter that only returns rows owned by the requesting user:

{ "owner_id": { "_eq": "X-Hasura-User-Id" } }

Hasura reads the X-Hasura-User-Id session variable from the validated end-user token and applies the filter inside PostgreSQL, so a user only ever sees their own notes. You can scope columns, rows, inserts, updates, and deletes per role, all without writing authorization code in an application server.

7. Note the auth issuer and the bucket

Two things your client uses:

  • The auth issuer. Your frontend or mobile client signs users in against the managed OIDC issuer at https://auth-<id>.foundrydb.com (reported on the api resource). The issuer hosts the login pages, issues RS256-signed JWTs, and writes every user, session, and refresh token into a managed _mdb_auth schema in your PostgreSQL. Your client sends the resulting token to the GraphQL API, and Hasura uses its claims (such as the user id) to enforce the permissions you set above. See Integrate from Your App for the discovery and token-validation flow.
  • The files bucket. The 10 GB storage bucket is attached to the API. Use it for user uploads, generated assets, or exports. The app holds the bucket's S3 credentials, and you can also access the bucket directly with the S3 API. See Using the S3 API.

How the wiring works

The stack engine does not introduce a new provisioning path. It calls the same APIs you would call yourself, in dependency order, and injects the credentials each resource needs to reach the others.

Database attachment. When the stack reaches the Wiring state, the engine attaches the Hasura app to the PostgreSQL service and injects HASURA_GRAPHQL_DATABASE_URL, the connection string Hasura uses to reach your database. Hasura connects, introspects the schema, and exposes every tracked table as GraphQL. You never copy a connection string.

Files attachment. The storage bucket is attached to the same app, so the platform injects the S3_* bucket credentials into the app's container environment automatically. The API can read and write uploads without you pasting an access key.

End-user auth. Because auth: true is set, the platform enables authentication as a service on the app: it applies the _mdb_auth schema to your PostgreSQL, brings up a managed OIDC issuer, and assigns a fixed issuer URL. Identity data lives in your database, not in FoundryDB's infrastructure. The control plane stores only configuration (the issuer URL, signing-key references, and status). See App Auth Overview.

The edge domain. Because domain: edge is set, the API is fronted by a custom edge hostname with an automatically issued certificate. The GraphQL endpoint, console, and WebSocket subscriptions are all reachable over HTTPS the moment the stack is Running.

Cost and EU residency

All three resources in this stack are billed as ordinary FoundryDB services on your account. The cost preview itemizes them before launch, and the accepted figure is re-checked at launch time. There is no surprise bill, and no line item in this stack is a budget ceiling.

Like every stack, realtime-backend is composed entirely from EU-resident primitives. The PostgreSQL database, the files bucket, the Hasura app, and the auth issuer all run within the platform's European footprint. Because end-user identities live in your database, your users' data residency follows your database: it stays in Europe.

Teardown

Deleting the stack removes every child resource atomically, in reverse dependency order:

curl -X DELETE https://api.foundrydb.com/stacks/$STACK_ID \
-H "Authorization: Bearer $FOUNDRYDB_TOKEN"

Returns 202 Accepted. The reconciler tears down the app (and its auth issuer and edge domain), the bucket, and the database. No child resource is left orphaned. Teardown is irreversible, so export anything you want to keep first.

What's next