Describing your architecture in JSON
Describe your wakapp's infrastructure in a JSON file and import it in a few clicks — get started fast, clone an app, or let an AI generate the skeleton.
Describing your architecture in JSON
Describe your wakapp's infrastructure in a single JSON file and import it in a few clicks from the WakaStart console. Ideal for getting started quickly, cloning an existing app, or letting an AI generate the skeleton from a simple description.
Why use it?
- Get started fast. No more clicking 30 times to create 5 services, 1 database, and 2 URLs: a single JSON file and it's imported.
- Clone an app. Export an app that works, import it into another blank app: you start from the same skeleton.
- AI-assisted authoring. Describe in natural language what your wakapp should do, the AI produces the compliant JSON, you import it.
In short
The JSON describes only your architecture: repos, services, databases, storage, URLs, environments. Not your secrets, not your credentials, not your data volumes.
Once imported, your app has the skeleton of its infrastructure. You can then instantiate this skeleton in an environment (dev, staging, prod…): each environment created will follow the same structure (services, databases, storage, routing) as described in the JSON.
Importing an architecture
Prerequisites
- You have created the target app (however you like).
- The target app is blank on the architecture side: no service, no database, no bucket, no URL has been manually added yet.
If the app already contains resources, the Import button is disabled (with an explicit tooltip).
Procedure
- Open your app → Architecture
- Click Import (top right, next to Export)
- Step 1 — Paste + Validate
- Paste your JSON into the text field
- Click Validate: if the JSON contains a mistake (missing required field, invalid type, broken reference), the errors are shown with their exact JSON path so you can fix them line by line.
- Step 2 — Summary
- WakaStart shows what will be created:
text
Ressources qui seront créées : • 1 Repository • 2 Services • 1 Base de données • 1 Bucket de stockage • 2 URLs publiques • 2 Routes HTTP • 3 Environnements ✅ Les 2 URLs seront attachées automatiquement au domaine plateforme y.wakastart.app - If everything looks right, click Import.
- WakaStart shows what will be created:
- After the import
- Your app now has the complete skeleton of its architecture.
- You can then create an environment (
dev,staging,prod…) that will instantiate this skeleton: services, databases, and storage will be provisioned identically for that environment. - You'll fill in your variables and secrets per environment, build your image from the repo, then deploy.
Exporting an architecture
When?
- You have an app that works well and want to clone it to another target app (different WakaStart, different environment).
- You want to archive your app's infra description to version it in your repo.
- You want to start from a template for an AI to help you modify it.
Procedure
- Open your app → Architecture
- Click Export → a dialog opens with a summary of the transformations applied
- Choose either:
- Copy the JSON to the clipboard
- Download the
.jsonfile
What is exported and what isn't
| Included | Excluded |
|---|---|
App metadata (name, shortname) | Technical identifiers (regenerated on the target app) |
| Repos (read-only) | Git credentials |
| Services + expected env variables | Secrets, variable values |
| Databases and buckets | Stored data |
| URLs and HTTP routes | Custom domains (to be reconfigured) |
App-scope apex and redirects (via textual domainName) | The CUSTOM domain itself (to be reconfigured before re-import) |
| Environments (without technical identifiers) | Per-environment instances (to be created after import) |
Export = a read-only snapshot of the existing setup. Even if your source app has a repo managed by WakaStart, it will be exported as a referenced repo (
isManaged: false, withhtmlUrl). The target app will by default point to that same remote repo. If you'd rather have a new repo created on the target app (useful for cloning an app's architecture into a new one), edit the JSON before importing and setisManaged: true(see thereposblock section).
The JSON format — practical reference
Minimal skeleton
json{ "version": "1.0", "kind": "wakastart-app-architecture", "app": { "name": "Mon Application", "shortname": "monapp" } }
The app block and the version + kind header are the only three required fields. Everything else is optional: you only describe what you need.
shortname≠ WID. The JSON'sshortnameis informational: at import time, it's the target app's shortname that's used (already set at creation). So you can never risk a collision on the import side.
Available sections
| Section | Type | When to use it |
|---|---|---|
app | required | Metadata (name, shortname) |
repos | recommended | Your GitHub repos to connect |
serviceDefinitions | recommended | Your services (back, front, worker) |
databaseDefinitions | optional | Your databases |
storageBucketDefinitions | optional | Your S3 buckets |
appUrls + appRoutes | optional | Your public URLs + routing |
appApexUrls | optional | Exposing an app at the root of a CUSTOM domain (apex) |
appUrlRedirects | optional | App-wide URL redirects on a CUSTOM domain |
environments | optional | List your environments (dev/staging/prod) |
harborProject, openbaoSecretEngine | optional | Overriding the defaults (rarely useful) |
The repos block
Your GitHub repos to connect to your app. Two modes to choose from depending on whether the repo already exists or not:
Mode 1 — managed repo (isManaged: true): WakaStart creates it for you
json"repos": [ { "ref": "main", "name": "wk-monapp", "isManaged": true, "defaultBranch": "main", "visibility": "internal" } ]
WakaStart will create an empty GitHub repo in your instance's organization, give you its URL, and all you'll have to do is push your code to it.
The final GitHub name isn't exactly
name: WakaStart automatically prefixes it with your app's identifier to guarantee uniqueness. Example:name: "wk-monapp"on the app whose internal identifier isvnjdjtwill produce the repoWaka-Start/vnjdjt-wk-monapp. So you don't need to worry about name collisions.
Conventions:
name: lowercase, digits, hyphens (^[a-z0-9][a-z0-9-]*$), max 63 characters. Recommended convention:wk-prefix (e.g.wk-api,wk-front).defaultBranchdefaults tomain.visibility:internal(default),private,public.
Mode 2 — existing repo (isManaged: false): you reference it
json"repos": [ { "ref": "main", "name": "monapp", "fullName": "moncompte/monapp", "htmlUrl": "https://github.com/moncompte/monapp.git", "isManaged": false, "defaultBranch": "main" } ]
WakaStart doesn't touch the repo: it just uses it as a source for your services' builds. fullName and htmlUrl are required in this mode. The URL must point to a repo your WakaStart instance can access (deploy keys / GitHub App already configured).
Which mode should you choose?
- Starting from scratch →
isManaged: true. WakaStart creates the repo for you. - You already have an existing Git repo →
isManaged: false. WakaStart points to it.
If you omit
isManaged, it defaults totrue(creation by WakaStart). If you exported your architecture from another app, the JSON will always containisManaged: false(export captures the existing setup read-only). You can set it totrueby hand if you want to recreate a new repo instead of pointing to the old one.
The serviceDefinitions block
json"serviceDefinitions": [ { "ref": "api", "name": "API Backend", "shortname": "api", "repoRef": "main-repo", "rootDirectory": "./backend", "dockerfilePath": "Dockerfile", "buildContext": ".", "imageName": "api", "role": "BACK", "technology": "nestjs", "defaultExposure": "PUBLIC", "defaultProtocol": "HTTP", "defaultPort": 3000, "defaultCpuRequest": "100m", "defaultCpuLimit": "500m", "defaultMemoryRequest": "128Mi", "defaultMemoryLimit": "512Mi", "defaultHealthCheckPath": "/health", "defaultHealthCheckPort": 3000, "varRequirements": [ { "envVarName": "NODE_ENV", "required": true, "defaultValue": "production" }, { "envVarName": "LOG_LEVEL", "required": false, "defaultValue": "info" } ] } ]
Key fields:
| Field | Description |
|---|---|
ref | Identifier local to the JSON, used to link things together (e.g. appRoutes.serviceRef) |
shortname | Short identifier (max 12 characters), unique per app |
repoRef | References a repos[].ref |
role | FRONT, BACK, BFF, WORKER |
defaultExposure | PUBLIC, INTERNAL, PRIVATE |
varRequirements | The environment variables your service expects (the actual value is filled in per environment after import) |
The databaseDefinitions block
json"databaseDefinitions": [ { "ref": "pg", "name": "PostgreSQL principale", "shortname": "pg", "engineType": "POSTGRESQL", "defaultMode": "IN_CLUSTER", "defaultPlan": "essential", "defaultRegion": "gra", "defaultVersion": "16", "defaultDiskSizeGb": 10, "defaultReplicas": 1, "defaultDatabaseName": "monapp" } ]
engineType: POSTGRESQL, MYSQL, MONGODB, VALKEY.
defaultMode: IN_CLUSTER (cost-effective) or MANAGED (OVH managed DB).
The storageBucketDefinitions block
json"storageBucketDefinitions": [ { "ref": "uploads", "name": "Uploads utilisateurs", "shortname": "uploads", "defaultRegion": "gra", "defaultStorageClass": "STANDARD", "defaultVersioning": false } ]
The appUrls and appRoutes blocks
json"appUrls": [ { "ref": "api-url", "subdomain": "api", "domainRef": null }, { "ref": "app-url", "subdomain": "app", "domainRef": null } ], "appRoutes": [ { "serviceRef": "api", "urlRef": "api-url", "path": "/", "pathType": "PathPrefix" }, { "serviceRef": "front", "urlRef": "app-url", "path": "/", "pathType": "PathPrefix" } ]
domainRefis alwaysnullin the JSON. At import time, WakaStart automatically attaches your URLs to your instance's platform domain (typicallyy.wakastart.app). You immediately get ready-to-use FQDNs:api.dev.monapp.y.wakastart.app,app.prod.monapp.y.wakastart.app, etc.If you want a custom domain (e.g.
app.mondomaine.com), you'll configure it after import from the Architecture → URLs → Domains page, then attach your URLs to the custom domain (the Attach button).
The appApexUrls and appUrlRedirects blocks (CUSTOM domains only)
These two blocks describe capabilities tied to a CUSTOM domain: exposing the app at the root of a domain (apex) and redirecting one URL to another within the app (see URLs and routing and URL redirects).
json"appApexUrls": [ { "ref": "apex", "domainName": "monsupersite.com", "serviceRef": "web", "path": "/" } ], "appUrlRedirects": [ { "ref": "www-to-apex", "domainName": "monsupersite.com", "sourceSubdomain": "www", "targetApexRef": "apex" } ]
appApexUrls[]:
| Field | Description |
|---|---|
ref | Identifier local to the JSON (referenced by appUrlRedirects[].targetApexRef) |
domainName | required — domain name (e.g. monsupersite.com). See the note below: it's a textual name, not a reference to the AppDomain. |
serviceRef | required — references a serviceDefinitions[].ref: the service that receives the apex traffic |
path | Path (default /) |
pathType | PathPrefix (default) or Exact |
appUrlRedirects[]:
| Field | Description |
|---|---|
ref | Identifier local to the JSON |
domainName | required — same rule as appApexUrls[].domainName |
sourceSubdomain | Source subdomain; null (or omitted) = the source is the domain's apex. At most one redirect per (domainName, sourceSubdomain). |
targetUrlRef | References an appUrls[].ref — the target is an existing URL of the app |
targetApexRef | References an appApexUrls[].ref — the target is the app's apex |
Target: exactly one. Provide either
targetUrlRefortargetApexRef, never both, never neither.
domainNameis a name, not a reference to the domain. Unlike other resources, export never references theAppDomain(no ID, no credentials/DNS zone data): it only carries an informational, textualdomainName. At import time, this name is resolved by exact match against aCUSTOM-typeAppDomainalready present on the target app — import never creates a domain.
Domain not found = entry ignored, without failing. If no
AppDomain CUSTOMwith the same name yet exists on the target app, the apex/redirect entry is silently skipped — the import does not fail. Skipped entries are reported in the import result (skippedApexUrls/skippedUrlRedirects). Configure theCUSTOMdomain on the target app first, then re-import or recreate the apex/redirect from the console.
The environments block (optional)
json"environments": [ { "env": "dev", "shortname": "dev", "slug": "dev", "urlPrefix": "dev" }, { "env": "staging", "shortname": "stg", "slug": "staging", "urlPrefix": "staging" }, { "env": "prod", "shortname": "prd", "slug": "prod", "urlPrefix": "prod" } ]
If you include this block, WakaStart creates the environment records. You'll then need to create the instances (DatabaseInstance, StorageBucketInstance, ServiceInstance) per environment from the console.
Three concrete examples
1. Monolithic app (back + front)
json{ "version": "1.0", "kind": "wakastart-app-architecture", "app": { "name": "Hello World", "shortname": "hello" }, "repos": [ { "ref": "main", "name": "hello-world", "fullName": "moncompte/hello-world", "htmlUrl": "https://github.com/moncompte/hello-world", "isManaged": false, "defaultBranch": "main" } ], "serviceDefinitions": [ { "ref": "api", "name": "API", "shortname": "api", "repoRef": "main", "rootDirectory": "./backend", "dockerfilePath": "Dockerfile", "buildContext": ".", "imageName": "api", "role": "BACK", "defaultExposure": "PUBLIC", "defaultPort": 3000 }, { "ref": "web", "name": "Web", "shortname": "web", "repoRef": "main", "rootDirectory": "./frontend", "dockerfilePath": "Dockerfile", "buildContext": ".", "imageName": "web", "role": "FRONT", "defaultExposure": "PUBLIC", "defaultPort": 3000 } ], "appUrls": [ { "ref": "api-url", "subdomain": "api", "domainRef": null }, { "ref": "app-url", "subdomain": "app", "domainRef": null } ], "appRoutes": [ { "serviceRef": "api", "urlRef": "api-url", "path": "/", "pathType": "PathPrefix" }, { "serviceRef": "web", "urlRef": "app-url", "path": "/", "pathType": "PathPrefix" } ] }
2. App with a database and storage
Add the databaseDefinitions + storageBucketDefinitions blocks to the previous one, and declare the variables your backend expects:
json"databaseDefinitions": [ { "ref": "pg", "name": "PostgreSQL", "shortname": "pg", "engineType": "POSTGRESQL", "defaultMode": "IN_CLUSTER", "defaultVersion": "16", "defaultDiskSizeGb": 10 } ], "storageBucketDefinitions": [ { "ref": "files", "name": "Uploads", "shortname": "files" } ]
In serviceDefinitions[].varRequirements, declare the names your code expects:
json"varRequirements": [ { "envVarName": "DATABASE_URL", "required": true }, { "envVarName": "S3_BUCKET", "required": true } ]
You'll fill in the value of each variable from the Environments → Variables screen, per environment. For secrets, it's from Environments → Secrets.
3. Multi-service app with preconfigured environments
If you want to create the environments in a single import:
json"environments": [ { "env": "dev", "shortname": "dev", "slug": "dev", "urlPrefix": "dev" }, { "env": "prod", "shortname": "prd", "slug": "prod", "urlPrefix": "prod" } ]
Best practices
- One
shortnameper block: a serviceapiand a databaseapican coexist, but two services withshortname: "api"cannot. - Consistency of
varRequirements: declare all the variables your code expects at startup. This will save you from wondering why your pod crashes after deployment. - Choose the right repo mode:
isManaged: true(default) if you want WakaStart to create the GitHub repo for you (ideal for starting a new app).isManaged: falseif you're referencing an existing repo — in this case, don't forgetfullNameandhtmlUrl.
- Version the JSON in your repo: it's the description of your infra. Versioning it alongside your code lets you redeploy it identically on any instance.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
| Import button greyed out | The target app already contains resources | Create a blank app (never edited) or delete the resources |
Step 1 shows serviceDefinitions[ref=api].repoRef: unknown repo "main" | You're referencing a repoRef that doesn't exist in repos[] | Check the spelling / add the repo to repos[] |
Step 1 shows Required property 'role' is missing | Required field missing | Add role (FRONT, BACK, BFF, or WORKER) to the service in question |
| Import goes through but my URLs are DETACHED | The instance has no platform domain configured | Ask your administrator to add a PLATFORM_MANAGED domain, or add a custom domain and use the Attach button |
| 409 when clicking Import even though the preview was OK | Someone created a resource on the target app in the meantime | Delete the resource, or start over on a truly blank app |
Using an AI to generate the JSON
The format is deliberately predictable and strict so that an assistant like Claude, ChatGPT, Cursor, etc. produces valid JSON on the first try.
Sample prompt
Here's the description of my project: I want a Node.js API that connects to a PostgreSQL database and writes files to an S3 bucket, plus a Next.js frontend. I want WakaStart to create a new repo for me (managed mode). Generate the architecture JSON compliant with the WakaStart v1.0 schema (
kind: wakastart-app-architecture). Repos withisManaged: true(justref,name,isManaged),domainRef: nullon all URLs, nowid.
Variant for pointing to an existing repo:
[...] I already have a repo at
https://github.com/moncompte/super-app. Generate the JSON [...] withisManaged: false,fullName: "moncompte/super-app",htmlUrl: "https://github.com/moncompte/super-app.git".
Tips
- Ask the AI to validate the JSON before handing it to you (header keys
"version": "1.0","kind": "wakastart-app-architecture"). - If the Validate step shows errors, feed them back to the AI as-is, with their JSON path; it will fix them.
- Version the prompt alongside the JSON in your repo: your infra becomes a reproducible artifact.