opened, (v) => { if (!v) requestClose() else opened = v } } target={modalTarget} formStyling title="Add a data table" contentClasses="flex flex-col" fixedWidth="md" fixedHeight="lg" >
goToStep(e.detail.index)} />
{#if run.steps.length} {#if run.running}

Setting up. This can take a few minutes — leave this open until it finishes.

{/if} {#if run.result} {@render poolerWarning()} {/if} {:else if wiz.step === 1} A data table runs on a database of your own. It stays yours — you can take it with you at any time.
{#if managedOffered} {#snippet managedIcon()} {/snippet} {@render providerCard( managedProvider ? wiz.provider! : preferredManagedProvider(), managedIcon, 'Windmill managed database (Recommended)', 'Windmill manages the databases and roles for you.' )} {/if} {#if supabaseAvailable} {#snippet supabaseIcon()} {/snippet} {@render providerCard( 'supabase', supabaseIcon, 'Supabase', 'Create a project, or connect one you already have. Signing in is required, and connecting an existing project needs its database password.' )} {/if} {#snippet ownIcon()} {/snippet} {@render providerCard( 'resource', ownIcon, 'Use a resource', 'Select or create a Postgres resource.' )}
{:else if wiz.step === 2} {#if wiz.provider === 'supabase'} {#if !supaOauth.authed} {#if supaOauth.pending} Sign in and approve Windmill in the Supabase window, then come back here. {:else} Windmill needs your approval on Supabase to see your databases. {/if} {:else} invalidate()} /> {/if} {:else if managedProvider} {@render managedKindToggle()} {#if managedKindUnavailable}

{#if $superadmin} Windmill has no Postgres it can manage databases on yet. Set up an external cluster, or turn Windmill's own database back on, in the instance settings. {:else} Only a superadmin can create a database Windmill manages. {/if}

{:else if wiz.provider === 'instance'} {@render instanceStep()} {:else} {@render externalStep()} {/if} {:else} {@render ownStep()} {/if} {:else}
{@render reviewStep()}
{/if}
{#if wiz.step > 1 && !run.steps.length && !(wiz.step === 2 && onlyResource)} {:else if canEditAfterFailure} {/if}
{#if wiz.step === 2 && !run.steps.length && wiz.provider === 'resource' && canSetUpExternal} {/if}
{#if wiz.provider === 'supabase' && !supaOauth.authed}

If you do not have a Supabase account you can create one for free.

{/if}
{#snippet externalHint(label: string)}
{/snippet} {#snippet managedKindToggle()} {@const internalReason = instanceAvailable ? "A database on Windmill's own Postgres." : !$superadmin ? "Only a superadmin can create a database on Windmill's own Postgres." : isCloudHosted() ? "Windmill's own Postgres is not available on cloud." : "Windmill's own Postgres is turned off for data tables in the instance settings."} {@const externalReason = externalInstanceAvailable ? 'A database on the external Postgres cluster Windmill administers.' : !$enterpriseLicense ? 'An external cluster is an Enterprise Edition feature.' : !$superadmin ? 'Only a superadmin can create a database on the external cluster.' : 'No external cluster is set up on this instance.'}
Postgres cluster
(managedKindUnavailable ? undefined : wiz.provider), (v) => { if (v === 'instance' || v === 'external_instance') selectProvider(v) } } > {#snippet children({ item })} {/snippet} {#snippet text()}
Internal: {internalReason} External: {externalReason}
{/snippet}
{#if !externalInstanceAvailable && canSetUpExternal} {@render externalHint('Set up externally managed Postgres')} {/if}
{/snippet} {#snippet providerCard(key: Provider, icon: Snippet, title: string, subtitle: string)} {@const selected = wiz.provider === key || (managedProvider && (key === 'instance' || key === 'external_instance'))} {/snippet} {#snippet instanceStep()} {@const instanceDbs = Object.entries(customInstanceDbs.current ?? {}) .filter(([_, db]) => db.tag === 'datatable') .map(([name, db]) => ({ name, db }))} {#if instanceDbs.length} wiz.instance.mode, (v) => { wiz.instance.mode = v wiz.instance.dbName = v === 'create' ? defaultInstanceDbName() : undefined } } > {#snippet children({ item })} {/snippet} {/if} {#if wiz.instance.mode === 'existing'} {@const shared = ( customInstanceDbs.current?.[wiz.instance.dbName ?? '']?.used_by_workspaces ?? [] ).filter((w) => w !== targetWorkspace)} {#if shared.length} This database is also used by workspace{shared.length > 1 ? 's' : ''} {shared.join(', ')}. Any data written here will be shared with {shared.length > 1 ? 'them' : 'it'}. {/if}
{#each instanceDbs as { name, db } (name)} {@const selected = wiz.instance.dbName === name} {@const others = (db.used_by_workspaces ?? []).filter((w) => w !== targetWorkspace)} {/each}
{:else}
Database name wiz.instance.dbName ?? '', (v) => (wiz.instance.dbName = v)} error={!!instanceNameError} inputProps={{ placeholder: defaultInstanceDbName() }} /> {#if !instanceNameError}

Created in the Windmill PostgreSQL instance when you finish. Windmill manages its credentials.

{/if}
{/if} {/snippet} {#snippet externalStep()} {@const externalDbs = Object.entries(externalInstanceDbs?.current ?? {}) .filter(([_, db]) => db.tag === 'datatable') .map(([name, db]) => ({ name, db }))} {#if externalDbs.length} wiz.external.mode, (v) => { wiz.external.mode = v wiz.external.dbName = v === 'create' ? externalDbName() : undefined } } > {#snippet children({ item })} {/snippet} {/if} {#if wiz.external.mode === 'existing'} {@const shared = ( externalInstanceDbs?.current?.[wiz.external.dbName ?? '']?.used_by_workspaces ?? [] ).filter((w) => w !== targetWorkspace)} {#if shared.length} This database is also used by workspace{shared.length > 1 ? 's' : ''} {shared.join(', ')}. Any data written here will be shared with {shared.length > 1 ? 'them' : 'it'}. {/if}
{#each externalDbs as { name, db } (name)} {@const selected = wiz.external.dbName === name} {@const others = (db.used_by_workspaces ?? []).filter((w) => w !== targetWorkspace)} {/each}
{:else}
Database name wiz.external.dbName ?? '', (v) => (wiz.external.dbName = v)} error={!!externalNameError} inputProps={{ placeholder: externalDbName() }} /> {#if !externalNameError}

Created on the external PostgreSQL cluster when you finish. Windmill manages its credentials.

{/if}
{/if} {/snippet} {#snippet ownStep()} {@const resources = pgResources.loading ? undefined : pgResources.current} {#if resources === undefined}

Loading resources...

{:else} {#if resources.length} Postgres resources in this workspace {:else}

A resource is a saved connection your scripts can use.

{/if}
{#each resources as r (r.path)} {@const selected = !wiz.own.creating && wiz.own.resourcePath === r.path} {/each}
{#if wiz.own.creating}
{@render newResourceForm()}
{/if}
{/if} {/snippet} {#snippet newResourceForm()}
{wiz.own.form === 'string' ? 'Connection string' : 'Connection'}
{#if wiz.own.form === 'string'} wiz.own.connectionString, (v) => { wiz.own.connectionString = v absorbConnectionString(v) invalidate() } } error={!!connectionStringError} inputProps={{ placeholder: 'postgres://user:password@host:5432/database' }} /> {:else}
Host wiz.own.fields.host, (v) => setField('host', v)} inputProps={{ placeholder: 'db.example.com' }} />
Port wiz.own.fields.port ?? '', (v) => setField('port', v === '' ? undefined : Number(v)) } inputProps={{ placeholder: '5432', type: 'number' }} />
Database wiz.own.fields.dbname ?? '', (v) => setField('dbname', v)} inputProps={{ placeholder: 'postgres' }} />
SSL mode certVerification, (v) => setAdvanced('accept_invalid_certs', v === 'default' ? undefined : v === 'accept') } clearable={false} />
Options wiz.own.fields.options ?? '', (v) => setField('options', v)} inputProps={{ placeholder: 'endpoint=ep-example-123456' }} />

Startup options sent to the server, such as the endpoint ID a Neon pooled or private connection needs.

setAdvanced('use_iam_auth', e.detail)} options={{ right: 'Authenticate with AWS IAM' }} /> {#if wiz.own.advanced.use_iam_auth}
Region wiz.own.advanced.region, (v) => setAdvanced('region', v)} inputProps={{ placeholder: 'us-east-1' }} />
{/if}
{/snippet} {#snippet poolerWarning()} {#if poolerUnavailable}
{poolerUnavailable} Windmill connects directly instead, which needs IPv6 from the workers, or the IPv4 add-on on the project. Granting the Supabase OAuth app database_pooling_config_read and connecting again restores the pooler.
{/if} {/snippet} {#snippet reviewStep()} {#if lastFailure} {lastFailure} {/if} {#if wiz.provider === 'supabase'} {@render poolerWarning()} {:else if wiz.provider === 'instance'} {:else if wiz.provider === 'external_instance'} {:else if !wiz.own.creating} {/if} {#if mintsResource} {/if} {#if sharesDatabaseWith} {sharesDatabaseWith.name} already uses this database. Both data tables would write to the same schema, so each one's tables are visible to the other and two tables of the same name collide. Migrations are tracked per data table, so those stay separate. {/if} {#if wiz.provider === 'supabase'} Your Supabase sign-in is not stored. If the database password ever changes, anyone with access to the project can sign in and reconnect it. Deleting the data table never deletes the Supabase project. {/if} {/snippet}