WakaStart
Architecture as JSON

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.

Version v1.014 min read

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

  1. Open your app → Architecture
  2. Click Import (top right, next to Export)
  3. 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.
  4. 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.
  5. 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

  1. Open your app → Architecture
  2. Click Export → a dialog opens with a summary of the transformations applied
  3. Choose either:
    • Copy the JSON to the clipboard
    • Download the .json file

What is exported and what isn't

IncludedExcluded
App metadata (name, shortname)Technical identifiers (regenerated on the target app)
Repos (read-only)Git credentials
Services + expected env variablesSecrets, variable values
Databases and bucketsStored data
URLs and HTTP routesCustom 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, with htmlUrl). 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 set isManaged: true (see the repos block 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's shortname is 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

SectionTypeWhen to use it
apprequiredMetadata (name, shortname)
reposrecommendedYour GitHub repos to connect
serviceDefinitionsrecommendedYour services (back, front, worker)
databaseDefinitionsoptionalYour databases
storageBucketDefinitionsoptionalYour S3 buckets
appUrls + appRoutesoptionalYour public URLs + routing
appApexUrlsoptionalExposing an app at the root of a CUSTOM domain (apex)
appUrlRedirectsoptionalApp-wide URL redirects on a CUSTOM domain
environmentsoptionalList your environments (dev/staging/prod)
harborProject, openbaoSecretEngineoptionalOverriding 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 is vnjdjt will produce the repo Waka-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).
  • defaultBranch defaults to main.
  • 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 to true (creation by WakaStart). If you exported your architecture from another app, the JSON will always contain isManaged: false (export captures the existing setup read-only). You can set it to true by 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:

FieldDescription
refIdentifier local to the JSON, used to link things together (e.g. appRoutes.serviceRef)
shortnameShort identifier (max 12 characters), unique per app
repoRefReferences a repos[].ref
roleFRONT, BACK, BFF, WORKER
defaultExposurePUBLIC, INTERNAL, PRIVATE
varRequirementsThe 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" } ]

domainRef is always null in the JSON. At import time, WakaStart automatically attaches your URLs to your instance's platform domain (typically y.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[]:

FieldDescription
refIdentifier local to the JSON (referenced by appUrlRedirects[].targetApexRef)
domainNamerequired — domain name (e.g. monsupersite.com). See the note below: it's a textual name, not a reference to the AppDomain.
serviceRefrequired — references a serviceDefinitions[].ref: the service that receives the apex traffic
pathPath (default /)
pathTypePathPrefix (default) or Exact

appUrlRedirects[]:

FieldDescription
refIdentifier local to the JSON
domainNamerequired — same rule as appApexUrls[].domainName
sourceSubdomainSource subdomain; null (or omitted) = the source is the domain's apex. At most one redirect per (domainName, sourceSubdomain).
targetUrlRefReferences an appUrls[].ref — the target is an existing URL of the app
targetApexRefReferences an appApexUrls[].ref — the target is the app's apex

Target: exactly one. Provide either targetUrlRef or targetApexRef, never both, never neither.

domainName is a name, not a reference to the domain. Unlike other resources, export never references the AppDomain (no ID, no credentials/DNS zone data): it only carries an informational, textual domainName. At import time, this name is resolved by exact match against a CUSTOM-type AppDomain already present on the target app — import never creates a domain.

Domain not found = entry ignored, without failing. If no AppDomain CUSTOM with 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 the CUSTOM domain 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 shortname per block: a service api and a database api can coexist, but two services with shortname: "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: false if you're referencing an existing repo — in this case, don't forget fullName and htmlUrl.
  • 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

SymptomCauseFix
Import button greyed outThe target app already contains resourcesCreate 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 missingRequired field missingAdd role (FRONT, BACK, BFF, or WORKER) to the service in question
Import goes through but my URLs are DETACHEDThe instance has no platform domain configuredAsk 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 OKSomeone created a resource on the target app in the meantimeDelete 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 with isManaged: true (just ref, name, isManaged), domainRef: null on all URLs, no wid.

Variant for pointing to an existing repo:

[...] I already have a repo at https://github.com/moncompte/super-app. Generate the JSON [...] with isManaged: 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.

Going further