<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saif Ali</title>
    <description>The latest articles on DEV Community by Saif Ali (@sali_ac161a1b71406354896c).</description>
    <link>https://dev.to/sali_ac161a1b71406354896c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780544%2F353acf11-059d-471c-a779-825d98da6c00.png</url>
      <title>DEV Community: Saif Ali</title>
      <link>https://dev.to/sali_ac161a1b71406354896c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sali_ac161a1b71406354896c"/>
    <language>en</language>
    <item>
      <title>Configuration Management in Markdown</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sat, 31 Oct 2026 16:53:21 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/configuration-management-in-markdown-18d4</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/configuration-management-in-markdown-18d4</guid>
      <description>&lt;h2&gt;
  
  
  Introducing Korta: Configuration Management in Markdown
&lt;/h2&gt;

&lt;p&gt;Managing servers should not require a complicated stack just to install packages, create users, or keep services running.&lt;/p&gt;

&lt;p&gt;Those tasks are familiar to anyone who operates infrastructure. Yet the knowledge behind them often ends up scattered across shell scripts, internal wikis, configuration files, and someone’s memory. Over time, it becomes harder to answer a simple question: What is supposed to be running on this server?&lt;/p&gt;

&lt;p&gt;Korta is our attempt to make that answer easier to write, review, and apply.&lt;/p&gt;

&lt;p&gt;Korta is a command-line tool for configuration management in Markdown. You describe the desired state of your servers in a role, preview what would change, and apply it over SSH.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Packages

- nginx, curl

# Users

- deploy added to apps group, shell /bin/bash

# Directories

- /opt/app owned by deploy:apps, mode 0750

# Services

- nginx started, enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Then run:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;korta plan role.md &lt;span class="nt"&gt;-i&lt;/span&gt; hosts.md
korta apply role.md &lt;span class="nt"&gt;-i&lt;/span&gt; hosts.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan checks your hosts and shows the changes Korta would make. The apply command plans first, asks for confirmation, and then carries out the changes.&lt;/p&gt;

&lt;p&gt;There is no agent to install on each server. Korta connects over SSH and uses root or passwordless sudo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration you can read
&lt;/h2&gt;

&lt;p&gt;A role is a Markdown document organized around the things you want to manage. You can describe users, groups, packages, directories, files, services, kernel settings, firewall rules, updates, and custom commands.&lt;/p&gt;

&lt;p&gt;Your hosts live in a Markdown inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ssh"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Production&lt;/span&gt;

&lt;span class="k"&gt;identity&lt;/span&gt;: ~/.ssh/prod_ed25519
&lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="k"&gt;web1&lt;/span&gt; deploy@web1.internal env=prod

&lt;span class="c1"&gt;# Staging&lt;/span&gt;

&lt;span class="k"&gt;identity&lt;/span&gt;: ~/.ssh/staging_ed25519
&lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="k"&gt;web2&lt;/span&gt; deploy@web2.internal env=staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  You can use labels to select a subset:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;korta apply role.md &lt;span class="nt"&gt;-i&lt;/span&gt; hosts.md &lt;span class="nt"&gt;--limit&lt;/span&gt; &lt;span class="nb"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Markdown gives teams a familiar place to keep configuration alongside the context that explains it. A role can live in Git, be reviewed in a pull request, and be read by the next person responsible for the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Korta is for
&lt;/h2&gt;

&lt;p&gt;Korta is for people who manage Linux servers and want a straightforward way to describe and maintain their configuration.&lt;/p&gt;

&lt;p&gt;That includes developers running a few virtual machines, small teams maintaining their own infrastructure, platform engineers who want reviewable roles, and consultants managing servers across multiple environments.&lt;/p&gt;

&lt;p&gt;It is especially useful when you want to keep control of your servers and use the SSH access you already have. The core CLI can run directly from your machine without a central manager.&lt;/p&gt;

&lt;p&gt;Korta supports Ubuntu, Debian, RHEL, Rocky Linux, AlmaLinux, and CentOS Stream. Hosts need SSH and appropriate privileges; they do not need a Korta agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why now
&lt;/h2&gt;

&lt;p&gt;More teams are reexamining how they run software. Some want more control over infrastructure costs. Others want to run applications and databases on their own servers, or simply make existing systems easier to understand and maintain.&lt;/p&gt;

&lt;p&gt;At the same time, teams are increasingly using AI to help write operational code. That makes review more important: generated configuration still needs to be understandable, validated, and approved by the people responsible for the machines.&lt;/p&gt;

&lt;p&gt;Korta starts with a workflow built around those needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write the desired state.&lt;/li&gt;
&lt;li&gt;See what would change.&lt;/li&gt;
&lt;li&gt;Review and approve it.&lt;/li&gt;
&lt;li&gt;Apply it to the selected hosts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The goal is to make routine server work easier to share and repeat, while keeping the operator in control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source
&lt;/h2&gt;

&lt;p&gt;Server configuration is foundational. The tool that interprets it should be something people can inspect, understand, and improve.&lt;/p&gt;

&lt;p&gt;We want Korta’s core to be open source so developers and operators can see how roles are parsed, how plans are produced, and what runs over SSH. We also want teams to be able to use the CLI in their own environments and contribute fixes, ideas, and support for real-world use cases.&lt;/p&gt;

&lt;p&gt;Open source is also a practical way to build a better infrastructure tool. Servers, operating systems, and operational requirements vary widely. The people using Korta will encounter cases we have not, and the project will be stronger with their feedback and contributions.&lt;/p&gt;

&lt;p&gt;The open-source core includes the korta CLI, Markdown role engine, inventory parser, SSH client, and operating-system detection.&lt;/p&gt;

&lt;p&gt;Korta Enterprise builds on that core for teams that need a shared manager. It adds a web console and API, approval and audit workflows, an encrypted vault, an AI role assistant, and deployment features for databases and applications. The same CLI and role files work with the Enterprise manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;Write a small role for a staging server, add the host to an inventory, and run korta plan. Review the output, then apply it when you are ready.&lt;/p&gt;

&lt;p&gt;The core project is at github.com/nexusrun/korta (&lt;a href="https://github.com/nexusrun/korta" rel="noopener noreferrer"&gt;https://github.com/nexusrun/korta&lt;/a&gt;). We’re looking forward to sharing it and hearing what you build with it.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>devtools</category>
      <category>systems</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Databases on NEXUS AI: Managed, Portable, and Ready for AI-Built Apps</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Mon, 26 Oct 2026 23:21:48 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/databases-on-nexus-ai-managed-portable-and-ready-for-ai-built-apps-59c4</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/databases-on-nexus-ai-managed-portable-and-ready-for-ai-built-apps-59c4</guid>
      <description>&lt;h2&gt;
  
  
  Databases on NEXUS AI: Managed, Portable, and Ready for AI-Built Apps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Databases · Platform · AI Deployments&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reviewed:&lt;/strong&gt; October 26, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;CLI syntax verified:&lt;/strong&gt; NEXUS AI CLI 4.3.2&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 13 minutes&lt;/p&gt;

&lt;p&gt;Most AI-built apps fail at the same point. The interface works, the demo impresses, and then someone asks where the data lives, who backs it up, and what happens when the AI agent runs a migration at 2 a.m.&lt;/p&gt;

&lt;p&gt;Generating code got fast. Running the database underneath it did not get any easier.&lt;/p&gt;

&lt;p&gt;NEXUS AI gives every app a database path that matches where it is in its life: a database that starts with the app, a standalone database you share between apps, the same database in your own AWS, Azure, or Google Cloud account, and a guarded way to connect databases you already run. One dashboard, one CLI, and one MCP server manage all of them.&lt;/p&gt;

&lt;p&gt;This article explains what each option is, where to use it, how it looks from a management point of view, how portable your data stays, and why the design fits AI deployments in particular.&lt;/p&gt;
&lt;h2&gt;
  
  
  What NEXUS AI databases are
&lt;/h2&gt;

&lt;p&gt;NEXUS AI databases are the data layer of the NEXUS AI deployment platform. There are four ways to use them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Engines&lt;/th&gt;
&lt;th&gt;Manage with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App database service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A database that runs next to one app, created with the deploy&lt;/td&gt;
&lt;td&gt;PostgreSQL, MySQL, MongoDB, Redis&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managed database on NEXUS AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A standalone database you create once and attach to any app on NEXUS AI&lt;/td&gt;
&lt;td&gt;PostgreSQL, Redis&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus managed-db --local&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managed cloud database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A standalone database on Amazon RDS, Google Cloud SQL, or Azure Database, in the platform account or yours&lt;/td&gt;
&lt;td&gt;PostgreSQL, MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus managed-db --provider&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connected external database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A database you already run elsewhere, connected for schema inspection, guarded queries, and AI fix proposals&lt;/td&gt;
&lt;td&gt;PostgreSQL, MySQL&lt;/td&gt;
&lt;td&gt;Dashboard and MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four sit behind the same organization, the same role-based permissions, and the same tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where to use each option
&lt;/h2&gt;
&lt;h3&gt;
  
  
  App database service: for prototypes and single apps
&lt;/h3&gt;

&lt;p&gt;Add a database when you deploy, and NEXUS AI runs it next to your app and injects the connection settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/helpdesk &lt;span class="nt"&gt;--services&lt;/span&gt; postgres,redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it when one app owns its data: a prototype, an internal tool, a side project, or the first version of a product. It is the fastest path from "generated code" to "working app with real data".&lt;/p&gt;

&lt;p&gt;Each service gets backups you control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db services helpdesk                               &lt;span class="c"&gt;# find the service ID&lt;/span&gt;
nexus db backup &amp;lt;service-id&amp;gt;                             &lt;span class="c"&gt;# back up now&lt;/span&gt;
nexus db backup-schedule &amp;lt;service-id&amp;gt; &lt;span class="nt"&gt;--enable&lt;/span&gt; &lt;span class="nt"&gt;--retention&lt;/span&gt; 14   &lt;span class="c"&gt;# daily, keep 14 days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managed database on NEXUS AI: for data shared by several apps
&lt;/h3&gt;

&lt;p&gt;When two apps need the same data (an API and a worker, or an app and its admin panel), the database should not belong to either one. Create it on its own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus managed-db create orders-db &lt;span class="nt"&gt;--local&lt;/span&gt; &lt;span class="nt"&gt;--engine&lt;/span&gt; postgres
nexus managed-db attach orders-db &lt;span class="nt"&gt;--deployment&lt;/span&gt; orders-api
nexus managed-db attach orders-db &lt;span class="nt"&gt;--deployment&lt;/span&gt; orders-worker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each app receives &lt;code&gt;DATABASE_URL&lt;/code&gt; (or &lt;code&gt;REDIS_URL&lt;/code&gt; for Redis) on its next deploy. Use &lt;code&gt;--env-prefix&lt;/code&gt; to give a second database a different name, such as &lt;code&gt;ANALYTICS_URL&lt;/code&gt;. Deleting an app no longer puts the data at risk, because the database has its own lifecycle.&lt;/p&gt;

&lt;p&gt;Managed databases on NEXUS AI listen on the primary host's private Docker network, so they attach to apps deployed on NEXUS AI on that host.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed cloud database: for production and compliance
&lt;/h3&gt;

&lt;p&gt;When the data needs a cloud provider's durability, encryption, and controls, create the database on Amazon RDS, Google Cloud SQL, or Azure Database for PostgreSQL and MySQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus managed-db create billing-db &lt;span class="nt"&gt;--provider&lt;/span&gt; AWS_RDS &lt;span class="nt"&gt;--engine&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--engine-version&lt;/span&gt; 17.10 &lt;span class="nt"&gt;--instance-class&lt;/span&gt; db.t3.micro &lt;span class="nt"&gt;--allocated-gb&lt;/span&gt; 20 &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or let the deploy create it for you. On a cloud provider, &lt;code&gt;--create-db&lt;/code&gt; provisions a managed database paired with the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/your-org/billing &lt;span class="nt"&gt;--provider&lt;/span&gt; aws_app_runner &lt;span class="nt"&gt;--create-db&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazon RDS instances are created with storage encryption and seven days of automated backups. On AWS you can choose &lt;code&gt;--network-mode vpc&lt;/code&gt; to keep the database private, reachable only from App Runner apps in the same account.&lt;/p&gt;

&lt;p&gt;If you have added your own cloud credentials in Deployment Providers, new cloud databases are created in &lt;strong&gt;your&lt;/strong&gt; AWS account or Azure subscription, not the platform's. See &lt;a href="//./bring-your-own-cloud-account-aws-azure-gcp.md"&gt;Bring Your Own Cloud Account&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connected external database: for data you already have
&lt;/h3&gt;

&lt;p&gt;Not every database should move. Connect an existing PostgreSQL or MySQL database in the dashboard, then inspect its schema, preview queries, and get AI-proposed fixes from your app's error logs, all with guardrails described below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick decision guide
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your situation&lt;/th&gt;
&lt;th&gt;Start with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prototype or demo with real data&lt;/td&gt;
&lt;td&gt;App database service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One app, small team, needs backups&lt;/td&gt;
&lt;td&gt;App database service with a backup schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Several apps share the same data&lt;/td&gt;
&lt;td&gt;Managed database on NEXUS AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production workload, customer data, uptime commitments&lt;/td&gt;
&lt;td&gt;Managed cloud database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance requires data in your own account&lt;/td&gt;
&lt;td&gt;Managed cloud database with your own cloud provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data already lives somewhere else&lt;/td&gt;
&lt;td&gt;Connected external database&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The management point of view
&lt;/h2&gt;

&lt;p&gt;For the person responsible for the platform, databases are usually where tools sprawl: one console per cloud, scripts for backups, and a wiki page about who has the passwords. NEXUS AI keeps them in one place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One inventory.&lt;/strong&gt; Every database in the organization, whichever option and provider, appears in the dashboard and in &lt;code&gt;nexus managed-db list&lt;/code&gt; and &lt;code&gt;nexus db services&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions by role.&lt;/strong&gt; Reading data and changing it are separate permissions. A teammate with read access can run &lt;code&gt;SELECT&lt;/code&gt; queries; write and schema-changing queries require the &lt;code&gt;managed_databases.manage&lt;/code&gt; permission. Assign roles per person, and use scoped access tokens for CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backups with a floor.&lt;/strong&gt; Scheduled backups keep at least 7 days of history, and retention can be raised but never lowered below that. Expired automatic backups are pruned on schedule; backups you upload are never deleted automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recovery that does not overwrite.&lt;/strong&gt; Managed database restores always create a new database from a snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus managed-db snapshot orders-db &lt;span class="nt"&gt;--notes&lt;/span&gt; &lt;span class="s2"&gt;"before v2 migration"&lt;/span&gt;
nexus managed-db restore orders-db &lt;span class="nt"&gt;--snapshot&lt;/span&gt; &amp;lt;snapshot-id&amp;gt; &lt;span class="nt"&gt;--new-name&lt;/span&gt; orders-db-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You compare the restored copy with the original before you switch anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safe sharing of backups.&lt;/strong&gt; Instead of handing out credentials, create a signed download link that expires in 30 to 3,600 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--share&lt;/span&gt; &lt;span class="nt"&gt;--ttl&lt;/span&gt; 900
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Plan limits you can predict.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Standalone managed databases&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;App database services only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Portability: your data is never trapped
&lt;/h2&gt;

&lt;p&gt;A database platform is only as good as your ability to leave it. NEXUS AI is built on that assumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standard engines, no proprietary layer.&lt;/strong&gt; PostgreSQL, MySQL, MongoDB, and Redis run as themselves. Your app connects with its normal driver and a normal connection URL. No SDK, no special query language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backups in native formats.&lt;/strong&gt; PostgreSQL backups use &lt;code&gt;pg_dump&lt;/code&gt;, MySQL uses &lt;code&gt;mysqldump&lt;/code&gt;, and MongoDB uses &lt;code&gt;mongodump&lt;/code&gt;. Download any backup and restore it with standard tools on any server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--out&lt;/span&gt; ./prod.dump
pg_restore &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TARGET_DATABASE_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--no-owner&lt;/span&gt; ./prod.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Move data between services.&lt;/strong&gt; Copy production into staging without leaving the platform, as long as both use the same engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore-to &amp;lt;staging-service-id&amp;gt; &amp;lt;backup-id&amp;gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or bring data in from outside with &lt;code&gt;nexus db backup-upload&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection settings, not code changes.&lt;/strong&gt; Apps read their database from environment variables. Detach one database and attach another, redeploy, and the app follows. Moving from a database on NEXUS AI to Amazon RDS is a data copy and an attach, not a rewrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your cloud account, your resources.&lt;/strong&gt; Cloud databases created with your own provider credentials live in your account. If you stop using NEXUS AI, they keep running where they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flexibility: one workflow, many shapes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Four engines&lt;/strong&gt; for different jobs: relational data in PostgreSQL or MySQL, documents in MongoDB, caches and queues in Redis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four homes&lt;/strong&gt; for standalone databases: NEXUS AI, Amazon RDS, Google Cloud SQL, and Azure Database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public or private networking&lt;/strong&gt; on AWS: public endpoints restricted to allowed address ranges, or VPC mode with no public endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Several databases per app&lt;/strong&gt;, each under its own environment variable prefix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three ways in&lt;/strong&gt;: the dashboard for people, the CLI for scripts and CI, and the MCP server for AI assistants.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who it is for
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Founders and solo builders&lt;/strong&gt; who generated an app with AI and need real data, backups, and a URL today, without learning a cloud console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product and agency teams&lt;/strong&gt; shipping many small apps, who need each one to have a database that is backed up, separated, and easy to hand over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform and DevOps leads&lt;/strong&gt; who want one inventory, consistent backups, and role-based access across every database their teams create.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulated and enterprise teams&lt;/strong&gt; who need the data in their own cloud account, encrypted, with their own audit trail, while developers keep a simple workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why NEXUS AI is a strong fit for AI deployments
&lt;/h2&gt;

&lt;p&gt;AI changes who touches the database. An agent in Claude, Cursor, or Codex can now create a schema, write a migration, and run it, often faster than a person can review it. That is useful, and it needs guardrails that do not depend on the agent behaving perfectly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI assistants get real database tools.&lt;/strong&gt; The NEXUS AI MCP server exposes database operations directly to AI assistants: listing services, taking and restoring backups, creating and attaching managed databases, taking snapshots, inspecting schemas, and running queries. An agent can deploy an app with a database and back it up before a migration in the same conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every query is classified before it runs.&lt;/strong&gt; Queries are sorted into reads, data changes, schema changes, and blocked operations. Statements such as &lt;code&gt;DROP DATABASE&lt;/code&gt;, role changes, extension installs, and server file access are refused outright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preview before execute.&lt;/strong&gt; On connected databases, a preview runs read queries with &lt;code&gt;EXPLAIN&lt;/code&gt; and does not apply changes, so the agent (and you) can see what a statement would do first. Execution runs with a statement timeout and returns at most 1,000 rows by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions apply to agents too.&lt;/strong&gt; An assistant acts with the permissions of the person or token it uses. Give an agent a read-only role and it cannot change data, whatever it is asked to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixes are proposals, not surprises.&lt;/strong&gt; NEXUS AI can read an app's error logs and propose a schema fix, such as a missing column or index. The fix is stored as a proposal and is applied only when someone applies it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backups are one command away from any agent.&lt;/strong&gt; The habit that saves teams most often is simple: snapshot before a migration. With MCP, asking the agent to "take a snapshot first" is all it takes, and restores never overwrite the original.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The database follows the app's lifecycle.&lt;/strong&gt; AI-built apps change quickly: prototype on Monday, shared with a team on Wednesday, in production the next month. The path from app database service to managed database to your own cloud account uses the same commands and the same connection variables at every step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to know before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Managed databases on NEXUS AI support PostgreSQL and Redis; use a cloud provider for managed MySQL.&lt;/li&gt;
&lt;li&gt;Managed databases on NEXUS AI attach only to apps running on the NEXUS AI primary host.&lt;/li&gt;
&lt;li&gt;Private (VPC) cloud databases must be in the same cloud account as the app that uses them.&lt;/li&gt;
&lt;li&gt;App database service restores replace the current data in place. Take a fresh backup first if you may want the current state back.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;restore-to&lt;/code&gt; requires the source and target to use the same engine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What databases does NEXUS AI support?
&lt;/h3&gt;

&lt;p&gt;PostgreSQL, MySQL, MongoDB, and Redis as app database services. PostgreSQL and Redis as managed databases on NEXUS AI. PostgreSQL and MySQL as managed cloud databases on Amazon RDS, Google Cloud SQL, and Azure Database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I export my data and leave?
&lt;/h3&gt;

&lt;p&gt;Yes. Backups use the engines' native dump formats, so you can download them and restore them anywhere with standard tools. Cloud databases created with your own credentials are already in your account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it safe to let an AI agent run queries?
&lt;/h3&gt;

&lt;p&gt;NEXUS AI classifies every query, blocks dangerous statements, separates read and write permissions, and offers a preview mode. Give agents the least access they need, and ask them to snapshot before migrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the difference between &lt;code&gt;nexus db&lt;/code&gt; and &lt;code&gt;nexus managed-db&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;nexus db&lt;/code&gt; manages databases that run alongside one app, created with &lt;code&gt;--services&lt;/code&gt;. &lt;code&gt;nexus managed-db&lt;/code&gt; manages standalone databases that you create once and attach to any number of apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I move from a database on NEXUS AI to Amazon RDS later?
&lt;/h3&gt;

&lt;p&gt;Yes. Create the RDS database, copy the data with a backup and restore, attach the new database to the app, and redeploy. The app keeps reading &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long are backups kept?
&lt;/h3&gt;

&lt;p&gt;Scheduled backups are kept for 7 days by default, and you can raise retention per service. Backups you upload are kept until you delete them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the database your app needs today
&lt;/h2&gt;

&lt;p&gt;Deploy your next app with &lt;code&gt;--services postgres&lt;/code&gt;, turn on a backup schedule, and connect your AI assistant to NEXUS AI through MCP. When the data outgrows one app, create a managed database and attach it. When it needs a cloud provider or your own account, move it there with the same tools.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>mongodb</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Virtual Models: One Model Name, Any Model Behind It</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Mon, 26 Oct 2026 18:50:55 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/virtual-models-one-model-name-any-model-behind-it-2b1g</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/virtual-models-one-model-name-any-model-behind-it-2b1g</guid>
      <description>&lt;h2&gt;
  
  
  Virtual Models: One Model Name, Any Model Behind It
&lt;/h2&gt;

&lt;p&gt;A new model ships almost every week. One is cheaper, one is faster, one finally handles your hardest coding task. Taking advantage of it should be a five-minute decision. In most stacks it is a small project: change an environment variable in three services, update a provider SDK, add a new API key, redeploy, and hope nothing else breaks.&lt;/p&gt;

&lt;p&gt;Virtual models remove that work. This article explains what a virtual model is, when to use one, why it fits the way AI agents and apps are built today, and how to route different jobs to different models using the routing strategies in &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt;, an open source, OpenAI-compatible AI gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a virtual model?
&lt;/h2&gt;

&lt;p&gt;A virtual model is a model name you define, plus a rule for what that name means.&lt;/p&gt;

&lt;p&gt;Your application sends the name in the normal &lt;code&gt;model&lt;/code&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hi"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway reads &lt;code&gt;assistant&lt;/code&gt;, looks up its rule, and decides which real provider model serves the request. The rule can be as simple as "&lt;code&gt;assistant&lt;/code&gt; means &lt;code&gt;anthropic/claude-sonnet-4-6&lt;/code&gt;" or as rich as "send easy prompts to a cheap model, hard prompts to a strong one, and fail over to a second provider if the first is down".&lt;/p&gt;

&lt;p&gt;Your app never learns which model answered unless you want it to. It only knows the name.&lt;/p&gt;

&lt;p&gt;In ZRouter, a virtual model comes in two forms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redirect (alias).&lt;/strong&gt; A name that points at one or more real models. This is what most of this article is about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access policy.&lt;/strong&gt; A rule with no target that restricts who can use an existing model, by user path, or disables it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You create them in the dashboard (&lt;strong&gt;Models → New virtual model&lt;/strong&gt;), in &lt;code&gt;config.yaml&lt;/code&gt;, through the &lt;code&gt;VIRTUAL_MODELS&lt;/code&gt; environment variable, or through the admin API. When prepaid billing is enabled, customers can also create private virtual models in their own dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem it solves: model choice is wired into your code
&lt;/h2&gt;

&lt;p&gt;Without a gateway, the model is usually decided in three places at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The endpoint.&lt;/strong&gt; Each provider has its own base URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The credentials.&lt;/strong&gt; Each provider has its own API key, stored in each app's environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model ID.&lt;/strong&gt; A string like &lt;code&gt;gpt-4o-2024-08-06&lt;/code&gt; hardcoded or set in an environment variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Changing models means changing one or all of these, in every service that calls the model. For a single chatbot that is annoying. For a company with ten apps and twenty agents it is a migration.&lt;/p&gt;

&lt;p&gt;With a gateway and virtual models, all three are fixed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One endpoint:&lt;/strong&gt; &lt;code&gt;https://zrouter.si/v1&lt;/code&gt; (or your self-hosted gateway), for every provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One key:&lt;/strong&gt; your gateway key. Provider keys live in the gateway, not in each app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One stable model name:&lt;/strong&gt; &lt;code&gt;assistant&lt;/code&gt;, &lt;code&gt;coder&lt;/code&gt;, &lt;code&gt;summarizer&lt;/code&gt;, whatever you choose.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://zrouter.si/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ZROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;coder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refactor this function...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switching &lt;code&gt;coder&lt;/code&gt; from one model to another is an edit in the gateway. No environment variable changes, no new endpoint, no redeploy, no restart of the agent. The next request simply goes to the new model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why virtual models fit today's AI agents and apps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Models change faster than release cycles
&lt;/h3&gt;

&lt;p&gt;Frontier models are updated, renamed, repriced, and deprecated on a monthly rhythm. If every model change needs a code release, you either fall behind or spend your team's time on plumbing. A virtual model turns "upgrade the model" into a configuration change you can make and undo in seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agents call models from many places
&lt;/h3&gt;

&lt;p&gt;A modern agent is not one model call. It plans, writes code, calls tools, summarizes results, checks its own work, and often runs sub-agents. Each step has a different ideal model. Hardcoding a model ID in each step spreads model decisions across your codebase. Naming each step by its job (&lt;code&gt;planner&lt;/code&gt;, &lt;code&gt;coder&lt;/code&gt;, &lt;code&gt;reviewer&lt;/code&gt;, &lt;code&gt;summarizer&lt;/code&gt;) keeps the agent code about the job, and moves the model decision to the gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many tools only let you set one model
&lt;/h3&gt;

&lt;p&gt;Coding agents and AI apps such as Claude Code, Codex, Cursor, OpenCode, OpenWebUI, LibreChat, and n8n let you set a base URL and a model name, and nothing more. A virtual model lets that single name carry routing logic, failover, cost control, and guardrails the tool itself knows nothing about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliability needs more than one provider
&lt;/h3&gt;

&lt;p&gt;Providers have outages and rate limits. A virtual model with several targets keeps serving as long as one target is available, without the app writing retry logic for each provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost needs to follow the task
&lt;/h3&gt;

&lt;p&gt;Sending every request to the strongest model is expensive. Sending every request to the cheapest model hurts quality. Routing by task, time of day, or request difficulty lets you pay for capability only where it matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teams need governance in one place
&lt;/h3&gt;

&lt;p&gt;Who can use which model, which guardrails apply, and how much they can spend are platform decisions. Putting them on the virtual model means every app that uses the name gets the same policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use a virtual model
&lt;/h2&gt;

&lt;p&gt;Use one when any of these is true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You expect to &lt;strong&gt;change or upgrade&lt;/strong&gt; the model behind a feature.&lt;/li&gt;
&lt;li&gt;More than one app or agent step &lt;strong&gt;shares a model decision&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;failover&lt;/strong&gt; to a second model or provider.&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;balance load or cost&lt;/strong&gt; across several models.&lt;/li&gt;
&lt;li&gt;You want different models for &lt;strong&gt;different jobs&lt;/strong&gt; behind a clear name.&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;A/B test&lt;/strong&gt; a model without touching the app.&lt;/li&gt;
&lt;li&gt;You need to &lt;strong&gt;limit access&lt;/strong&gt; to a model for certain users or teams.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;guardrails&lt;/strong&gt; to apply to everything that calls a model name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When should you skip it? If one script calls one model and you have no routing, failover, or access needs, call the provider model ID directly. ZRouter accepts both, so you can add a virtual model later without changing anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route by job: name models after what they do
&lt;/h2&gt;

&lt;p&gt;The most useful habit with virtual models is naming them after the &lt;strong&gt;job&lt;/strong&gt;, not the model. Here is a typical agent setup:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Virtual model&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Good fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;planner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Break a task into steps, reason about trade-offs&lt;/td&gt;
&lt;td&gt;Strongest reasoning model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;coder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write and edit code&lt;/td&gt;
&lt;td&gt;Best coding model, with failover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Classify, extract, route, short answers&lt;/td&gt;
&lt;td&gt;Small, cheap, low-latency model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;summarizer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Condense logs, documents, tool output&lt;/td&gt;
&lt;td&gt;Cheap model with long context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reviewer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check output, judge quality&lt;/td&gt;
&lt;td&gt;A different model family from &lt;code&gt;coder&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General chat where difficulty varies&lt;/td&gt;
&lt;td&gt;Difficulty-based routing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The agent code calls &lt;code&gt;planner&lt;/code&gt; for planning and &lt;code&gt;fast&lt;/code&gt; for classification. When a better coding model ships, you repoint &lt;code&gt;coder&lt;/code&gt; and every agent improves at once. When &lt;code&gt;summarizer&lt;/code&gt; is costing too much, you swap it for a cheaper model and watch quality, all without a deploy.&lt;/p&gt;

&lt;p&gt;Each of these names can use a different routing strategy. That is where the real power is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing strategies, and what each is best for
&lt;/h2&gt;

&lt;p&gt;A ZRouter redirect can have several targets and a &lt;strong&gt;strategy&lt;/strong&gt; that decides which target serves each request. Every strategy shares three behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unavailable targets are skipped.&lt;/strong&gt; An unknown model or a provider that is down does not break the name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The other targets form a failover chain.&lt;/strong&gt; If the chosen target fails with a retryable error, ZRouter tries the remaining available targets in order. You can turn this off per virtual model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session keeping is on by default.&lt;/strong&gt; Requests from one detected client session (for example one Claude Code or Codex session) stay on the same target, so provider prompt caches stay warm and behavior stays consistent mid-conversation. The failover strategy ignores this setting because it always uses the first available target anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Single target (alias)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; One name, one model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Stable names for apps, hiding provider IDs, and making future upgrades a one-line change. Start here for every job, and add targets when you need them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;virtual_models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;summarizer&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-4o-mini&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Round-robin (default)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Rotates requests across targets. Weights bias the share: a target with weight &lt;code&gt;2&lt;/code&gt; gets twice the traffic of a target with weight &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spreading load across providers to stay under rate limits.&lt;/li&gt;
&lt;li&gt;Gradual migrations and canaries: send 10% of traffic to a new model with weights &lt;code&gt;9&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, then shift.&lt;/li&gt;
&lt;li&gt;Comparing models on real traffic.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;coder&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;round_robin&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;anthropic/claude-sonnet-4-6&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;9&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openai/gpt-5&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Lowest cost
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Sends every request to the cheapest currently available target, using catalog input plus output prices. Targets without pricing are skipped while a priced target exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; High-volume jobs where several models are good enough and price is the deciding factor: classification, extraction, tagging, routine summaries. If the cheapest provider goes down, the next cheapest takes over automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fast&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cost&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openai/gpt-4o-mini&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;groq/llama-3.3-70b&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;anthropic/claude-haiku-4-5&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Failover
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Always uses the first available target in the list. Lower entries only serve when everything above them is unavailable or fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Production features where you have a clear preferred model and want backups only for emergencies. Customer-facing chat, critical agent steps, and anything where consistency matters more than spreading load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;planner&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;failover&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;anthropic/claude-opus-4-6&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openai/gpt-5&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;anthropic/claude-sonnet-4-6&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Schedule (time of day)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Uses one set of targets during a daily peak window and another set off-peak, in a timezone you choose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paying for speed when users are waiting (business hours) and saving money when they are not.&lt;/li&gt;
&lt;li&gt;Overnight batch jobs, evals, and background agents that can run on a cheaper model.&lt;/li&gt;
&lt;li&gt;Moving traffic away from a provider that is congested at certain hours.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;assistant&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;schedule&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-4o&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;groq/llama-3.3-70b&lt;/span&gt;
    &lt;span class="na"&gt;strategy_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;America/New_York&lt;/span&gt;
      &lt;span class="na"&gt;peak_start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;08:00"&lt;/span&gt;
      &lt;span class="na"&gt;peak_end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;18:00"&lt;/span&gt;
      &lt;span class="na"&gt;peak_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;openai/gpt-4o&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;groq/llama-3.3-70b&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;off_peak_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;groq/llama-3.3-70b&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the timezone to where your users are, not where the server runs. Windows that wrap midnight work too.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Super Intelligence Router (request difficulty)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Scores every request from 0 to 100 using signals in the request itself: prompt length, code, reasoning words, math, tools, images, conversation length, and requested reasoning effort. The score picks a tier: &lt;strong&gt;simple&lt;/strong&gt; (below 25), &lt;strong&gt;standard&lt;/strong&gt; (25 to 54), or &lt;strong&gt;complex&lt;/strong&gt; (55 and above). The request goes to the first available target in that tier. Scoring adds well under a millisecond.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; General-purpose names like &lt;code&gt;auto&lt;/code&gt; where users and agents send a mix of easy and hard work. Greetings and rewrites go to a cheap model, debugging sessions with tools go to your strongest model. A hard request is never downgraded while a strong model is up: if its tier is empty or down, ZRouter tries stronger tiers first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;super_intelligence&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;groq/llama-3.3-70b&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-4o-mini&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-sonnet-4-5&lt;/span&gt;
    &lt;span class="na"&gt;strategy_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;simple_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;groq/llama-3.3-70b&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;standard_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;openai/gpt-4o-mini&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;complex_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;anthropic/claude-sonnet-4-5&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every response explains the decision, which makes tuning easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;X-ZRouter-Route: super_intelligence; tier=complex; score=63; reasons=code,tools,reasoning-cues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Adaptive (ZRouter Pro)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Picks the target with the best observed mix of latency, error rate, and price, backs off from targets that are rate limited or failing, and keeps a session on the provider whose prompt cache is warm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; High-traffic production names where provider performance changes during the day and you want the gateway to react on its own. On an open source build this strategy falls back to round-robin.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Plugin strategies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Hands the choice to a routing plugin. ZRouter ships one, &lt;code&gt;cheapest_healthy&lt;/code&gt;, which prefers the cheapest (or fastest) target whose recent error rate is acceptable. You can also write your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Cost-sensitive jobs that also need to avoid flaky providers, and teams with custom routing rules (data residency, customer tiers, internal model servers). If the plugin is missing, errors, or takes longer than 250 ms, the redirect falls back to weighted round-robin, so a broken plugin does not take the name down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;smart-router&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plugin&lt;/span&gt;
    &lt;span class="na"&gt;strategy_plugin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cheapest_healthy&lt;/span&gt;
    &lt;span class="na"&gt;strategy_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;prefer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cheapest&lt;/span&gt;     &lt;span class="c1"&gt;# or: fastest&lt;/span&gt;
      &lt;span class="na"&gt;max_error_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;openai/gpt-4o-mini&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;anthropic/claude-haiku-4-5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Choosing a strategy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If the job needs...&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A stable name and easy upgrades&lt;/td&gt;
&lt;td&gt;Single target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load spread, canaries, gradual migration&lt;/td&gt;
&lt;td&gt;Round-robin with weights&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The lowest price among good-enough models&lt;/td&gt;
&lt;td&gt;Lowest cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One preferred model with emergency backups&lt;/td&gt;
&lt;td&gt;Failover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed by day, savings by night&lt;/td&gt;
&lt;td&gt;Schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One name for mixed easy and hard work&lt;/td&gt;
&lt;td&gt;Super Intelligence Router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic tuning on live performance&lt;/td&gt;
&lt;td&gt;Adaptive (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cheapest model that is also healthy, or custom rules&lt;/td&gt;
&lt;td&gt;Plugin (&lt;code&gt;cheapest_healthy&lt;/code&gt; or your own)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Combine strategies by chaining virtual models
&lt;/h2&gt;

&lt;p&gt;A target can be another virtual model. This lets you build routing in layers instead of one complicated rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;virtual_models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# A cheap pool, balanced across two providers.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cheap&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openai/gpt-4o-mini&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;groq/llama-3.3-70b&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# A premium model with a backup.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;premium&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;failover&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;anthropic/claude-sonnet-4-6&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;openai/gpt-5&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Difficulty routing on top of both pools.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;super_intelligence&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;cheap&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;premium&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;strategy_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;simple_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;cheap&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;complex_targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;premium&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;auto&lt;/code&gt; sends easy work to a balanced cheap pool and hard work to a premium model with failover. Change &lt;code&gt;cheap&lt;/code&gt; once, and every name that uses it follows. Chains are checked for cycles and can be up to 8 levels deep. Session keeping applies at every level, so a session stays on one concrete model.&lt;/p&gt;

&lt;h2&gt;
  
  
  More things a virtual model can carry
&lt;/h2&gt;

&lt;p&gt;Routing is the main use, but the same name can hold other policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shadowing.&lt;/strong&gt; Give a virtual model the same name as a real model, such as &lt;code&gt;anthropic/claude-opus-4-6&lt;/code&gt;, and point it somewhere else. Apps that hardcode that ID get the new model without a code change. This is useful for emergency swaps and for testing whether a cheaper model handles an existing flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access control.&lt;/strong&gt; Limit a name to certain user paths (teams or customer accounts), or disable it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guardrails.&lt;/strong&gt; Attach up to 16 guardrails that run on every request to that name, such as PII redaction or blocked terms. Every app and agent that uses the name is covered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promotions.&lt;/strong&gt; Publish a model for a limited time with per-user and total request and token caps, for launches and free trials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slowdown.&lt;/strong&gt; Add artificial latency to test how your app behaves with a slower model before you switch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model list control.&lt;/strong&gt; Set &lt;code&gt;KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=true&lt;/code&gt; so &lt;code&gt;/v1/models&lt;/code&gt; lists only your curated names instead of every provider model.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A practical rollout plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Name your jobs.&lt;/strong&gt; List the places your apps and agents call a model and give each job a name: &lt;code&gt;assistant&lt;/code&gt;, &lt;code&gt;coder&lt;/code&gt;, &lt;code&gt;fast&lt;/code&gt;, &lt;code&gt;summarizer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with single targets.&lt;/strong&gt; Create each name pointing at the model you use today. Point your apps at the gateway URL and switch the &lt;code&gt;model&lt;/code&gt; field to the new names. Behavior is unchanged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add failover to critical names.&lt;/strong&gt; Add a second provider to anything customer-facing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize cost where quality allows.&lt;/strong&gt; Move high-volume jobs to lowest cost, or put a general &lt;code&gt;auto&lt;/code&gt; name on the Super Intelligence Router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experiment safely.&lt;/strong&gt; Use weighted round-robin to try new models on a small share of traffic, and compare usage, latency, and quality in the dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the names, change the models.&lt;/strong&gt; From now on, model upgrades are edits in the gateway.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A virtual model is a stable name that the gateway resolves to a real model at request time.&lt;/li&gt;
&lt;li&gt;Your app keeps one endpoint, one key, and one model name. Switching models never means updating environment variables or reconfiguring endpoints.&lt;/li&gt;
&lt;li&gt;Name virtual models after jobs, then give each job the routing strategy that fits it.&lt;/li&gt;
&lt;li&gt;Round-robin spreads load, lowest cost saves money, failover protects critical paths, schedule follows the clock, the Super Intelligence Router follows request difficulty, adaptive follows live performance, and plugins cover everything else.&lt;/li&gt;
&lt;li&gt;Chain virtual models to combine strategies, and attach guardrails and access rules so every caller gets the same policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt; is open source under the MIT license and speaks the OpenAI API. Point any existing client at &lt;code&gt;https://zrouter.si/v1&lt;/code&gt;, create your first virtual model, and your next model upgrade will be a configuration change instead of a release.&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>Where to Put Your AI Cache: Client, Gateway, or Provider</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Mon, 26 Oct 2026 18:17:27 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/where-to-put-your-ai-cache-client-gateway-or-provider-4380</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/where-to-put-your-ai-cache-client-gateway-or-provider-4380</guid>
      <description>&lt;h2&gt;
  
  
  Where to Put Your AI Cache: Client, Gateway, or Provider
&lt;/h2&gt;

&lt;p&gt;There are three places: inside your application, at an AI gateway between your apps and the providers, and at the provider itself. Each has a role. This post compares them and then walks through a complete setup using &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt;, an open source AI gateway that runs every caching layer in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Cache in the application
&lt;/h2&gt;

&lt;p&gt;You add a cache library or a Redis call around your LLM client code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single app with a narrow use case, such as caching one expensive summary per document.&lt;/li&gt;
&lt;li&gt;Business-level caching, where you know something the model call does not. For example, "this product description is unchanged, so reuse yesterday's summary."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weak at:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Duplication.&lt;/strong&gt; Every service, language, and team writes its own cache with its own bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correctness details.&lt;/strong&gt; Key design, streaming replay, request coalescing, tenant isolation, and invalidation on policy changes are easy to get subtly wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider prompt caching.&lt;/strong&gt; Each provider has its own directives. Your app code ends up full of provider-specific fields that break when you switch models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibility.&lt;/strong&gt; Hit rates are scattered across services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 2: Cache at the provider
&lt;/h2&gt;

&lt;p&gt;Provider prompt caching runs on the provider's servers. You mostly need to structure prompts well and, for some providers, send a directive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long, stable prefixes. It is the only layer that can reuse the model's own processing.&lt;/li&gt;
&lt;li&gt;Keeping every answer fresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weak at:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It never skips the call. You still pay for output and for the new part of the input.&lt;/li&gt;
&lt;li&gt;Every provider works differently: automatic caching, cache keys, breakpoints, or explicit cached-content objects with their own lifetimes.&lt;/li&gt;
&lt;li&gt;It depends on routing. Spreading one conversation across providers, models, or API keys can leave the cache cold.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 3: Cache at the gateway
&lt;/h2&gt;

&lt;p&gt;An AI gateway is a proxy that sits between your applications and model providers. All LLM traffic passes through it, which makes it the natural home for shared concerns: routing, failover, budgets, guardrails, logging, and caching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One cache for every app.&lt;/strong&gt; Any client that speaks the OpenAI API benefits without code changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correct keys.&lt;/strong&gt; The gateway knows the authenticated tenant, the resolved provider and model, and which policies ran, so it can key on all of them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider prompt caching without provider code.&lt;/strong&gt; The gateway knows the final provider after routing and can add the right directive for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing that respects caches.&lt;/strong&gt; The gateway can keep a session on the same target and key so provider caches stay warm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One place to measure.&lt;/strong&gt; Hit rates and cost avoided appear in one dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weak at:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business-level knowledge that only your app has. Keep that in the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The recommended layout
&lt;/h3&gt;

&lt;p&gt;For most teams the answer is a combination:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gateway:&lt;/strong&gt; exact cache, semantic cache where it fits, prompt-cache planning, and session stickiness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider:&lt;/strong&gt; prompt caching, driven by the gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application:&lt;/strong&gt; only domain-specific caching, plus stable prompt structure (static content first).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting it up in ZRouter
&lt;/h2&gt;

&lt;p&gt;The examples below show configuration only. Keep real API keys and connection strings in your secret manager or environment, never in committed files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Provider prompt caching (already on)
&lt;/h3&gt;

&lt;p&gt;ZRouter's provider prompt-cache planner is enabled by default. To make that explicit in your deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROVIDER_PROMPT_CACHE_PLANNER_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Gemini, you can tune the lifetime of the cached-content objects ZRouter creates. The default is 300 seconds, and values from 30 to 3600 are accepted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GEMINI_PROMPT_CACHE_TTL_SECONDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your client already sends its own cache directives (for example Anthropic &lt;code&gt;cache_control&lt;/code&gt; blocks), ZRouter passes them through unchanged. To turn off automatic planning but keep pass-through, set the flag to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Exact response cache
&lt;/h3&gt;

&lt;p&gt;Point ZRouter at Redis in &lt;code&gt;config.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;simple&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://localhost:6379&lt;/span&gt;
        &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with environment variables only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RESPONSE_CACHE_SIMPLE_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;REDIS_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;redis://localhost:6379
&lt;span class="nv"&gt;REDIS_TTL_RESPONSES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Semantic cache (optional)
&lt;/h3&gt;

&lt;p&gt;Add an embedder and a vector store. Qdrant is shown here. pgvector, Pinecone, and Weaviate are also supported.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;semantic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;embedder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text-embedding-3-small&lt;/span&gt;
      &lt;span class="na"&gt;vector_store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;qdrant&lt;/span&gt;
        &lt;span class="na"&gt;qdrant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:6333&lt;/span&gt;
          &lt;span class="na"&gt;collection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aigateway_semantic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful tuning variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SEMANTIC_CACHE_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.92            &lt;span class="c"&gt;# similarity required for a hit&lt;/span&gt;
&lt;span class="nv"&gt;SEMANTIC_CACHE_TTL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3600                  &lt;span class="c"&gt;# seconds an entry lives&lt;/span&gt;
&lt;span class="nv"&gt;SEMANTIC_CACHE_MAX_CONV_MESSAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3       &lt;span class="c"&gt;# recent conversation included in the key&lt;/span&gt;
&lt;span class="nv"&gt;SEMANTIC_CACHE_EXCLUDE_SYSTEM_PROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use pgvector or Pinecone, make sure the configured dimension matches your embedding model's output size (1536 for &lt;code&gt;text-embedding-3-small&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When both layers are enabled, a semantic hit is also remembered in Redis for that exact request. The next identical request skips the embedding and the vector search and is still reported as &lt;code&gt;HIT (semantic)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Send traffic and check the headers
&lt;/h3&gt;

&lt;p&gt;Any OpenAI-compatible client works. Send the same request twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://your-gateway/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$ZROUTER_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"What are your API rate limits?"}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second response includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Cache: HIT (exact)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A paraphrased question, with the semantic layer enabled, can return &lt;code&gt;X-Cache: HIT (semantic)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Bypass when you need fresh output
&lt;/h3&gt;

&lt;p&gt;For "regenerate" buttons, live data, or debugging, skip both response cache layers for one request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cache-Control: no-cache"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;no-store&lt;/code&gt; when the response should not be written to the cache either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Keep sessions sticky for agents
&lt;/h3&gt;

&lt;p&gt;If you run coding agents through ZRouter, session keeping works with zero configuration. Known session headers such as &lt;code&gt;X-Session-Id&lt;/code&gt;, &lt;code&gt;X-Claude-Code-Session-Id&lt;/code&gt;, and Codex's &lt;code&gt;Session-Id&lt;/code&gt; keep a session on the same load-balanced target and the same provider key, so provider prompt caches stay warm for the whole task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Measure
&lt;/h3&gt;

&lt;p&gt;Cache hits are recorded as usage entries with &lt;code&gt;cache_type&lt;/code&gt; set to &lt;code&gt;exact&lt;/code&gt; or &lt;code&gt;semantic&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Hits only, for one team&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://your-gateway/admin/usage/summary?user_path=/team/alpha&amp;amp;cache_mode=cached"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AIGATEWAY_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;/admin/cache/overview&lt;/code&gt; for a cache-only summary. Default usage reports exclude hits, so cost totals keep matching your provider invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ZRouter handles so you do not have to
&lt;/h2&gt;

&lt;p&gt;A quick checklist of the details covered in Part 3, and how the gateway deals with them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;How ZRouter handles it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tenant isolation&lt;/td&gt;
&lt;td&gt;Keys include the auth scope and account-bound user path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter changes&lt;/td&gt;
&lt;td&gt;Exact keys include the final body and resolved model; semantic keys include all request options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy changes&lt;/td&gt;
&lt;td&gt;The guardrail chain is part of the exact cache key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formatting differences&lt;/td&gt;
&lt;td&gt;JSON is canonicalized before hashing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic spikes&lt;/td&gt;
&lt;td&gt;Concurrent identical misses are coalesced per process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;Raw event streams are stored and replayed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failover answers&lt;/td&gt;
&lt;td&gt;Not cached under the primary's key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BYOK traffic&lt;/td&gt;
&lt;td&gt;Bypasses the shared response cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider differences&lt;/td&gt;
&lt;td&gt;One planner adds the right prompt-cache directive per provider&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Wrapping up the series
&lt;/h2&gt;

&lt;p&gt;AI caching is one of the few optimizations that cuts cost and latency at the same time. The key ideas from all four parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Know the layers.&lt;/strong&gt; Exact and semantic caches reuse answers. Prompt caching reuses the model's reading of your prompt. Embedding caching reuses vectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure the benefit.&lt;/strong&gt; Track hit rate and cost avoided per layer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match the layer to the workload.&lt;/strong&gt; Response caches for repetitive, stable traffic. Prompt caching nearly everywhere, especially for agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put shared caching at the gateway.&lt;/strong&gt; It sees every request, knows the tenant and the final model, and keeps provider caches warm.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to try it, &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt; is open source under the MIT license and speaks the OpenAI API, so you can point an existing client at it and turn on each layer one step at a time.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>When to Use AI Caching, and When Not To</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Sat, 24 Oct 2026 13:19:30 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/when-to-use-ai-caching-and-when-not-to-13e8</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/when-to-use-ai-caching-and-when-not-to-13e8</guid>
      <description>&lt;h2&gt;
  
  
  When to Use AI Caching, and When Not To
&lt;/h2&gt;

&lt;p&gt;We start with a simple test, go workload by workload, then cover the pitfalls. Examples refer to &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt; settings where it helps to be concrete.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-question test
&lt;/h2&gt;

&lt;p&gt;Before enabling any response cache (exact or semantic), ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Do the same requests repeat?&lt;/strong&gt; If most prompts are unique, a response cache will mostly miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is a reused answer still correct?&lt;/strong&gt; If the right answer depends on time, live data, or the specific user, reuse is risky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is a reused answer acceptable?&lt;/strong&gt; Some products need variety (creative writing, brainstorming). Others need consistency (policy answers).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the answers are yes, yes, and yes, use a response cache. If any is no, skip response caching for that traffic and lean on provider prompt caching instead, which never reuses answers and is safe almost everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload by workload
&lt;/h2&gt;

&lt;h3&gt;
  
  
  FAQ bots and support assistants
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt; exact cache, semantic cache, prompt caching.&lt;/p&gt;

&lt;p&gt;This is the best case. Questions repeat constantly, answers are stable, and consistency is a feature. Start the semantic threshold high and lower it only when you have evidence. ZRouter's default threshold is &lt;code&gt;0.92&lt;/code&gt;, which is a sensible starting point. Set a TTL that matches how often your underlying content changes. If your help center updates weekly, a one-hour TTL (ZRouter's semantic default, &lt;code&gt;SEMANTIC_CACHE_TTL=3600&lt;/code&gt;) is safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval-augmented generation (RAG)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt; exact cache, prompt caching, embedding caching. Semantic cache with care.&lt;/p&gt;

&lt;p&gt;In RAG, the answer depends on the retrieved documents as much as on the question. An exact cache is safe because the retrieved context is part of the request body, so a document change produces a new key. A semantic cache that matches only on the question could return an answer built from old documents. ZRouter reduces this risk because only the final user message is matched by meaning, while the rest of the request, including injected context in other messages and a window of recent conversation (&lt;code&gt;SEMANTIC_CACHE_MAX_CONV_MESSAGES&lt;/code&gt;, default 3), still has to match. Even so, keep TTLs short when your corpus changes often.&lt;/p&gt;

&lt;p&gt;Embedding caching pays off when you re-index. Identical chunks hit the exact cache on &lt;code&gt;/v1/embeddings&lt;/code&gt; instead of being billed again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coding agents and multi-step agents
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt; prompt caching. Avoid response caching.&lt;/p&gt;

&lt;p&gt;Agents send long, stable prefixes (instructions, tool schemas, file contents) followed by new, specific steps. Each response must reflect the latest state of the task, so a reused answer is almost always wrong. But the prefix is the perfect target for prompt caching, and on long sessions it is where most of the money goes.&lt;/p&gt;

&lt;p&gt;Two things decide whether prompt caching actually hits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prefix stability.&lt;/strong&gt; Put static content first and changing content last. A timestamp at the top of a system prompt breaks the cache on every request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing stability.&lt;/strong&gt; The provider cache lives on a specific provider, model, and often API key. If your router load-balances turn 5 to a different target than turn 4, the cache is cold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ZRouter handles the second point with session keeping: requests in a detected session (for example from Claude Code, Codex CLI, or OpenCode session headers) stay on the same target and the same provider key.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chat apps with personal context
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt; prompt caching. Response caching only for non-personal requests.&lt;/p&gt;

&lt;p&gt;If answers mention the user's name, account, or history, a shared response cache is a data leak waiting to happen. Make sure any response cache is scoped per user or per account. ZRouter includes the authenticated access scope and the server-derived user path in every exact and semantic cache key. Customer API keys are bound to &lt;code&gt;/accounts/ACCOUNT_ID&lt;/code&gt;, and client headers cannot override that binding, so two accounts never share cached answers, even for identical prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batch and offline jobs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use:&lt;/strong&gt; exact cache.&lt;/p&gt;

&lt;p&gt;Evaluation runs, classification over a dataset, and nightly summaries often re-run the same inputs after a crash or a code change. An exact cache makes reruns cheap. Since timing does not matter, consider a long TTL for the duration of the job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creative and generative features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; response caching.&lt;/p&gt;

&lt;p&gt;If users click "regenerate" to get a different poem, serving the same poem defeats the feature. Bypass the cache for those requests. In ZRouter, send &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; or &lt;code&gt;Cache-Control: no-store&lt;/code&gt; on any request that should skip both response cache layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-time or time-sensitive answers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Avoid:&lt;/strong&gt; response caching, or use a very short TTL.&lt;/p&gt;

&lt;p&gt;"What is the status of my order?", "What is the BTC price?", and "Is the API down?" need live data. Fresh tool calls and retrieval are the right answer here, and prompt caching can still reduce the cost of the static instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Exact&lt;/th&gt;
&lt;th&gt;Semantic&lt;/th&gt;
&lt;th&gt;Prompt cache&lt;/th&gt;
&lt;th&gt;Embedding cache&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FAQ / support&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes, high threshold&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Careful, short TTL&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding agents&lt;/td&gt;
&lt;td&gt;Rarely useful&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, most value&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal chat&lt;/td&gt;
&lt;td&gt;Only if scoped&lt;/td&gt;
&lt;td&gt;Only if scoped&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch jobs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Rarely&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creative&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time data&lt;/td&gt;
&lt;td&gt;No, or short TTL&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pitfalls that cause wrong answers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Semantic thresholds that are too loose
&lt;/h3&gt;

&lt;p&gt;At a low threshold, "cancel my subscription" and "renew my subscription" can look similar. The cost of one wrong hit is usually higher than the savings from many right ones. Start strict, monitor wrong-hit reports, and loosen slowly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring parameters
&lt;/h3&gt;

&lt;p&gt;Two requests with the same prompt but different models, temperatures, tools, or response formats are different requests. A cache that keys only on the prompt text will serve a JSON-mode answer to a plain-text request. ZRouter's semantic keys include all top-level request options and provider extensions, and its exact keys include the resolved provider and model, so a change in any of them is a miss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching across policy changes
&lt;/h3&gt;

&lt;p&gt;If you add a guardrail that redacts personal data, cached responses from before the change were never checked by it. ZRouter includes the resolved guardrail chain in the exact cache key. Editing, adding, or removing a guardrail step produces a miss rather than replaying a response the new policy never saw.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching failures and fallbacks
&lt;/h3&gt;

&lt;p&gt;Never cache errors. Be careful about caching answers from a fallback model under the primary model's key. ZRouter does not cache failed responses, and it does not cache responses served through failover.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leaking data between tenants
&lt;/h3&gt;

&lt;p&gt;The most serious pitfall. Any shared response cache must key on the tenant. If you run multi-tenant infrastructure, verify this before enabling caching, not after. Also check how bring-your-own-key traffic is handled. In ZRouter, BYOK requests bypass the shared response cache entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stale answers after content changes
&lt;/h3&gt;

&lt;p&gt;Pick a TTL that matches how often the truth changes. When you ship a big content update, expect old entries to live until they expire, or clear the cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unstable prompt prefixes
&lt;/h3&gt;

&lt;p&gt;For prompt caching, small changes at the start of a prompt (a date, a random ID, a reordered tool list) silently disable the cache. If your prompt-cache hit rate is near zero, look at the first few hundred tokens of consecutive requests and diff them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use response caching for repetitive, stable, non-personal traffic.&lt;/li&gt;
&lt;li&gt;Use prompt caching almost everywhere, and especially for agents and long conversations.&lt;/li&gt;
&lt;li&gt;Scope every response cache to the tenant and include every parameter that changes the answer in the key.&lt;/li&gt;
&lt;li&gt;Give users and developers a way to bypass the cache per request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; [Part 4: Where to Put Your AI Cache]&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>performance</category>
    </item>
    <item>
      <title>The Real Benefits of AI Caching: Cost, Speed, Limits, and Reliability</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Fri, 23 Oct 2026 10:11:19 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/the-real-benefits-of-ai-caching-cost-speed-limits-and-reliability-2gef</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/the-real-benefits-of-ai-caching-cost-speed-limits-and-reliability-2gef</guid>
      <description>&lt;h2&gt;
  
  
  The Real Benefits of AI Caching: Cost, Speed, Limits, and Reliability
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="//./01-what-is-ai-caching.md"&gt;Part 1&lt;/a&gt; we described the four layers of AI caching: exact response caching, semantic caching, provider prompt caching, and embedding caching. This post answers the obvious follow-up: what do you actually get for turning them on?&lt;/p&gt;

&lt;p&gt;The short answer is five things: lower cost, lower latency, more headroom under rate limits, better resilience, and more consistent answers. Below we go through each one, with worked examples you can adapt to your own traffic. We use &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt; to show how each benefit shows up in practice.&lt;/p&gt;

&lt;p&gt;A note on the numbers: provider prices change often, so the examples use round, illustrative figures. Plug in your own provider's current rates to get real estimates.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Lower cost
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Response caching removes calls entirely
&lt;/h3&gt;

&lt;p&gt;A response cache hit costs nothing at the model provider. Your savings are simply your hit rate multiplied by your spend on those requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worked example.&lt;/strong&gt; A customer support assistant handles 100,000 requests a month. Each request averages 1,500 input tokens and 300 output tokens. Assume a model priced at $2.50 per million input tokens and $10 per million output tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: 100,000 × 1,500 = 150M tokens → $375&lt;/li&gt;
&lt;li&gt;Output: 100,000 × 300 = 30M tokens → $300&lt;/li&gt;
&lt;li&gt;Total: &lt;strong&gt;$675 per month&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Support traffic is highly repetitive. If the exact cache catches 15% of requests and the semantic cache catches another 20%, then 35% of calls never reach the provider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model spend: 65% × $675 = &lt;strong&gt;$439&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Semantic lookups: about 85,000 embedding calls (every request that missed the exact cache) at roughly 20 tokens of final user text each. That is around 1.7M embedding tokens, which costs cents at typical embedding prices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is about &lt;strong&gt;$236 saved per month&lt;/strong&gt; on one assistant, before counting the latency improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt caching shrinks the repeated part
&lt;/h3&gt;

&lt;p&gt;For workloads where every answer must be fresh, like agents, coding tools, and long chats, response caching rarely hits. Prompt caching still helps, because most of each request is the same prefix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worked example.&lt;/strong&gt; A coding agent session runs 40 turns. Each turn sends a 25,000-token stable prefix (system prompt, tool definitions, project files) plus a growing conversation. Ignore the conversation growth and focus on the prefix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without prompt caching: 40 × 25,000 = 1M input tokens at full price.&lt;/li&gt;
&lt;li&gt;With prompt caching: the first turn pays full price (some providers add a small write premium). Turns 2 to 40 read 975,000 prefix tokens at the provider's cached rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Providers discount cached input heavily. Depending on provider and model, cached input tokens are billed at roughly 10% to 50% of the normal input price. At a 90% discount, the prefix cost falls by close to 85% across the session. At a 50% discount, it still falls by close to half.&lt;/p&gt;

&lt;p&gt;The catch is that you only get this if the prefix is actually identical every turn and the request reaches the same provider, the same model, and often the same API key. That is harder than it sounds, and it is one of the main reasons to handle it at a gateway (see Part 4). ZRouter's prompt-cache planner adds the right directive for each provider automatically, and its session keeping feature pins every request in a detected session to the same target and the same provider key so the cache stays warm.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Lower latency
&lt;/h2&gt;

&lt;p&gt;A model call has two slow parts: reading the prompt (time to first token) and writing the answer (generation time).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response cache hits&lt;/strong&gt; skip both. A Redis lookup takes a few milliseconds. A typical chat completion takes one to ten seconds. For the user, a cached answer feels instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic cache hits&lt;/strong&gt; add an embedding call and a vector search before the answer, usually tens of milliseconds, which is still far faster than generating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt cache hits&lt;/strong&gt; cut time to first token, because the provider does not reprocess the long prefix. On very long prompts this can be the difference between a two-second pause and a near-immediate start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Latency matters beyond user patience. Agents chain many calls together. Saving a second on each of 40 steps saves 40 seconds on one task.&lt;/p&gt;

&lt;p&gt;In ZRouter, streaming responses are cached as the raw event stream, so a cached streaming answer still arrives as a stream. Your chat UI renders it the same way, just much faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. More headroom under rate limits
&lt;/h2&gt;

&lt;p&gt;Providers limit requests per minute and tokens per minute. When you hit a limit, requests fail or queue.&lt;/p&gt;

&lt;p&gt;Every response cache hit is a request that does not count against your provider limits. Every prompt cache hit can reduce the input tokens that count against token-per-minute limits on providers that discount cached tokens in their limit accounting.&lt;/p&gt;

&lt;p&gt;A subtler benefit comes from &lt;strong&gt;request coalescing&lt;/strong&gt;. Picture a product launch, where hundreds of users ask the same question in the same minute. A plain cache misses on all of them until the first answer returns. ZRouter coalesces concurrent identical misses in each gateway process: one request goes to the provider, and the rest wait and replay its answer. A spike of identical traffic becomes one provider call instead of hundreds.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Better resilience
&lt;/h2&gt;

&lt;p&gt;A cache is also a buffer against provider trouble. During a brief outage or a period of heavy throttling, popular questions can still be answered from cache because the lookup happens before the request is sent anywhere.&lt;/p&gt;

&lt;p&gt;ZRouter is careful about one edge case here. If a request is answered by a failover target rather than the primary target, the response is not cached. Otherwise the backup model's answer would be stored under the primary model's key and served long after the primary recovered. Entries stored before the outage keep serving until they expire.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. More consistent answers
&lt;/h2&gt;

&lt;p&gt;Language models do not always give the same answer to the same question. For some products, that variation is a problem: a pricing FAQ, a policy explanation, or a compliance answer should not change from one user to the next.&lt;/p&gt;

&lt;p&gt;A response cache makes repeated questions return the same reviewed answer until the entry expires. That makes support quality easier to audit and reduces "the bot told me something different yesterday" tickets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What caching does not buy you
&lt;/h2&gt;

&lt;p&gt;To keep expectations honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It does not make novel requests cheaper.&lt;/strong&gt; A brand new question with a brand new context gets full price, except for any cached prefix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It does not improve answer quality.&lt;/strong&gt; A cache replays answers. A bad answer cached is a bad answer served many times, which is why expiry (TTL) matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic caching is not free.&lt;/strong&gt; Each miss still pays for an embedding and a vector search. If your traffic is almost never repetitive, those lookups are pure overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Measuring the benefit
&lt;/h2&gt;

&lt;p&gt;Turning a cache on is easy. Knowing whether it pays off takes data. Track at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hit rate&lt;/strong&gt; per layer (exact, semantic, prompt).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost avoided&lt;/strong&gt;: provider spend you would have paid without the hits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt; for hits versus misses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong-hit reports&lt;/strong&gt; for the semantic layer, which tell you if your threshold is too loose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ZRouter records each cache hit as a usage entry tagged &lt;code&gt;cache_type: exact&lt;/code&gt; or &lt;code&gt;cache_type: semantic&lt;/code&gt;, with the caller's user path. By default, usage totals only count provider-bound requests so your cost reports match your provider bill. Add &lt;code&gt;cache_mode=all&lt;/code&gt; to include hits, or &lt;code&gt;cache_mode=cached&lt;/code&gt; to see only hits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Provider spend only (default)&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://your-gateway/admin/usage/summary"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AIGATEWAY_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Only cache hits&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://your-gateway/admin/usage/summary?cache_mode=cached"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$AIGATEWAY_MASTER_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also a cache overview at &lt;code&gt;/admin/cache/overview&lt;/code&gt;, and every cached response carries an &lt;code&gt;X-Cache&lt;/code&gt; header you can log on the client side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Response caching saves the entire call and is the biggest win for repetitive traffic.&lt;/li&gt;
&lt;li&gt;Prompt caching is the biggest win for long, fresh, multi-turn work like agents and coding tools.&lt;/li&gt;
&lt;li&gt;Caching also buys latency, rate-limit headroom, resilience, and consistency.&lt;/li&gt;
&lt;li&gt;Measure hit rates and cost avoided per layer, or you are guessing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; [Part 3: When to Use AI Caching, and When Not To]&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>openai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>What Is AI Caching? A Practical Guide to the Four Layers</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:56:30 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/what-is-ai-caching-a-practical-guide-to-the-four-layers-564l</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/what-is-ai-caching-a-practical-guide-to-the-four-layers-564l</guid>
      <description>&lt;h2&gt;
  
  
  What Is AI Caching? A Practical Guide to the Four Layers
&lt;/h2&gt;

&lt;p&gt;Every call to a large language model costs money and time. You pay for each input token and each output token, and you wait while the model reads your prompt and writes its answer. In a busy application, a large share of those calls are repeats: the same help-center question, the same system prompt, the same 20-page document sent with every turn of a conversation.&lt;/p&gt;

&lt;p&gt;AI caching is the practice of noticing that work and not paying for it twice.&lt;/p&gt;

&lt;p&gt;This is the first post in a four-part series. Here we cover what AI caching is and the different layers it can happen at. Part 2 covers the benefits in numbers, Part 3 covers when caching helps and when it hurts, and Part 4 covers where to put each layer in your stack. Throughout the series we use &lt;a href="https://zrouter.si" rel="noopener noreferrer"&gt;ZRouter&lt;/a&gt;, an open source, OpenAI-compatible AI gateway, as a working reference, because it implements every layer described here in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching, but for models
&lt;/h2&gt;

&lt;p&gt;Classic web caching is simple. A request for &lt;code&gt;/logo.png&lt;/code&gt; always returns the same bytes, so you store them and serve the copy next time.&lt;/p&gt;

&lt;p&gt;AI requests are messier for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Outputs are not deterministic.&lt;/strong&gt; Ask a model the same question twice and you may get two different wordings. Caching an answer means choosing one and reusing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inputs are large and mostly repeated.&lt;/strong&gt; A chat request carries the full conversation history, a long system prompt, tool definitions, and maybe retrieved documents. The new part of each request is often a few dozen tokens at the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same question has many spellings.&lt;/strong&gt; "How do I reset my password?" and "password reset steps?" mean the same thing but share almost no characters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because of this, AI caching is not one technique. It is a set of layers, each catching a different kind of repetition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: Exact response caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Store the complete response to a request. When an identical request arrives, return the stored response without calling the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What counts as identical:&lt;/strong&gt; The request body after normalization, plus anything that changes the meaning of the request: which model it resolved to, which account sent it, and which policies ran on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you save:&lt;/strong&gt; The whole call. No input tokens, no output tokens, and the response comes back in milliseconds instead of seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A documentation assistant receives "What are your API rate limits?" two hundred times a day, sent by a widget that builds the exact same request every time. After the first call, the other 199 are free.&lt;/p&gt;

&lt;p&gt;In ZRouter, the exact cache is backed by Redis and covers &lt;code&gt;/v1/chat/completions&lt;/code&gt;, &lt;code&gt;/v1/responses&lt;/code&gt;, &lt;code&gt;/v1/messages&lt;/code&gt;, and &lt;code&gt;/v1/embeddings&lt;/code&gt;. A hit is marked with a response header so you can see it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Cache: HIT (exact)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details make the ZRouter version more useful than a naive "hash the body" approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Canonicalization.&lt;/strong&gt; JSON key order and whitespace are normalized before hashing, so two clients that format the same request differently still share one entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request coalescing.&lt;/strong&gt; If ten identical requests arrive at the same moment and none is cached yet, only one goes to the provider. The other nine wait and replay its answer. This prevents a burst of traffic from turning into a burst of identical paid calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Streaming is handled too. A streaming miss stores the raw server-sent event bytes, and a streaming hit replays them as &lt;code&gt;text/event-stream&lt;/code&gt;, so your client code does not change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: Semantic caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Store responses along with an embedding (a vector representing meaning) of the prompt. When a new prompt arrives, embed it, search for a stored prompt whose vector is close enough, and if one is found, return its response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What counts as a match:&lt;/strong&gt; Similarity above a threshold, usually expressed as cosine similarity between 0 and 1. A high threshold such as 0.92 means "almost the same question". A lower threshold catches more paraphrases but risks wrong answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you save:&lt;/strong&gt; The model call, at the price of one embedding call and one vector search. Embeddings are typically far cheaper than generation, so the trade is usually good for repeated question patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A support bot gets "how do I reset my password", "I forgot my password, what now?", and "password reset link not working?" The first two are the same need and can share an answer. The third is a different problem, and a well-tuned threshold keeps it separate.&lt;/p&gt;

&lt;p&gt;ZRouter's semantic layer uses an embedding provider you choose plus a vector store: Qdrant, pgvector, Pinecone, or Weaviate. A hit is labelled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;X-Cache: HIT (semantic)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ZRouter is deliberately conservative about what it matches on. Only the final user message is compared by meaning. Everything else (model, temperature, tools, other request options, and a limited window of conversation context) must match exactly as part of the key. That way a semantic hit never crosses from one model or configuration to another. It also never applies semantic matching to &lt;code&gt;/v1/embeddings&lt;/code&gt;, because an embedding has to represent the exact text it was requested for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: Provider prompt caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; The model provider keeps the processed form of a long, repeated prompt prefix in memory. When your next request starts with the same prefix, the provider skips reprocessing it and bills those tokens at a discount.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What counts as a match:&lt;/strong&gt; A shared prefix. The system prompt, tool definitions, and earlier conversation turns must be byte-identical and appear first. Anything after the first difference is processed normally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you save:&lt;/strong&gt; Part of the input cost and part of the time to first token. Output is still generated fresh, because the question at the end is new.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A coding agent sends a 30,000-token system prompt and project context on every turn. With prompt caching, turns two through fifty reuse that prefix. The new tokens at the end are billed normally, and the cached prefix is billed at the provider's reduced cached-input rate.&lt;/p&gt;

&lt;p&gt;This layer is different from the first two. The response is not reused at all. You still get a new, fresh answer for every request. What gets reused is the model's work of reading the prompt.&lt;/p&gt;

&lt;p&gt;Each provider exposes it differently. Some cache automatically, some need a cache key, some need explicit breakpoints, and some need you to create a cached-content object ahead of time. ZRouter handles this with a &lt;strong&gt;provider prompt-cache planner&lt;/strong&gt; that is on by default. After it knows which provider and model will serve a request, it adds the right directive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI:&lt;/strong&gt; a stable &lt;code&gt;prompt_cache_key&lt;/code&gt;, plus explicit breakpoints on models that support them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anthropic:&lt;/strong&gt; automatic ephemeral caching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock (Converse):&lt;/strong&gt; a cache point after the stable prefix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Gemini (AI Studio):&lt;/strong&gt; a cached-content object, created only after the same prefix is seen again within five minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your client already sends its own cache directives, ZRouter leaves them alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 4: Embedding caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; Store the vector for a piece of text so you do not embed the same text twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you save:&lt;/strong&gt; Embedding calls. Each one is cheap, but RAG pipelines and semantic caches make a lot of them.&lt;/p&gt;

&lt;p&gt;ZRouter uses this in two ways. Requests to &lt;code&gt;/v1/embeddings&lt;/code&gt; participate in the exact cache, so re-indexing the same documents does not re-bill you. Inside the semantic cache, identical lookup text is reused in memory for five minutes within the same account and embedder, and concurrent identical lookups share a single embedding call.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fifth layer you do not control: the KV cache
&lt;/h2&gt;

&lt;p&gt;Inside the model server, every transformer keeps a key-value (KV) cache of the tokens it has already processed in the current generation. It is the reason the second word of an answer comes out faster than the first. Provider prompt caching is essentially that KV cache, kept alive between your requests. You do not configure it directly, but it explains why prompt structure matters so much, which we cover in Part 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  The layers side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What is reused&lt;/th&gt;
&lt;th&gt;Match rule&lt;/th&gt;
&lt;th&gt;Saves&lt;/th&gt;
&lt;th&gt;Answer is fresh?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exact response cache&lt;/td&gt;
&lt;td&gt;Full response&lt;/td&gt;
&lt;td&gt;Identical request&lt;/td&gt;
&lt;td&gt;Entire call&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic cache&lt;/td&gt;
&lt;td&gt;Full response&lt;/td&gt;
&lt;td&gt;Similar final prompt, same everything else&lt;/td&gt;
&lt;td&gt;Entire call, minus one embedding&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider prompt cache&lt;/td&gt;
&lt;td&gt;Processed prompt prefix&lt;/td&gt;
&lt;td&gt;Identical leading tokens&lt;/td&gt;
&lt;td&gt;Part of input cost and latency&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embedding cache&lt;/td&gt;
&lt;td&gt;Vectors&lt;/td&gt;
&lt;td&gt;Identical text&lt;/td&gt;
&lt;td&gt;Embedding calls&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The layers stack. A request can miss the exact cache, miss the semantic cache, and still get a prompt-cache discount from the provider. In ZRouter, all of them run in one gateway, in that order, after routing, workflows, and guardrails have produced the final request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI caching is a stack of layers, not a single switch.&lt;/li&gt;
&lt;li&gt;Response caches (exact and semantic) skip the model entirely but reuse an old answer.&lt;/li&gt;
&lt;li&gt;Prompt caching keeps every answer fresh while cutting the cost of the repeated prefix.&lt;/li&gt;
&lt;li&gt;The right mix depends on how repetitive your traffic is and how much freshness matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next in the series:&lt;/strong&gt; &lt;a href="//./02-benefits-of-ai-caching.md"&gt;Part 2: The Real Benefits of AI Caching&lt;/a&gt;, where we put numbers on cost, latency, rate limits, and reliability.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>claude</category>
      <category>gemini</category>
    </item>
    <item>
      <title>AI-Powered Code Editors: Build, Fix, and Deploy Apps with AI Builder and CLI</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Wed, 21 Oct 2026 09:42:17 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/ai-powered-code-editors-build-fix-and-deploy-apps-with-ai-builder-and-cli-24nh</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/ai-powered-code-editors-build-fix-and-deploy-apps-with-ai-builder-and-cli-24nh</guid>
      <description>&lt;h2&gt;
  
  
  AI-Powered Code Editors: Build, Fix, and Deploy Apps with AI Builder and CLI
&lt;/h2&gt;

&lt;p&gt;You ask an AI coding tool to build a feature. The first screen looks promising. Then you edit a component locally, the browser preview shows an older version, and the next AI change no longer matches the files you just tested.&lt;/p&gt;

&lt;p&gt;That is where a good AI development workflow earns its place. You need a clear way to move between prompts, editable files, checks, preview, and deployment.&lt;/p&gt;

&lt;p&gt;This guide shows how to use AI-powered code editors alongside NEXUS AI Builder and its CLI. You will create a small app, work on it in your editor, capture a failed build, review an AI fix, and deploy the reviewed Builder snapshot.&lt;/p&gt;

&lt;p&gt;The commands below are real NEXUS AI CLI commands. The scenario is a worked example, rather than a claim that a customer completed these steps or that this particular app has already been generated and deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are AI-powered code editors?
&lt;/h2&gt;

&lt;p&gt;An AI-powered code editor adds assistance to the place where you read and change source files. Depending on the product, it may offer inline suggestions, code explanations, project search, edits across files, or an agent that runs commands and checks its work.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://cursor.com/docs/agent/overview" rel="noopener noreferrer"&gt;Cursor Agent&lt;/a&gt; can edit code and run terminal commands. &lt;a href="https://code.visualstudio.com/docs/agents/overview" rel="noopener noreferrer"&gt;AI in Visual Studio Code&lt;/a&gt; supports workflows for understanding code, fixing bugs, and building features.&lt;/p&gt;

&lt;p&gt;The useful question is how those capabilities fit your task. Can you inspect the generated code? Run your own tests? Keep the preview synchronized? Identify the files that will be deployed?&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the editor, AI Builder, and CLI fit
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Its role in this workflow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code editor&lt;/td&gt;
&lt;td&gt;Read files, make precise edits, review diffs, and use your preferred coding assistant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEXUS AI Builder&lt;/td&gt;
&lt;td&gt;Generate and revise a project, inspect its browser preview, and save shared checkpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEXUS AI CLI&lt;/td&gt;
&lt;td&gt;Bring Builder files to your machine, sync changes, run the review workflow, and submit a deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can keep your preferred editor. The CLI connects your local work to the Builder project, while the browser gives you a place to check the interface and behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The app we will build
&lt;/h2&gt;

&lt;p&gt;Imagine you are making a support-ticket dashboard prototype. You need to show a teammate a working ticket list, a creation form, search, and status changes.&lt;/p&gt;

&lt;p&gt;For this first version, tickets will be saved in browser &lt;code&gt;localStorage&lt;/code&gt;. That keeps the local setup focused on the editor and Builder workflow. It also gives the prototype a clear limit: records belong to that browser and origin. They are not shared across users or synchronized between preview and deployment URLs.&lt;/p&gt;

&lt;p&gt;The acceptance checks are simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A ticket requires a nonempty title.&lt;/li&gt;
&lt;li&gt;Search finds tickets regardless of capitalization.&lt;/li&gt;
&lt;li&gt;A ticket can move from Open to In Progress to Resolved.&lt;/li&gt;
&lt;li&gt;Saved tickets remain after refreshing the same browser URL.&lt;/li&gt;
&lt;li&gt;Type checks, tests, and the production build pass.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Later, we will cover what changes when the prototype needs a shared database and authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install the CLI and confirm access
&lt;/h2&gt;

&lt;p&gt;You need Node.js 18 or later, a NEXUS AI account, an available AI provider for Builder, and a local editor. The generated app may require a newer Node.js version; inspect its package and setup instructions before running it.&lt;/p&gt;

&lt;p&gt;In your normal terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nexusapp-cli@latest
nexus &lt;span class="nt"&gt;--version&lt;/span&gt;
nexus auth login
nexus auth &lt;span class="nb"&gt;whoami
&lt;/span&gt;nexus builder providers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;nexus auth login&lt;/code&gt; opens browser sign-in. &lt;code&gt;whoami&lt;/code&gt; confirms the account and organization you will work in. &lt;code&gt;builder providers&lt;/code&gt; shows the AI providers available to your account.&lt;/p&gt;

&lt;p&gt;AI requests use the applicable Builder allowance or provider configuration. Choose a provider and model available to your account rather than copying a model name from an old tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create an app with specific requirements
&lt;/h2&gt;

&lt;p&gt;Run this command from the parent directory where you want to work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder new &lt;span class="nt"&gt;--name&lt;/span&gt; support-desk-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"Build a Next.js App Router and TypeScript support ticket dashboard. Include a ticket list, title and description fields, priority, and Open, In Progress, and Resolved statuses. Add case-insensitive search and required-title validation. For this prototype, use seeded example tickets and browser localStorage, with no backend or database. Handle server rendering safely. Add working npm scripts named typecheck, test, and build. Include tests for title validation and case-insensitive search, plus README instructions for setup and local use."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI creates a project and prints its project ID. Copy that actual ID, then set a shell variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_THE_RETURNED_PROJECT_ID'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an ordinary shell variable used in the remaining commands. It is not an AI model ID or a deployment ID.&lt;/p&gt;

&lt;p&gt;The prompt defines a small, inspectable product and asks for checks that match its behavior. Review whether the generated project meets those requirements before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Inspect the files and browser preview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder files &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus builder open &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;files&lt;/code&gt; lists the current Builder files. &lt;code&gt;open&lt;/code&gt; prints the browser Builder URL; open that link yourself.&lt;/p&gt;

&lt;p&gt;Try the ticket form, search, status changes, and a page refresh. A plausible screenshot does not confirm that those interactions work.&lt;/p&gt;

&lt;p&gt;Use the browser Builder for visual iteration. Depending on the preview mode and project, you can inspect the app beside its conversation and use the available visual tools. Keep the application behavior in your acceptance checks, even when a change is mostly visual.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://nexusai.run/docs#builder-overview" rel="noopener noreferrer"&gt;NEXUS AI documentation&lt;/a&gt; describes the browser Builder and its preview workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit code directly inside AI Builder
&lt;/h3&gt;

&lt;p&gt;You can also stay in the browser and open Builder's Code tab. Its built-in editor lets you read and change generated files, create or delete files, search and replace, and save with Cmd+S or Ctrl+S. Saved changes become checkpoints.&lt;/p&gt;

&lt;p&gt;Use this for a precise change while keeping the preview nearby. For example, inspect the ticket form component and adjust its empty-title error message, then save and check the form again. The AI conversation and direct file editing work on the same project.&lt;/p&gt;

&lt;p&gt;The browser editor is useful when you want to avoid a local setup. Pulling the project in the next step gives you access to your own editor, installed development tools, and local commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Pull the project into your code editor
&lt;/h2&gt;

&lt;p&gt;Download the Builder snapshot into a new local folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder pull &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--out&lt;/span&gt; ./support-desk-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;support-desk-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open that folder in Cursor, Visual Studio Code, or your preferred editor. Read the generated &lt;code&gt;README.md&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;List the available package scripts and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example prompt requested &lt;code&gt;typecheck&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, and &lt;code&gt;build&lt;/code&gt;. If any are missing, have Builder add the corresponding working scripts and checks, then sync the updated project. Treat a missing test command as unfinished setup, rather than treating the absence of a failure as a pass.&lt;/p&gt;

&lt;p&gt;The local folder also contains Builder checkout metadata. Keep it so the CLI can identify the project and compare local changes with the shared snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make one focused editor change
&lt;/h3&gt;

&lt;p&gt;For example, ask your editor's AI assistant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review the existing ticket search implementation. Make search ignore
leading and trailing whitespace as well as capitalization. Keep the
current UI and storage behavior. Update the relevant tests and show
which files changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could make the same change manually. Either way, inspect the diff and keep the change focused enough to verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Test locally and send the changes back
&lt;/h2&gt;

&lt;p&gt;From the pulled project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run typecheck
npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are project commands requested in the generation prompt, rather than built-in NEXUS commands. Confirm they exist and inspect what they run. If a check fails, use the captured-failure workflow in Step 7 before publishing the change.&lt;/p&gt;

&lt;p&gt;Once the changes pass your checks, push them back from this folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder push &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--from&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
nexus builder check &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;push&lt;/code&gt; synchronizes local edits with Builder. If it reports a conflict, resolve it before continuing. When files changed on both sides, the CLI can merge nonoverlapping edits and leave conflict markers where manual resolution is needed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;builder check&lt;/code&gt; performs static syntax and relative-import checks against the shared Builder files. It does not replace the local type checker, test suite, production build, or browser checks you just ran.&lt;/p&gt;

&lt;p&gt;Refresh the Builder preview after a successful push. Repeat the search test with a term containing spaces, such as &lt;code&gt;login&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Use live sync when you want a tighter editing loop
&lt;/h2&gt;

&lt;p&gt;For continuous local editing, run this from the pulled project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder dev &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--dir&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--open&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI starts a live sync session and attempts to open a supported editor if its launcher is available. If it cannot open one, open the folder manually.&lt;/p&gt;

&lt;p&gt;You can now edit local files and use Builder chat in the same terminal session. Changes made in the browser Builder can also arrive in the local workspace through sync.&lt;/p&gt;

&lt;p&gt;At the &lt;code&gt;dev&amp;gt;&lt;/code&gt; prompt, inspect the session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/status
/diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;/status&lt;/code&gt; to see local changes, incoming changes, and pending drafts. Use &lt;code&gt;/diff&lt;/code&gt; to review the actual differences.&lt;/p&gt;

&lt;p&gt;Finish one focused change before having another assistant rewrite the same files. Shared files still need coordination, even when synchronization is automatic.&lt;/p&gt;

&lt;p&gt;To stop live sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a single sync pass instead of a running session, use your normal terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--dir&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Fix a failed build using its actual output
&lt;/h2&gt;

&lt;p&gt;Suppose a change adds an invalid component prop and the production build fails. A useful AI fix starts with the files and error that produced that failure.&lt;/p&gt;

&lt;p&gt;Start interactive Builder chat from the local project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder chat &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--dir&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the &lt;code&gt;builder&amp;gt;&lt;/code&gt; prompt, capture the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/run --capture npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it fails, ask Builder to work from that captured result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/fix --last-run
/diff
/rerun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what each step does:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/run --capture npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes the local build and saves bounded output, exit status, and the tested source state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/fix --last-run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends that failure context to Builder and requests a proposed fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/diff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shows the changes for review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/rerun&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicitly repeats the captured command against the current local files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The proposed fix is applied locally as a pending draft. The shared Builder snapshot stays unchanged until you accept it. Automatic sync is held while the draft is pending.&lt;/p&gt;

&lt;p&gt;If the rerun succeeds, run the other relevant checks before accepting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/run npm run typecheck
/run npm test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inspect the outputs. If the fix meets the requirements and the checks pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/accept
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it is the wrong change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Acceptance saves the local draft as a shared Builder checkpoint, including earlier local edits that are part of that workspace. Review the full diff before accepting.&lt;/p&gt;

&lt;p&gt;When the shared Builder project changed while you were testing, acceptance can stop to prevent overwriting newer work. Follow the CLI's instructions, resolve the differences, and retest against the updated files.&lt;/p&gt;

&lt;p&gt;The captured-failure workflow is described on the &lt;a href="https://nexusai.run/ai-app-builder" rel="noopener noreferrer"&gt;NEXUS AI Builder page&lt;/a&gt;. It gives you an explicit sequence: capture, propose, inspect, rerun, and save.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep shell commands and Builder commands separate
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where you are typing&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal terminal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus builder chat --project "$PROJECT_ID" --dir .&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;builder&amp;gt;&lt;/code&gt; or &lt;code&gt;dev&amp;gt;&lt;/code&gt; prompt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/run --capture npm run build&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;builder&amp;gt;&lt;/code&gt; or &lt;code&gt;dev&amp;gt;&lt;/code&gt; prompt&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/diff&lt;/code&gt;, &lt;code&gt;/accept&lt;/code&gt;, &lt;code&gt;/reject&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Slash commands belong inside the interactive Builder session. Type &lt;code&gt;/exit&lt;/code&gt; to return to your normal terminal before running standalone &lt;code&gt;nexus&lt;/code&gt; commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Review checkpoints and the final preview
&lt;/h2&gt;

&lt;p&gt;Back in your normal terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder versions &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus builder open &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The versions list includes checkpoint message IDs. Keep the ID of a known working state with your project notes.&lt;/p&gt;

&lt;p&gt;If you need to restore an earlier Builder file state, choose its actual message ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CHECKPOINT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_SELECTED_CHECKPOINT_MESSAGE_ID'&lt;/span&gt;
nexus builder revert &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHECKPOINT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reverting changes the current Builder files and saves a new checkpoint. Sync the result locally and rerun the relevant checks. It does not restore a deployed database or change an already running deployment.&lt;/p&gt;

&lt;p&gt;For the ticket prototype, finish with the five acceptance checks from the start of this guide. Check a narrow viewport as well as a desktop screen, and test the empty-result state in search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Deploy the reviewed Builder snapshot
&lt;/h2&gt;

&lt;p&gt;Make sure your local changes have been accepted or successfully pushed before deploying. The deployment command uses the shared Builder snapshot, rather than uncommitted changes sitting only on your laptop.&lt;/p&gt;

&lt;p&gt;From your normal terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus builder deploy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; support-desk-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker

nexus deploy list &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;--provider docker&lt;/code&gt; selects NEXUS AI managed deployment infrastructure. It is separate from the AI provider that generated your code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;builder deploy&lt;/code&gt; submits a new deployment and prints its ID. It does not run your local checks first, and this command does not have a &lt;code&gt;--wait&lt;/code&gt; flag in CLI 4.3.2.&lt;/p&gt;

&lt;p&gt;Copy the returned deployment ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DEPLOYMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_THE_RETURNED_DEPLOYMENT_ID'&lt;/span&gt;
nexus deploy status &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYMENT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After inspecting the status, press Ctrl+C to stop watching. For a build failure, inspect build logs. For an app that starts but behaves incorrectly, inspect runtime logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYMENT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; build &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
nexus deploy logs &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYMENT_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; runtime &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the actual public URL from the status output. Create a ticket, search for it, change its status, and refresh the page.&lt;/p&gt;

&lt;p&gt;Because this prototype uses &lt;code&gt;localStorage&lt;/code&gt;, you will create fresh demo records on the deployed origin. Records you made on the preview origin will not appear automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; the deployed app passes the same user checks as the reviewed version. A submitted deployment request alone is not confirmation that the app works.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://nexusai.run/kb/cli-app-builder" rel="noopener noreferrer"&gt;CLI Builder guide&lt;/a&gt; covers project creation, file editing, checkpoints, preview links, and snapshot deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the prototype needs a shared database
&lt;/h2&gt;

&lt;p&gt;Once several people need access to the same tickets, browser storage no longer meets the requirements. Treat that as a new feature with its own review and checks.&lt;/p&gt;

&lt;p&gt;Ask Builder to plan the data change before implementing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Turn this ticket prototype into a team app using a backend and
PostgreSQL through Prisma. Replace browser-local ticket storage with
server-side persistence. Keep the current search and status behavior.
Explain the schema, migration, environment requirements, and tests
before changing files. Do not deploy the change yet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a persistent-data app, Builder supports a Next.js and Prisma workflow. Add authentication and authorization as a separate follow-up so you can verify which users may access which tickets.&lt;/p&gt;

&lt;p&gt;Database work introduces new acceptance checks: records must persist across sessions, permissions must hold, migrations must run against the intended database, and preview data must be distinguished from deployed data. Follow the generated environment instructions and configure credentials through the platform's secure settings.&lt;/p&gt;

&lt;p&gt;Before migrating an existing database, take a backup and test the recovery procedure. See the companion &lt;a href="//../docs/kb-database-backup-restore-cli.md"&gt;database backup and migration guide&lt;/a&gt;, including its current engine-specific caveats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Already have a Git repository from your AI code editor?
&lt;/h2&gt;

&lt;p&gt;Use the source deployment path when your existing repository is the release source. You do not need to create a duplicate Builder project just to deploy it.&lt;/p&gt;

&lt;p&gt;After committing and pushing the code you intend to deploy, replace the repository URL and branch below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/YOUR_USERNAME/YOUR_APP.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt; main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; editor-built-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; DEVELOPMENT &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an alternative to the Builder snapshot deployment in Step 9. Select the path that matches the source you want to release.&lt;/p&gt;

&lt;p&gt;For a private repository, configure repository access in NEXUS AI. For a database-backed app, configure its required database, environment, and migrations as part of that deployment setup.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://nexusai.run/solutions/developers" rel="noopener noreferrer"&gt;NEXUS AI developer workflow&lt;/a&gt; covers working in an editor, using GitHub, and deploying through developer tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common problems and the next useful step
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The preview shows older code&lt;/td&gt;
&lt;td&gt;Confirm the local edits were pushed or accepted, inspect &lt;code&gt;/status&lt;/code&gt;, and refresh the preview.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An AI fix changed more than expected&lt;/td&gt;
&lt;td&gt;Inspect &lt;code&gt;/diff&lt;/code&gt; before acceptance. Reject the draft if it does not meet the requested scope.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;check&lt;/code&gt; passes but the build fails&lt;/td&gt;
&lt;td&gt;Capture the actual build with &lt;code&gt;/run --capture npm run build&lt;/code&gt;; static syntax checks cover a narrower set of problems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A draft blocks sync or another prompt&lt;/td&gt;
&lt;td&gt;Review it, then use &lt;code&gt;/accept&lt;/code&gt; or &lt;code&gt;/reject&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The files contain merge markers&lt;/td&gt;
&lt;td&gt;Resolve the conflicting edits, rerun checks, and sync the resolved files.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The AI provider list is empty&lt;/td&gt;
&lt;td&gt;Check account configuration and available providers with &lt;code&gt;nexus builder providers&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A project script is missing&lt;/td&gt;
&lt;td&gt;Inspect &lt;code&gt;package.json&lt;/code&gt; and ask for a working script and its checks before using it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The deployed version misses an edit&lt;/td&gt;
&lt;td&gt;Confirm whether you deployed a shared Builder snapshot or a Git branch, then inspect that source.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The terminal rejects &lt;code&gt;/diff&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Enter Builder chat first; slash commands do not run directly in your shell.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Do I need to switch code editors?
&lt;/h3&gt;

&lt;p&gt;No. Pull the Builder project and edit the local files in your preferred editor. Use push or live sync to return the changes to Builder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is AI Builder the same as an AI code editor?
&lt;/h3&gt;

&lt;p&gt;They overlap in generating and editing code, but serve different parts of this workflow. The editor gives you direct local file work. Builder maintains the shared project, conversation, and browser preview. The CLI connects those files and release actions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does every AI change go straight into the shared project?
&lt;/h3&gt;

&lt;p&gt;In interactive chat with a local checkout attached, proposed file changes use the pending-draft workflow. You review and accept them before they become the shared checkpoint. Other Builder operations, such as browser edits or standalone snapshot commands, have their own effects; check the source you are changing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I choose a different AI provider or model?
&lt;/h3&gt;

&lt;p&gt;In interactive Builder chat or dev mode, &lt;code&gt;/provider&lt;/code&gt; and &lt;code&gt;/model&lt;/code&gt; show available choices. Access depends on your account and configured providers. These settings control code generation, while the deployment provider selects where the app runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a Builder checkpoint replace Git?
&lt;/h3&gt;

&lt;p&gt;Use checkpoints to revisit Builder file states. Keep Git for the repository history, branches, review, and CI workflow your team needs. Neither a source checkpoint nor a Git commit is a database backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I check before sharing the app URL?
&lt;/h3&gt;

&lt;p&gt;Confirm deployment status, inspect relevant logs, and complete the main user journey on the public URL. For apps with a database or authentication, include persistence and access-control checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with one feature you can verify
&lt;/h2&gt;

&lt;p&gt;Choose a small app or a single change in an existing project. Write down the expected behavior, build the first version, and keep the files visible in your editor.&lt;/p&gt;

&lt;p&gt;Then use the same loop each time: inspect the change, run the relevant checks, save the reviewed version, and verify it after deployment. &lt;a href="https://nexusai.run/ai-app-builder" rel="noopener noreferrer"&gt;Explore NEXUS AI Builder&lt;/a&gt; when you are ready to connect that workflow to your next app.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>nocode</category>
      <category>programming</category>
    </item>
    <item>
      <title>AI Coding: What Matters Most Before Your Next Database Migration</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Tue, 20 Oct 2026 18:05:39 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/ai-coding-what-matters-most-before-your-next-database-migration-pn9</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/ai-coding-what-matters-most-before-your-next-database-migration-pn9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Protect your app’s data with one backup and restore workflow for PostgreSQL, MongoDB, MySQL, and Redis.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Author:&lt;/strong&gt; NEXUS AI Team&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Category:&lt;/strong&gt; Knowledge Base · Databases · CLI&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Command syntax verified:&lt;/strong&gt; NEXUS AI CLI 4.3.2, October 20, 2026&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Reading time:&lt;/strong&gt; 11 minutes&lt;/p&gt;

&lt;p&gt;You used AI to write code that changes your database. Before running that migration or releasing the app, you need a backup, a way to test recovery, and a clear path back if the change goes wrong.&lt;/p&gt;

&lt;p&gt;This guide explains how to back up and restore your database from the CLI when working with AI-generated code. It covers PostgreSQL, MongoDB, MySQL, and Redis through one NEXUS AI command workflow.&lt;/p&gt;

&lt;p&gt;With NEXUS AI, the commands stay familiar whether your app uses PostgreSQL, MongoDB, MySQL, or Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexus db services
nexus db backup &amp;lt;service-id&amp;gt;
nexus db backups &amp;lt;service-id&amp;gt;
nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You choose the database service and the backup. NEXUS AI selects the database's native backup and restore tools and uses the service's stored connection settings. Your laptop needs the NEXUS AI CLI; the engine-specific tools run on the platform.&lt;/p&gt;

&lt;p&gt;This guide shows how that simplifies release preparation and recovery, using a worked example of an orders application. The commands are real CLI commands. The application names describe an example you can adapt, rather than a reported customer incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which databases this guide covers
&lt;/h2&gt;

&lt;p&gt;Use this workflow for database services running alongside a deployment on NEXUS AI managed infrastructure, such as services added with &lt;code&gt;--services postgres&lt;/code&gt;, &lt;code&gt;mongodb&lt;/code&gt;, &lt;code&gt;mysql&lt;/code&gt;, or &lt;code&gt;redis&lt;/code&gt; on the &lt;code&gt;docker&lt;/code&gt; provider.&lt;/p&gt;

&lt;p&gt;Standalone databases and managed cloud database instances have a separate snapshot workflow under &lt;code&gt;nexus managed-db&lt;/code&gt;. An external database connection does not automatically become a deployment-service backup target. See the &lt;a href="https://nexusai.run/database-backup-restore" rel="noopener noreferrer"&gt;database backup overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before starting, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18 or later and a NEXUS AI account with access to the deployment.&lt;/li&gt;
&lt;li&gt;A running database service visible to your current organization.&lt;/li&gt;
&lt;li&gt;For the recovery rehearsal, a separate test deployment with a database of the same engine and a compatible version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why developers only need one command workflow
&lt;/h2&gt;

&lt;p&gt;Without a shared interface, each engine introduces its own utility, connection options, credentials, backup format, and restore procedure. NEXUS AI puts those mechanics behind the database service ID.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;What NEXUS AI uses behind the scenes&lt;/th&gt;
&lt;th&gt;Your backup command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pg_dump&lt;/code&gt; custom-format backup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mongodump&lt;/code&gt; directory packaged into a compressed backup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mysqldump&lt;/code&gt; SQL backup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;RDB snapshot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nexus db backup &amp;lt;service-id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Listing, scheduling, and downloading also use the same commands. Restore uses the same CLI syntax across engines, with each engine's own data replacement behavior underneath.&lt;/p&gt;

&lt;p&gt;You still decide what to back up, where to restore it, and whether the recovered application is correct. The CLI removes the need to assemble a different backup command for each database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario: protect an orders app before a release
&lt;/h2&gt;

&lt;p&gt;Assume you maintain these two deployments:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orders-api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production application and its PostgreSQL service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orders-recovery&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Isolated recovery-test application and a compatible PostgreSQL service in the same organization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your coding AI has proposed a migration that changes how order statuses are stored. Before running that generated code, you want to save the current data, review the migration, and prove that you can restore the backup into the recovery environment.&lt;/p&gt;

&lt;p&gt;Keep the recovery app disconnected from live payments, email delivery, and production job processing. A restored copy contains the data represented by the backup. Use an environment approved for that data, and sanitize it before broader development use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install the CLI and confirm your account
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nexusapp-cli@latest
nexus &lt;span class="nt"&gt;--version&lt;/span&gt;
nexus auth login
nexus auth &lt;span class="nb"&gt;whoami&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm that the reported account and organization are the ones that own your deployments. You do not enter individual database passwords for the backup commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Find the database service IDs
&lt;/h3&gt;

&lt;p&gt;List deployments and their database services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy list
nexus db services orders-api
nexus db services orders-recovery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service table includes &lt;code&gt;SERVICE ID&lt;/code&gt;, &lt;code&gt;TYPE&lt;/code&gt;, &lt;code&gt;DEPLOYMENT&lt;/code&gt;, &lt;code&gt;STATUS&lt;/code&gt;, &lt;code&gt;AUTO-BACKUP&lt;/code&gt;, and &lt;code&gt;RETENTION&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy the PostgreSQL service ID from each deployment. &lt;strong&gt;A service ID identifies the database service; it is different from the deployment ID.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the commands below, set these shell variables after replacing both example values with your actual service IDs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROD_SERVICE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_PRODUCTION_DATABASE_SERVICE_ID'&lt;/span&gt;
&lt;span class="nv"&gt;RECOVERY_SERVICE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_RECOVERY_DATABASE_SERVICE_ID'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variables are ordinary Bash or Zsh variables used to keep the examples readable. They are not special NEXUS AI settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Take a backup before the change
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus db backups &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command creates the backup and prints its ID, engine type, size, and creation time. The second lists the service's backups, including their status.&lt;/p&gt;

&lt;p&gt;Identify the backup you just created and check that its status is &lt;code&gt;completed&lt;/code&gt;. Save that specific ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PRE_RELEASE_BACKUP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_COMPLETED_BACKUP_ID'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the ID in your release notes with the service and release you are protecting. A backup taken before the change and a backup taken after a failure serve different purposes.&lt;/p&gt;

&lt;p&gt;If you need a precise application-wide checkpoint, coordinate writes while taking it. Backing up several services one after another does not produce an atomic snapshot across all of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Download a copy if you need separate retention
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRE_RELEASE_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI saves the backup locally using its original filename. For a custom PostgreSQL backup path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRE_RELEASE_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--out&lt;/span&gt; ./backups/orders-before-status-change.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default-filename command works across all four engines and keeps the engine's appropriate file extension. Store downloaded files in your approved backup location, outside your source repository.&lt;/p&gt;

&lt;p&gt;Downloading creates a local copy. It does not test that the backup can be restored.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Rehearse recovery in the separate database
&lt;/h3&gt;

&lt;p&gt;If the recovery database contains data you may need, back it up first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RECOVERY_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restore the production backup into that recovery database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore-to &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RECOVERY_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRE_RELEASE_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the confirmation prompt and confirm the target. &lt;code&gt;restore-to&lt;/code&gt; writes into the selected target service. The source database is not the restore target.&lt;/p&gt;

&lt;p&gt;The source backup and target service must belong to the same organization and use the same database engine. Use a compatible engine version as well. This command does not convert a PostgreSQL backup into MySQL or another engine.&lt;/p&gt;

&lt;p&gt;Check the recovery app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy status orders-recovery
nexus deploy logs orders-recovery &lt;span class="nt"&gt;--type&lt;/span&gt; runtime &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open its URL and verify the application behavior. For this orders example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open several known orders and confirm their customer, total, and status.&lt;/li&gt;
&lt;li&gt;Confirm the expected orders appear in the relevant filtered lists.&lt;/li&gt;
&lt;li&gt;Perform a controlled test write with external side effects disabled.&lt;/li&gt;
&lt;li&gt;Refresh the page and confirm the test change persists.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Database restore success confirms the restore operation finished. These application checks confirm the recovered data works with the app version you intend to run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; you have a completed pre-release backup and a verified PostgreSQL recovery copy. You can now test the status migration in recovery before releasing it to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Recover production if the release needs a data rollback
&lt;/h3&gt;

&lt;p&gt;Suppose the migration changes existing statuses incorrectly. First stop application writes and any workers that could modify the affected data, using your application's maintenance controls. Prepare the application version compatible with the old schema and data before reopening traffic.&lt;/p&gt;

&lt;p&gt;Do not assume that stopping an entire deployment leaves its database available. Backup and restore need a running database container.&lt;/p&gt;

&lt;p&gt;Preserve the current production state before replacing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus db backups &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record this new backup ID separately. It preserves the post-change state for investigation or later reconciliation.&lt;/p&gt;

&lt;p&gt;Then restore the pre-release backup into its original service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROD_SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PRE_RELEASE_BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm the prompt only after checking the service and backup IDs. In-place restore can overwrite or remove current data. Changes made after the chosen backup may need reconciliation, including orders accepted between that backup and maintenance mode.&lt;/p&gt;

&lt;p&gt;Restore the compatible application release through your normal release procedure, check logs, and repeat the order checks before resuming writes. A database restore does not roll back application code, and a code rollback does not restore database data.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/nexusrun/nexusai/wiki/CLI" rel="noopener noreferrer"&gt;NEXUS AI CLI reference&lt;/a&gt; documents the same-service &lt;code&gt;restore&lt;/code&gt; and separate-target &lt;code&gt;restore-to&lt;/code&gt; commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply the same commands to MongoDB, MySQL, and Redis
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MongoDB restore caveat:&lt;/strong&gt; source verification for this draft identified a path mismatch when restoring platform-created MongoDB backups. Resolve and test that restore path before relying on it for MongoDB recovery. The CLI command names below are supported, but this guide does not claim a successful live restore test for every engine. The worked recovery example above uses PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis backup caveat:&lt;/strong&gt; the current implementation waits a fixed interval after requesting a background save, rather than checking that the save completed. Verify snapshot freshness and recovery on a representative dataset before relying on that backup path.&lt;/p&gt;

&lt;p&gt;For a different engine, run &lt;code&gt;nexus db services&lt;/code&gt; and copy that database's service ID. The core sequence stays the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SERVICE_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_DATABASE_SERVICE_ID'&lt;/span&gt;

nexus db backup &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
nexus db backups &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy a completed backup ID from the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BACKUP_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'REPLACE_WITH_COMPLETED_BACKUP_ID'&lt;/span&gt;

nexus db backup-download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you intend to restore that service, after coordinating writes and preserving any current state you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not add a database-engine flag to these commands. NEXUS AI resolves the engine from the service.&lt;/p&gt;

&lt;h3&gt;
  
  
  What still differs between engines
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;What to account for during restore&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;Restore resets the target's &lt;code&gt;public&lt;/code&gt; schema before loading the backup. Objects and data can be removed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;Restore uses collection drop behavior for collections being restored. Check the recovered documents through the app.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;Restore imports SQL into the target database. Objects not addressed by that dump can remain.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;Restore replaces the RDB file and stops and starts the Redis container. Allow for interruption and reconnects.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shared interface does not make every restore an identical byte-for-byte replacement of the whole service. Keep recovery targets compatible, and test the data your application depends on.&lt;/p&gt;

&lt;p&gt;For Redis used as a job queue, restoring an earlier dataset can bring back jobs that already ran. Coordinate workers and account for duplicate side effects. If Redis only stores disposable cache data, decide whether recovery should rebuild that cache instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable daily backups with the same command
&lt;/h2&gt;

&lt;p&gt;After the release, enable a daily backup policy for the chosen service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-schedule &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--enable&lt;/span&gt; &lt;span class="nt"&gt;--retention&lt;/span&gt; 14
nexus db services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that the service reports automatic backups as &lt;code&gt;on&lt;/code&gt; and retention as &lt;code&gt;14d&lt;/code&gt;. Enable the policy separately for each database service that needs it.&lt;/p&gt;

&lt;p&gt;Scheduled backups use a daily interval, with a default retention of seven days and a minimum of seven days. The command above raises retention to fourteen days. It does not set a particular wall-clock execution time.&lt;/p&gt;

&lt;p&gt;While the schedule is enabled, the retention window applies to NEXUS AI-created backups, including on-demand backups. Download release checkpoints that you need to retain beyond that window. Uploaded backups are excluded from that automatic rotation.&lt;/p&gt;

&lt;p&gt;These are scheduled and on-demand recovery points, rather than continuous point-in-time recovery. Take an on-demand backup before a risky change even when daily backups are enabled. See the &lt;a href="https://nexusai.run/database-backup-restore" rel="noopener noreferrer"&gt;backup schedule and retention documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To disable future scheduled backups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-schedule &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--disable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other useful commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inspect backups as JSON
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db services &lt;span class="nt"&gt;--json&lt;/span&gt;
nexus db backups &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
nexus db backup &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last command creates a new backup and returns its record as JSON. It is not a read-only inspection command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate a temporary download link
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db backup-download &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--share&lt;/span&gt; &lt;span class="nt"&gt;--ttl&lt;/span&gt; 900
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a link lasting fifteen minutes. Anyone with the link can download the file until it expires. The supported lifetime is 30 to 3,600 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skip restore confirmation in a reviewed automation job
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus db restore &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BACKUP_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--yes&lt;/code&gt; skips the interactive confirmation. It does not verify that you chose the intended data. Use it only when your automation has already established the service, backup, and maintenance conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A command or flag is missing&lt;/td&gt;
&lt;td&gt;Update &lt;code&gt;nexusapp-cli&lt;/code&gt;, then run &lt;code&gt;nexus db --help&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No database services appear&lt;/td&gt;
&lt;td&gt;Confirm the organization with &lt;code&gt;nexus auth whoami&lt;/code&gt;. Check whether the database is a deployment service or a standalone managed database.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Service has no running container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The database must be running for backup and restore. Restore database availability before retrying.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Backup not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the organization, source service, and backup ID. In-place &lt;code&gt;restore&lt;/code&gt; needs a backup belonging to that service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engine mismatch during &lt;code&gt;restore-to&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Choose a target service of the same engine as the backup.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A downloaded file is missing later&lt;/td&gt;
&lt;td&gt;Download again while the platform backup still exists. The local download location and platform retention are separate.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore finishes but the app fails&lt;/td&gt;
&lt;td&gt;Check the app's connection settings, schema compatibility, and runtime logs. A restore does not change the deployed code version.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Quick command reference
&lt;/h2&gt;

&lt;p&gt;The angle-bracket values below are placeholders, not literal shell input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nexus db services [deployment-name-or-id]
nexus db backup &amp;lt;service-id&amp;gt;
nexus db backups &amp;lt;service-id&amp;gt;
nexus db backup-download &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db restore &amp;lt;service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db restore-to &amp;lt;target-service-id&amp;gt; &amp;lt;backup-id&amp;gt;
nexus db backup-schedule &amp;lt;service-id&amp;gt; --enable --retention 14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For developers moving between Python, Node.js, SQL databases, document storage, and Redis, this is the practical benefit: one backup workflow to learn and reuse. Find the service, create the backup, retain the recovery point, and rehearse the restore through your application.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>postgres</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Deploy a full-stack Python app with MongoDB using CLI or MCP</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Tue, 20 Oct 2026 11:04:28 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/deploy-a-full-stack-python-app-with-mongodb-using-cli-or-mcp-1m7d</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/deploy-a-full-stack-python-app-with-mongodb-using-cli-or-mcp-1m7d</guid>
      <description>&lt;p&gt;You have a Python app working on your laptop. Now you need somewhere to run it, a database that keeps its data, and a way to find out what went wrong when a request fails.&lt;/p&gt;

&lt;p&gt;This guide walks through that next step. You will build a small notes app with a browser interface, a Flask backend, and MongoDB for storage, then deploy it on NEXUS AI.&lt;/p&gt;

&lt;p&gt;You can deploy from your terminal with the CLI or ask an AI assistant to deploy through MCP. Both paths use the same application and database setup. Choose one deployment path to avoid creating two copies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Understand what you are deploying
&lt;/h2&gt;

&lt;p&gt;The app has three parts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;An HTML page with a form and a list of saved notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python backend&lt;/td&gt;
&lt;td&gt;Flask routes that read and write notes through PyMongo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;Stores notes outside the app container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The request flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
   |
   | HTTPS
   v
Flask app on NEXUS AI
   |
   | PyMongo connection over the deployment network
   v
MongoDB service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flask serves the HTML and the API from the same origin, so this example needs one web deployment and no separate frontend build.&lt;/p&gt;

&lt;p&gt;We will use NEXUS AI managed infrastructure with &lt;code&gt;provider: docker&lt;/code&gt;. Its MongoDB sidecar service supplies the app's connection settings, including &lt;code&gt;MONGO_URI&lt;/code&gt; and &lt;code&gt;MONGO_DATABASE&lt;/code&gt;. The MongoDB sidecar option is not supported on the Cloud Run, ECS Fargate, or Azure Container Apps deployment paths.&lt;/p&gt;

&lt;p&gt;Before starting, have these ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12 and Git.&lt;/li&gt;
&lt;li&gt;Docker for the local MongoDB example.&lt;/li&gt;
&lt;li&gt;A GitHub account and an empty repository named &lt;code&gt;python-mongo-notes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A NEXUS AI account with access to managed container deployments and enough quota for the app and database.&lt;/li&gt;
&lt;li&gt;Node.js 18 or later for the NEXUS AI CLI, or an MCP client such as Cursor or Claude Code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a shared public demo: everyone who opens the URL can read and add notes. Use sample content. Add authentication and per-user access controls before storing private information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create the project
&lt;/h2&gt;

&lt;p&gt;Run these commands in Bash or Zsh on macOS or Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;python-mongo-notes
&lt;span class="nb"&gt;cd &lt;/span&gt;python-mongo-notes
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;span class="nb"&gt;mkdir &lt;/span&gt;templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flask&amp;gt;=3.1,&amp;lt;4
pymongo&amp;gt;=4.10,&amp;lt;5
gunicorn&amp;gt;=23,&amp;lt;24
python-dotenv&amp;gt;=1,&amp;lt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These ranges keep the example within the intended major versions. Before maintaining a release, lock the exact versions you have tested.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.gitignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.venv/
__pycache__/
.env
.env.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your final project will contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python-mongo-notes/
  app.py
  templates/
    index.html
  requirements.txt
  Dockerfile
  .dockerignore
  .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Add the Python backend
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;app.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MongoClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pymongo.errors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PyMongoError&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MAX_CONTENT_LENGTH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MONGO_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;serverSelectionTimeoutMS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MONGO_DATABASE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;appdb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_notes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
    &lt;span class="p"&gt;])&lt;/span&gt;


&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;silent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Send a JSON object.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter a note between 1 and 500 characters.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_one&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inserted_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;


&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;PyMongoError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unhealthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unavailable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;connected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MONGO_URI&lt;/code&gt; comes from the environment. No database credentials belong in the source code or browser JavaScript. PyMongo manages connection pooling through &lt;code&gt;MongoClient&lt;/code&gt;. See the &lt;a href="https://www.mongodb.com/docs/languages/python/pymongo-driver/current/connect/mongoclient/" rel="noopener noreferrer"&gt;official MongoClient documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/health&lt;/code&gt; route actually pings MongoDB. A working homepage alone does not tell you whether the app can reach its database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add the browser interface
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;templates/index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Python + MongoDB Notes&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt; &lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;640px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60px&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
           &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#142033&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0a60ff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;overflow-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;anywhere&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Your notes, saved with Python + MongoDB&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"note-form"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"note"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New note&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"note"&lt;/span&gt; &lt;span class="na"&gt;maxlength=&lt;/span&gt;&lt;span class="s"&gt;"500"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Save note&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"notes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#note-form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#note&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadNotes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not load notes.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceChildren&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/notes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not save your note.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Note saved.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadNotes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;loadNotes&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser sends requests to &lt;code&gt;/api/notes&lt;/code&gt;. Only the Python process connects to MongoDB. Using &lt;code&gt;textContent&lt;/code&gt; also makes note content display as text instead of executable HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Run the complete app locally
&lt;/h2&gt;

&lt;p&gt;Start a local MongoDB container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; python-mongo-notes-db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 127.0.0.1:27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; python-mongo-notes-data:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo:7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This local development database has no authentication and binds only to your machine's loopback interface. The named volume stores data separately from the container. On later runs, start the existing container with &lt;code&gt;docker start python-mongo-notes-db&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a local &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_URI=mongodb://127.0.0.1:27017/appdb
MONGO_DATABASE=appdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start Flask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flask &lt;span class="nt"&gt;--app&lt;/span&gt; app run &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;, save a note, and refresh the page. The note should still appear.&lt;/p&gt;

&lt;p&gt;In another terminal, check the database connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--fail-with-body&lt;/span&gt; http://127.0.0.1:8000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected response, with key order allowed to vary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"connected"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; the page loads, a note survives a refresh, and &lt;code&gt;/health&lt;/code&gt; reports a database connection. Fix local errors before deploying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Package the app and push it to GitHub
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; templates ./templates&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["sh", "-c", "exec gunicorn --bind 0.0.0.0:${PORT:-8000} --workers 2 --access-logfile - --error-logfile - app:app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gunicorn runs Flask in the deployment container and listens on all container interfaces. Its logs go to stdout and stderr so you can inspect them through NEXUS AI. Flask's development server is for local use; see &lt;a href="https://flask.palletsprojects.com/en/stable/deploying/gunicorn/" rel="noopener noreferrer"&gt;Flask's deployment guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;.dockerignore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.git
.venv
__pycache__
.env
.env.*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push the project to the empty GitHub repository you created. Replace &lt;code&gt;YOUR_USERNAME&lt;/code&gt; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add app.py templates/index.html requirements.txt Dockerfile .dockerignore .gitignore
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add Python and MongoDB notes app"&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git remote add origin https://github.com/YOUR_USERNAME/python-mongo-notes.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The explicit file list keeps your local &lt;code&gt;.env&lt;/code&gt; out of the commit. For this tutorial, use a public repository containing only demo code. A private repository requires repository access configured in NEXUS AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Deploy using the CLI
&lt;/h2&gt;

&lt;p&gt;If you prefer an AI assistant, skip to Step 8. You only need one deployment path.&lt;/p&gt;

&lt;p&gt;Install the CLI and sign in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nexusapp-cli@latest
nexus auth login
nexus auth &lt;span class="nb"&gt;whoami&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the GitHub repository with its MongoDB service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/YOUR_USERNAME/python-mongo-notes.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt; main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; python-mongo-notes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; DEVELOPMENT &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--framework&lt;/span&gt; python &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dockerfile&lt;/span&gt; Dockerfile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; mongodb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what matters in that command:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--dockerfile Dockerfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds the app using your repository's Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--provider docker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selects NEXUS AI managed container infrastructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--services mongodb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds the MongoDB sidecar and injects its connection environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--environment DEVELOPMENT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Labels this tutorial deployment as development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--wait&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Waits for the deployment to reach a terminal build/run state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Do not pass your local &lt;code&gt;.env&lt;/code&gt; file to this deployment. Its &lt;code&gt;127.0.0.1&lt;/code&gt; address points to the app container in the cloud, which is a different machine from your laptop. The platform supplies the database connection instead.&lt;/p&gt;

&lt;p&gt;Save the deployment ID from the output. Inspect its status and public URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy status python-mongo-notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For command details and version-specific options, see the &lt;a href="https://github.com/nexusrun/nexusai/wiki/CLI" rel="noopener noreferrer"&gt;NEXUS AI CLI reference&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Deploy using MCP instead
&lt;/h2&gt;

&lt;p&gt;MCP, the Model Context Protocol, lets an AI assistant call NEXUS AI deployment tools. You describe the task, and the assistant sends structured tool calls.&lt;/p&gt;

&lt;p&gt;For Claude Code, connect the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http nexus-ai https://mcp.nexusai.run/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete browser sign-in when prompted. For Cursor, add the same URL in its MCP settings. The &lt;a href="https://nexusai.run/mcp-deployment-platform" rel="noopener noreferrer"&gt;NEXUS AI MCP setup guide&lt;/a&gt; explains the connection flow.&lt;/p&gt;

&lt;p&gt;Allow the deployment read, create, and log scopes needed for this workflow: &lt;code&gt;deployments:read&lt;/code&gt;, &lt;code&gt;deployments:create&lt;/code&gt;, and &lt;code&gt;deployments:logs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then send your assistant this prompt, replacing the repository URL:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy &lt;a href="https://github.com/YOUR_USERNAME/python-mongo-notes.git" rel="noopener noreferrer"&gt;https://github.com/YOUR_USERNAME/python-mongo-notes.git&lt;/a&gt; from branch main to NEXUS AI. Name it python-mongo-notes, use the docker provider and DEVELOPMENT environment, build with the repository Dockerfile, and attach the mongodb service. Keep health checks enabled. Confirm my account and available providers first. Follow the returned deployment ID until it is running, inspect health, and return its public URL. If deployment fails, inspect the build and runtime logs. Do not print database credentials.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The assistant should first call &lt;code&gt;nexusai_whoami&lt;/code&gt; and &lt;code&gt;nexusai_providers_list&lt;/code&gt; with empty argument objects. The source deployment tool call then uses these arguments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP tool: &lt;code&gt;nexusai_deploy_source&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repoUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/YOUR_USERNAME/python-mongo-notes.git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repoBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python-mongo-notes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEVELOPMENT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"framework"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dockerfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"services"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mongodb"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"healthCheckEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These JSON blocks are MCP tool arguments for your assistant, not terminal commands. Tool names may have a client-specific prefix. If your connection does not expose the &lt;code&gt;services&lt;/code&gt; field, update the connection before proceeding with this recipe.&lt;/p&gt;

&lt;p&gt;With the returned ID, the assistant checks progress:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP tool: &lt;code&gt;nexusai_deploy_status&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploymentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_RETURNED_DEPLOYMENT_ID"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once running, it checks platform health:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP tool: &lt;code&gt;nexusai_deploy_health&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploymentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_RETURNED_DEPLOYMENT_ID"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A queued deployment is not yet a working application. Keep the actual returned ID and follow status through completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Verify the deployed app and MongoDB
&lt;/h2&gt;

&lt;p&gt;Whichever deployment path you used, copy the actual public HTTPS URL from the deployment output. Set it in your terminal, replacing the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://REPLACE_WITH_YOUR_APP_HOSTNAME'&lt;/span&gt;
curl &lt;span class="nt"&gt;--fail-with-body&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/health"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected result is &lt;code&gt;status: ok&lt;/code&gt; and &lt;code&gt;database: connected&lt;/code&gt;. This explicit request tests the application's database check even if platform health checks use another route.&lt;/p&gt;

&lt;p&gt;Create a note through the deployed API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--fail-with-body&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/notes"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text":"My Python app is live with MongoDB."}'&lt;/span&gt;

curl &lt;span class="nt"&gt;--fail-with-body&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP_URL&lt;/span&gt;&lt;span class="s2"&gt;/api/notes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the public URL in your browser. The same note should appear. Add another note through the form and refresh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; the HTTPS page works, the API can write and read notes, and the database health check succeeds. You have verified the full request path from browser to Python to MongoDB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: Read logs and deploy updates
&lt;/h2&gt;

&lt;p&gt;If something fails, start with the logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy logs python-mongo-notes &lt;span class="nt"&gt;--type&lt;/span&gt; build &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
nexus deploy logs python-mongo-notes &lt;span class="nt"&gt;--type&lt;/span&gt; runtime &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;MCP tool: &lt;code&gt;nexusai_deploy_logs&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploymentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_RETURNED_DEPLOYMENT_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"runtime"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;"type": "build"&lt;/code&gt; to inspect build failures.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;First thing to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ModuleNotFoundError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The missing dependency is in the committed &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KeyError: 'MONGO_URI'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MongoDB was attached and its environment reached the app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB connection timeout&lt;/td&gt;
&lt;td&gt;The database is running, and the deployed URI was not overwritten with localhost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication failure&lt;/td&gt;
&lt;td&gt;The injected database credentials match the existing database; inspect settings without printing secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App starts but URL fails&lt;/td&gt;
&lt;td&gt;Gunicorn binds to &lt;code&gt;0.0.0.0&lt;/code&gt; and the configured container port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latest edits are missing&lt;/td&gt;
&lt;td&gt;You committed and pushed them to the deployment's branch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To release an update, commit and push the changed files, then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nexus deploy redeploy python-mongo-notes &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or ask your assistant to use &lt;strong&gt;&lt;code&gt;nexusai_deploy_redeploy&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploymentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REPLACE_WITH_RETURNED_DEPLOYMENT_ID"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this repository-based deployment, omit name and provider overrides to use an in-place rebuild. Afterward, repeat the &lt;code&gt;/health&lt;/code&gt; and notes checks. Existing notes should still be present; verifying this also catches accidental attachment to a new or empty database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before using the app for real users
&lt;/h2&gt;

&lt;p&gt;The tutorial gives you a working deployment foundation. Before collecting real data, add authentication, per-user note ownership, and rate limits. Confirm persistent database storage and a backup policy, then test restoring a backup. App container storage and database backups serve different purposes.&lt;/p&gt;

&lt;p&gt;If you choose MongoDB Atlas later, supply its connection URI through secure deployment settings and configure Atlas network access for your deployment's outbound traffic. Atlas permits connections from entries in its &lt;a href="https://www.mongodb.com/docs/atlas/security/ip-access-list/" rel="noopener noreferrer"&gt;IP access list&lt;/a&gt;. For MCP workflows, keep credential-bearing URIs out of chat and tool arguments; configure them through the authenticated dashboard.&lt;/p&gt;

&lt;p&gt;Start by saving one note, refreshing the page, and seeing it again at your public URL. From there, you can build on an application whose frontend, Python backend, and MongoDB connection have all been checked.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Choose the Best AI for Coding in 2026</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Tue, 20 Oct 2026 11:01:56 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/how-to-choose-the-best-ai-for-coding-in-2026-3npe</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/how-to-choose-the-best-ai-for-coding-in-2026-3npe</guid>
      <description>&lt;p&gt;The best AI for coding depends on the work you want to finish. Cursor is worth evaluating for an editor-centered workflow, Claude Code for terminal-based development, Codex for agent-assisted repository tasks, and GitHub Copilot for teams working through GitHub and supported editors. NEXUS AI is worth evaluating when you want to build, preview, and deploy a full-stack app in a connected workflow.&lt;/p&gt;

&lt;p&gt;If you are new to coding AI, the number of choices can make a simple decision feel complicated. Start with one question: &lt;strong&gt;what should the tool help you finish?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Explaining an unfamiliar function, fixing a bug across several files, and getting a database-backed app online are different jobs. This guide explains the differences, compares five options, and gives you a step-by-step way to choose.&lt;/p&gt;

&lt;p&gt;The recommendations below are editorial judgments based on documented capabilities. They are not a benchmark ranking or the result of a controlled performance test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is coding AI?
&lt;/h2&gt;

&lt;p&gt;Coding AI is software that uses artificial intelligence to help people write, understand, debug, test, and maintain code. Depending on the tool and its permissions, it may suggest a line of code, explain an error, edit project files, or run commands to check its work.&lt;/p&gt;

&lt;p&gt;For example, you could ask a coding assistant:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This Python endpoint returns a 500 error when a customer submits an empty email address. Find the cause, add validation, and verify that valid submissions still work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Useful output would include a focused fix, evidence that the behavior changed, and an explanation you can review.&lt;/p&gt;

&lt;h3&gt;
  
  
  The main types of AI coding tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it helps with&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat assistant&lt;/td&gt;
&lt;td&gt;Explanations, small examples, and debugging conversations&lt;/td&gt;
&lt;td&gt;Whether it has the current project context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor assistant&lt;/td&gt;
&lt;td&gt;Suggestions and changes while you work in an IDE&lt;/td&gt;
&lt;td&gt;Which files it reads and how you review edits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding agent&lt;/td&gt;
&lt;td&gt;Tasks involving multiple files, commands, and verification&lt;/td&gt;
&lt;td&gt;What it actually ran and whether the changes passed review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI app builder&lt;/td&gt;
&lt;td&gt;Creating an application from a description and iterating on a preview&lt;/td&gt;
&lt;td&gt;Whether the backend, database, and deployment meet your needs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A model and a coding tool are also different things. The model produces responses; the tool gives it access to files, terminals, project instructions, and other services. Evaluate the complete setup you will use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best AI for coding: a practical shortlist
&lt;/h2&gt;

&lt;p&gt;Use this table to pick candidates for your own project. The tools have overlapping capabilities, and the right choice can change with your task, client, and plan.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Consider it when you want to…&lt;/th&gt;
&lt;th&gt;Check before committing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Work closely with an agent inside a code editor&lt;/td&gt;
&lt;td&gt;Whether its editing and review workflow suits your project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Use an agent through your terminal or preferred development environment&lt;/td&gt;
&lt;td&gt;Command permissions, project instructions, and usage limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI Codex&lt;/td&gt;
&lt;td&gt;Delegate repository work through terminal, IDE, or cloud workflows&lt;/td&gt;
&lt;td&gt;Execution environment, available integrations, and how you review results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;Keep AI assistance close to GitHub issues, pull requests, and supported editors&lt;/td&gt;
&lt;td&gt;Which capabilities your plan, client, and organization allow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NEXUS AI&lt;/td&gt;
&lt;td&gt;Build and preview an app, then deploy it with its supporting services&lt;/td&gt;
&lt;td&gt;Runtime support, deployment provider, service availability, and resource costs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Cursor: consider it for an editor-centered workflow
&lt;/h3&gt;

&lt;p&gt;Cursor's Agent can search a codebase, edit files, and execute terminal commands. That makes it a reasonable candidate when you want to discuss a change while keeping the relevant code and terminal nearby. These capabilities are described in the &lt;a href="https://cursor.com/docs/agent/overview" rel="noopener noreferrer"&gt;Cursor Agent documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Try it on a feature that crosses your interface and backend. Review whether it follows existing conventions and whether its changes are easy to inspect. A convincing explanation should be backed by a working change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code: consider it for terminal-based development
&lt;/h3&gt;

&lt;p&gt;Claude Code reads code, edits files, and runs commands. It is available through the terminal and other development surfaces, including IDE integrations. See the &lt;a href="https://code.claude.com/docs/en/overview" rel="noopener noreferrer"&gt;Claude Code overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is worth evaluating if your day already involves command-line tools, scripts, Git, and test commands. Give it a bounded maintenance task, then inspect the diff and the verification output. Check how it handles project instructions before giving it broader work.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenAI Codex: consider it for agent-assisted repository work
&lt;/h3&gt;

&lt;p&gt;Codex provides terminal, IDE, and cloud ways to work on coding tasks. The CLI can read and change files and run commands in the selected directory; the IDE extension brings an agent into supported editors. See the official &lt;a href="https://learn.chatgpt.com/docs/codex/cli" rel="noopener noreferrer"&gt;Codex CLI&lt;/a&gt; and &lt;a href="https://learn.chatgpt.com/docs/codex/ide" rel="noopener noreferrer"&gt;Codex IDE extension&lt;/a&gt; guides.&lt;/p&gt;

&lt;p&gt;Evaluate it on a task with a clear finish line: reproduce a bug, implement a focused fix, and run the relevant checks. Choose the execution surface that fits your repository and access requirements, then review the resulting changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Copilot: consider it for GitHub-based teamwork
&lt;/h3&gt;

&lt;p&gt;GitHub Copilot offers code suggestions, codebase questions, change review, and agent-assisted tasks. Its available capabilities depend on the client, plan, and organization settings. See &lt;a href="https://docs.github.com/en/copilot/get-started/about-github-copilot" rel="noopener noreferrer"&gt;GitHub's Copilot documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It deserves a place on the shortlist when your team already organizes development through GitHub. Test the path from an issue to a proposed change and review. Check whether the features you need are available to everyone who will use the workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  NEXUS AI: consider it for building and deploying an app
&lt;/h3&gt;

&lt;p&gt;NEXUS AI combines an AI app builder with previews and full-stack deployment tools. Its platform covers application services, databases, workers, storage, logs, and recovery workflows. See the &lt;a href="https://nexusai.run/product" rel="noopener noreferrer"&gt;NEXUS AI product overview&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Consider it when your goal includes a running application people can use. You can also bring code from another coding tool: the &lt;a href="https://nexusai.run/solutions/developers" rel="noopener noreferrer"&gt;developer workflow&lt;/a&gt; supports editing locally, using GitHub, and deploying through developer tools.&lt;/p&gt;

&lt;p&gt;Test the full path that matters to you: create a feature, inspect the preview, save real data, deploy, and read it back. Confirm which services your selected provider supports before choosing the deployment setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose a coding AI tool in seven steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Write down one task you actually need done
&lt;/h3&gt;

&lt;p&gt;Avoid starting with a large request such as “build my entire business.” Choose a task you can assess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explain how an existing login flow works.&lt;/li&gt;
&lt;li&gt;Fix a reproducible validation bug.&lt;/li&gt;
&lt;li&gt;Add a searchable list to an existing dashboard.&lt;/li&gt;
&lt;li&gt;Build a small app with one database-backed form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Describe what success looks like. For a form, that might mean valid input saves, invalid input produces a useful error, and the saved entry survives a page refresh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Pick two tools that fit that task
&lt;/h3&gt;

&lt;p&gt;For an existing repository, shortlist tools that work comfortably with its files and checks. For a new app, include a builder that lets you inspect the generated project and verify backend behavior.&lt;/p&gt;

&lt;p&gt;Keep the trial manageable. Two tools completing the same realistic task tell you more about your workflow than several unrelated demos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Give each tool the same context
&lt;/h3&gt;

&lt;p&gt;Start from the same repository version and provide the same requirements. Include your framework, relevant files, existing conventions, and verification command.&lt;/p&gt;

&lt;p&gt;Here is a reusable prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Goal: Add validation to the customer signup endpoint.
Context: This is an existing Python application. Follow its current style.
Required behavior: Reject missing or malformed email addresses with a
clear client error. Valid submissions must continue to work.
Scope: Keep changes focused on this behavior.
Verification: Reproduce the invalid-input case, implement the fix,
and run the relevant checks.
Report: Explain the changes, what you verified, and any remaining issue.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a new app, make the data behavior equally explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a small customer directory with a browser interface, a backend,
and persistent database storage. Let me add and search customers.
Validate required fields and show useful errors. Verify that a saved
customer remains after refreshing the page. Show me how to run and
deploy the app.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Review correctness and scope
&lt;/h3&gt;

&lt;p&gt;Read the changed files. Confirm that the behavior matches the request and that the tool did not introduce unnecessary dependencies or unrelated edits.&lt;/p&gt;

&lt;p&gt;Ask it to show the actual verification output. Run the important checks yourself when needed, especially where authentication, payments, or data access are involved. A test that merely repeats an implementation detail may miss the behavior you care about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Test the user journey
&lt;/h3&gt;

&lt;p&gt;Open the app and complete the task as a user would. Submit valid and invalid input, refresh the page, and check that saved data is still available.&lt;/p&gt;

&lt;p&gt;For a repository fix, revisit the original failure. For a generated app, confirm that the interface calls a working backend and that the backend reaches its intended database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Compare cost and review effort
&lt;/h3&gt;

&lt;p&gt;Subscription price is only part of the decision. Record usage limits, paid features you require, time spent correcting output, and how much review the change needed.&lt;/p&gt;

&lt;p&gt;For an application, also account for hosting, database, storage, and external API costs. Check the vendors' current plan pages before buying; access and limits can change.&lt;/p&gt;

&lt;p&gt;Use a simple worksheet:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;What to record&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Correctness&lt;/td&gt;
&lt;td&gt;Did the requested behavior pass the relevant checks?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;Did it follow your project's conventions?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;Were the changes focused and understandable?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review effort&lt;/td&gt;
&lt;td&gt;How long did you spend checking and correcting the result?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;What did this task consume, and what would repeated use cost?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery&lt;/td&gt;
&lt;td&gt;Could you run, deploy, and verify the result?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prefer a tool whose results you can understand and maintain. Generating more code is useful only when that code helps finish the task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Deploy and verify the result
&lt;/h3&gt;

&lt;p&gt;For an app project, include deployment in your evaluation. NEXUS AI supports deploying repository code from its CLI or through MCP, so you can continue using your chosen coding assistant. Its &lt;a href="https://nexusai.run/docs" rel="noopener noreferrer"&gt;deployment documentation&lt;/a&gt; covers the supported workflows.&lt;/p&gt;

&lt;p&gt;For example, the following CLI path deploys a repository with a PostgreSQL service on NEXUS AI managed infrastructure. It assumes your app is committed and pushed, has a supported runtime, and reads its database connection from &lt;code&gt;DATABASE_URL&lt;/code&gt;. Replace the repository URL first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; nexusapp-cli@latest
nexus auth login

nexus deploy &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--repo&lt;/span&gt; https://github.com/YOUR_USERNAME/YOUR_APP.git &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--branch&lt;/span&gt; main &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; coding-ai-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provider&lt;/span&gt; docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; DEVELOPMENT &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; postgres &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;

nexus deploy status coding-ai-demo
nexus deploy logs coding-ai-demo &lt;span class="nt"&gt;--type&lt;/span&gt; runtime &lt;span class="nt"&gt;--lines&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding a database service supplies infrastructure and connection settings. Your application still needs its database client, schema, and any migrations it requires. The &lt;a href="https://github.com/nexusrun/nexusai/wiki/CLI" rel="noopener noreferrer"&gt;CLI reference&lt;/a&gt; documents the commands and options.&lt;/p&gt;

&lt;p&gt;If you prefer MCP, connect your compatible assistant to NEXUS AI. For Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http nexus-ai https://mcp.nexusai.run/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete browser sign-in, then ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy my GitHub repository to NEXUS AI managed infrastructure with PostgreSQL in the DEVELOPMENT environment. Confirm account access and available providers first. Keep health checks enabled, follow the returned deployment ID, and inspect logs if it fails. Return the public URL and observed health. Keep credentials out of chat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The assistant's &lt;strong&gt;&lt;code&gt;nexusai_deploy_source&lt;/code&gt;&lt;/strong&gt; tool call would use arguments such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repoUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/YOUR_USERNAME/YOUR_APP.git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"repoBranch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"coding-ai-demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEVELOPMENT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"services"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"healthCheckEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This JSON describes an MCP tool call; it is not a shell command. Choose either the CLI or MCP creation path for this example. Then follow status with &lt;code&gt;nexusai_deploy_status&lt;/code&gt; and inspect health with &lt;code&gt;nexusai_deploy_health&lt;/code&gt;, passing the actual returned &lt;code&gt;deploymentId&lt;/code&gt; to each. See the &lt;a href="https://nexusai.run/mcp-deployment-platform" rel="noopener noreferrer"&gt;MCP connection guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After deployment, open the actual public URL, complete the main user action, and confirm that saved data can be read back. A running process is one checkpoint; a working user journey completes the evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions about coding AI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best AI for coding?
&lt;/h3&gt;

&lt;p&gt;Choose by workflow. Start with Cursor for editor-centered development, Claude Code for terminal work, Codex for agent-assisted repository tasks, GitHub Copilot for GitHub-based teamwork, or NEXUS AI for building and deploying an app. Compare two candidates on the same task before committing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can a beginner use coding AI?
&lt;/h3&gt;

&lt;p&gt;Yes. Start with a small feature and ask the tool to explain the files, decisions, and verification steps. Learn enough to understand how the app starts, where it stores data, and what happens when input is invalid. Ask for help when you cannot assess a change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is free coding AI enough?
&lt;/h3&gt;

&lt;p&gt;It may be enough for your workload. GitHub offers a Copilot Free option, while access to advanced capabilities depends on the plan and client. Review the &lt;a href="https://docs.github.com/en/copilot/get-started/about-github-copilot#get-access" rel="noopener noreferrer"&gt;current access options&lt;/a&gt;, then test whether the allowance fits your real tasks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can AI code an entire application?
&lt;/h3&gt;

&lt;p&gt;An AI app builder or coding agent can generate files across an application. You still need to verify the requirements, data access, error handling, dependencies, and deployment. Start with one complete user journey and expand after it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which AI is best for Python coding?
&lt;/h3&gt;

&lt;p&gt;Use the same task-based evaluation. Ask candidates to work with your actual Python framework and dependencies, reproduce a failure, make a focused change, and run the relevant checks. Include database and deployment behavior if those are part of your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use one coding AI and deploy somewhere else?
&lt;/h3&gt;

&lt;p&gt;Yes, when you can access the source and meet the destination's runtime requirements. NEXUS AI supports a workflow where you edit locally, push to GitHub, and deploy through developer tools. Check your app's build settings, secrets, and supporting services before moving it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put your shortlist to work
&lt;/h2&gt;

&lt;p&gt;Pick a small project you care about and define what “done” means. Give two tools the same task, review their changes, and compare the result you can run.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aicode</category>
      <category>nocode</category>
      <category>development</category>
    </item>
    <item>
      <title>NEXUS CLI App Builder</title>
      <dc:creator>Saif Ali</dc:creator>
      <pubDate>Tue, 20 Oct 2026 10:58:53 +0000</pubDate>
      <link>https://dev.to/sali_ac161a1b71406354896c/nexus-cli-app-builder-2d1n</link>
      <guid>https://dev.to/sali_ac161a1b71406354896c/nexus-cli-app-builder-2d1n</guid>
      <description>&lt;p&gt;The NEXUS CLI Builder is the terminal interface to the same AI App Builder used on the NEXUS AI website. You describe an app, the Builder creates files in a project, and later prompts revise that project. You can inspect and edit the files locally, check common syntax and import errors, open the browser preview, and explicitly deploy a snapshot. It is not a separate code generator: the terminal and browser use the same Builder session and checkpoints.&lt;/p&gt;

&lt;p&gt;This guide follows one app from first prompt to deployment, then covers the commands you need when you want more control.&lt;/p&gt;

&lt;p&gt;Who is it for, and when should you use it?&lt;br&gt;
Use the NEXUS CLI Builder if you prefer a terminal workflow, want to make a quick app without first creating a project in the website, or want to combine AI changes with edits in your own code editor. It is also useful when you need a repeatable sequence of commands for an existing Builder project. A terminal beginner can stay in nexus builder chat; developers can pull the files, edit them locally, and push the result.&lt;/p&gt;

&lt;p&gt;Use the browser Builder when you need to see and interact with the preview, use its visual editing tools, or inspect the app alongside the conversation. The NEXUS CLI prints a link to that page; it does not render a visual preview inside the terminal. If you already have an independent Git repository rather than a Builder snapshot, the regular source deployment workflow may be a better fit than starting a new Builder app.&lt;/p&gt;

&lt;p&gt;The NEXUS CLI Builder does not silently deploy each AI response. Generation, checks, preview, and deployment are separate steps so you can inspect the result first.&lt;/p&gt;

&lt;p&gt;Before you start&lt;br&gt;
Install Node.js 18 or later, then install and sign in to the CLI:&lt;/p&gt;

&lt;p&gt;npm install -g nexusapp-cli@latest&lt;br&gt;
nexus auth login&lt;br&gt;
nexus auth whoami&lt;br&gt;
nexus auth login opens a browser for sign-in. If you already use a NEXUS AI API token in a noninteractive environment, set NEXUSAI_TOKEN instead. Run nexus --help or nexus builder --help to confirm the commands supported by your installed version.&lt;/p&gt;

&lt;p&gt;Your account also needs an available AI provider. To see the provider IDs and whether they are allowed for your account, run:&lt;/p&gt;

&lt;p&gt;nexus builder providers&lt;br&gt;
In interactive chat, /provider and /model present numbered choices, so you do not need to memorize IDs. If no provider is available, add or enable one under AI Providers on NEXUS AI Dashbaord. AI requests and automated fixes may use your Builder quota.&lt;/p&gt;

&lt;p&gt;Example: build a small issue tracker&lt;br&gt;
Start a new interactive build from your normal terminal:&lt;/p&gt;

&lt;p&gt;nexus builder chat&lt;br&gt;
The builder&amp;gt; prompt is inside the chat. Type a description of the app, not a shell command. Your first app prompt creates a NEXUS AI project automatically; you do not need to create one in the UI. For example:&lt;/p&gt;

&lt;p&gt;builder&amp;gt; Build an issue tracker for a small team. Include an issue list, status and priority filters, an issue detail view, and a form to add an issue. Use realistic sample data. Keep the layout usable on a phone.&lt;br&gt;
The CLI prints New build: ... (), streams file and tool activity, and reports a saved checkpoint. Keep the project ID. It is the address you use to resume the app and run non-chat Builder commands. The app name and exact files depend on the generated result; this prompt is a concrete starting specification, not a guarantee that every requested behavior is production-ready.&lt;/p&gt;

&lt;p&gt;To revise the same app, keep talking at builder&amp;gt;:&lt;/p&gt;

&lt;p&gt;builder&amp;gt; Make the issue form validate the title and show an inline error instead of submitting an empty issue.&lt;br&gt;
builder&amp;gt; Add an empty-state message when the selected filters match no issues.&lt;br&gt;
builder&amp;gt; /files&lt;br&gt;
builder&amp;gt; /check&lt;br&gt;
Describe the change you want and its expected behavior. A focused follow-up is easier to inspect than asking for many unrelated features at once. /files lists the current snapshot. /check reports static diagnostics such as a file and line number; it does not run the app.&lt;/p&gt;

&lt;p&gt;Type /help at any point for the chat commands. Use Up and Down to revisit earlier prompts and commands in the current CLI session. The history is held in memory and is not saved across restarts.&lt;/p&gt;

&lt;p&gt;Choose an AI provider or model&lt;br&gt;
You can choose before the first prompt, or switch while working on an app:&lt;/p&gt;

&lt;p&gt;builder&amp;gt; /provider&lt;br&gt;
Choose an AI provider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude ...&lt;/li&gt;
&lt;li&gt;OpenAI ...
Provider number (Enter to cancel): 1
builder&amp;gt; /model
builder&amp;gt; /status
The numbered entries reflect providers and models actually available to your account; they may differ from this example. The selection affects subsequent AI messages. Switching providers resets the model to that provider's default until you select another model. /status shows the current project, provider, model, and file count.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here “AI provider” means the model service generating code. Later, builder deploy --provider ... means the infrastructure provider running the deployed app. They are different settings.&lt;/p&gt;

&lt;p&gt;Start another app or resume this one&lt;br&gt;
Inside chat, /new clears the current chat target; your next ordinary prompt creates another project. It does not delete the previous app. /exit leaves Builder chat. If you launched chat from the interactive nexus shell, you return to that shell; otherwise you return to your normal terminal.&lt;/p&gt;

&lt;p&gt;To return to the issue tracker after leaving, run this in your normal terminal, replacing the placeholder with the ID printed when you created it:&lt;/p&gt;

&lt;p&gt;nexus builder chat --project &lt;br&gt;
For a single prompt without entering chat, use:&lt;/p&gt;

&lt;p&gt;nexus builder new "Build a responsive issue tracker with a list, filters, and an add-issue form"&lt;br&gt;
That also creates a project automatically and prints its ID. For an existing project, a one-shot follow-up is:&lt;/p&gt;

&lt;p&gt;nexus builder ask --project  "Add an empty state to the filtered issue list"&lt;br&gt;
Inspect the files and fix a failed check&lt;br&gt;
Outside chat, the equivalent commands are:&lt;/p&gt;

&lt;p&gt;nexus builder files --project &lt;br&gt;
nexus builder check --project &lt;br&gt;
check examines supported text files for syntax errors and checks relative imports against the current Builder files. For example, an error might point to an import in src/App.tsx whose target file does not exist. If the reported issue is appropriate for AI repair, run:&lt;/p&gt;

&lt;p&gt;nexus builder fix --project &lt;br&gt;
nexus builder check --project &lt;br&gt;
fix passes the diagnostics to the AI, saves its changes, and checks again. It attempts repair up to twice by default; --attempts 3 changes that limit (maximum 5). Each AI attempt may consume Builder quota. Inspect the result after an automated fix: a passing static check means the checked syntax and relative imports passed, not that the app builds, behaves correctly, is secure, or has passing tests.&lt;/p&gt;

&lt;p&gt;If you prefer to guide the fix yourself, ask a targeted question instead:&lt;/p&gt;

&lt;p&gt;nexus builder ask --project  "The check reports a missing relative import in src/App.tsx. Fix that import without changing the issue form behavior."&lt;br&gt;
nexus builder check --project &lt;br&gt;
Use the path and diagnostic actually printed for your app. After that, open the browser preview and test the affected flow. For serious apps, also run the project's own build, tests, and security review after pulling the code locally.&lt;/p&gt;

&lt;p&gt;Edit the generated app in your own editor&lt;br&gt;
Builder files live in the project snapshot. nexus builder dev keeps a local folder in live two-way sync with it, so you can use VS Code, Cursor, or any editor as your IDE while still chatting with the AI:&lt;/p&gt;

&lt;p&gt;nexus builder dev --project  --dir ./issue-tracker --open&lt;br&gt;
The first run downloads the app into ./issue-tracker (an empty or new folder) and --open opens it in VS Code or Cursor. Then:&lt;/p&gt;

&lt;p&gt;Your edits sync up. Save a file and it becomes a new Builder checkpoint within a second or two, so the browser preview shows it.&lt;br&gt;
AI edits sync down. Type a prompt at the dev&amp;gt; prompt (or in the browser Builder) and the changed files appear in your folder. Before each AI turn, your pending edits are saved first so the AI works on your latest code.&lt;br&gt;
Edits on both sides are merged. If you and the AI changed different parts of the same file, both changes are kept. If you both changed the same lines, the file gets git-style markers:&lt;br&gt;
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; local&lt;/p&gt;

&lt;h1&gt;
  
  
  const title = "Recipe Box";
&lt;/h1&gt;

&lt;p&gt;const title = "Cookbook";&lt;/p&gt;

&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;nexus&lt;br&gt;
Edit the file to keep what you want and remove the markers. A file with markers is never saved to Builder, and it syncs as soon as the markers are gone. Your other edits keep syncing in the meantime.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the dev&amp;gt; prompt, /open opens the folder in your editor, /sync syncs immediately, /check and /fix work as in chat, /preview prints the browser preview link, and /exit stops syncing. Press Ctrl+C to cancel a running AI turn.&lt;/p&gt;

&lt;p&gt;To sync once instead of continuously (for example in a script), use:&lt;/p&gt;

&lt;p&gt;nexus builder sync --project  --dir ./issue-tracker&lt;br&gt;
It exits with code 1 when conflicts are waiting for you.&lt;/p&gt;

&lt;p&gt;What syncs: text files, including new and deleted files. .git, node_modules, build output, and .env files stay local and are never uploaded. The limit is 200 files and 2 MB of content; symlinks and binary or non-UTF-8 files are not supported. Keep secrets out of Builder files. If most of the folder's files disappear at once, sync stops instead of deleting them in Builder; restore them, or pass --allow-deletes if you really removed them.&lt;/p&gt;

&lt;p&gt;Manual pull and push&lt;br&gt;
If you prefer explicit steps, pull copies the snapshot to an empty folder and push saves your edits back:&lt;/p&gt;

&lt;p&gt;nexus builder pull --project  --out ./issue-tracker&lt;br&gt;
nexus builder push --project  --from ./issue-tracker&lt;br&gt;
push refuses to save if Builder changed after your pull. Run nexus builder sync on the same folder instead: it merges both sides. If you removed files that were in the pulled snapshot, push asks you to confirm with --delete:&lt;/p&gt;

&lt;p&gt;nexus builder push --project  --from ./issue-tracker --delete&lt;br&gt;
Use --delete only when you intentionally removed those files. For a single file, you can skip the pull/push cycle:&lt;/p&gt;

&lt;p&gt;nexus builder put src/components/IssueCard.tsx --from ./IssueCard.tsx --project &lt;br&gt;
nexus builder rm src/components/OldIssueCard.tsx --project &lt;br&gt;
The Builder path in put or rm must match the project's file structure. put adds or replaces that file and rm deletes it; neither automatically edits imports elsewhere. Run check after either operation. File changes made through the CLI are shared with the browser Builder.&lt;/p&gt;

&lt;p&gt;What if someone edited the app after I pulled it?&lt;br&gt;
push compares the checkpoint recorded by pull with the current project checkpoint. If another Builder chat, browser edit, or file operation changed the app, push rejects your stale copy rather than overwriting those changes. Run nexus builder sync --project  --dir  on the same folder: it brings in the other changes, keeps yours, and marks any lines you both changed for you to resolve. Do not simply add --delete: that flag confirms missing files, not a stale checkpoint.&lt;/p&gt;

&lt;p&gt;Review checkpoints and open the preview&lt;br&gt;
Each successful AI response or file edit produces a Builder checkpoint. To list them:&lt;/p&gt;

&lt;p&gt;nexus builder versions --project &lt;br&gt;
The output includes message IDs. If you need an earlier file state, choose the intended ID and restore it:&lt;/p&gt;

&lt;p&gt;nexus builder revert  --project &lt;br&gt;
Revert saves the restored files as a new checkpoint. It does not erase the intervening history. Review the listed version before reverting because it changes the current snapshot.&lt;/p&gt;

&lt;p&gt;To inspect behavior, get the browser Builder URL:&lt;/p&gt;

&lt;p&gt;nexus builder open --project &lt;br&gt;
Inside chat, /preview prints the same kind of link. Open it in a browser to run the preview, click through the issue list and form, and make sure the result matches your requirements. open only prints the URL; it does not launch a preview inside the terminal.&lt;/p&gt;

&lt;p&gt;Deploy the reviewed snapshot&lt;br&gt;
Deployment is explicit. After reviewing the files, running check, and testing the browser preview, deploy the current Builder snapshot:&lt;/p&gt;

&lt;p&gt;nexus builder deploy --project  --name issue-tracker --provider docker&lt;br&gt;
nexus deploy list --project &lt;br&gt;
The command creates a deployment from the current files and prints a deployment ID. docker here is a deployment provider, not an AI model. Other providers may be available for your account; for example, --provider gcp_cloud_run selects Google Cloud Run. You can supply --region  and --framework  when needed. The CLI uses the project's framework hint, or nextjs if none is set, unless you override it. Check the generated app before deployment if that default does not fit.&lt;/p&gt;

&lt;p&gt;builder deploy does not automatically run builder check, your package's test suite, or a production build first. It submits a new deployment; it is not an in-place edit of an existing deployment. Deployment uses your normal infrastructure quotas and may incur charges. Check deployment status and logs through the normal deployment commands or dashboard if the build fails.&lt;/p&gt;

&lt;p&gt;Quick command map&lt;br&gt;
Goal    In builder&amp;gt; chat    From the normal terminal&lt;br&gt;
Start a new app First ordinary prompt, or /new then a prompt    nexus builder chat or nexus builder new "..."&lt;br&gt;
Continue an app Ordinary prompt nexus builder chat --project  or nexus builder ask --project  "..."&lt;br&gt;
Get help    /help   nexus builder --help&lt;br&gt;
Select AI   /provider, /model, /status  nexus builder providers; start supports provider/model IDs&lt;br&gt;
Inspect files   /files  nexus builder files --project &lt;br&gt;
Check and repair    /check, /fix    nexus builder check and nexus builder fix with --project &lt;br&gt;
Open preview    /preview    nexus builder open --project &lt;br&gt;
Edit locally    Leave chat, then use terminal commands  pull, edit, push; or put/rm for one file&lt;br&gt;
Review or restore   Leave chat, then use terminal commands  versions and revert with --project &lt;br&gt;
Deploy  Leave chat, then use terminal command   nexus builder deploy --project  --name  --provider &lt;br&gt;
Common sticking points&lt;br&gt;
“Where is my project?” The first Builder prompt creates it automatically. Copy the printed project ID. /status shows the ID during chat; use chat --project  to resume later.&lt;br&gt;
“My provider is missing.” Run nexus builder providers. In chat, /provider lists supported providers allowed for your account. Configure one under website Settings → AI Providers if the list is empty.&lt;br&gt;
“check passed, but preview or deployment failed.” Static checks do not execute the app or replace its build/tests. Inspect the browser preview and deployment logs, then fix the actual runtime or build failure.&lt;br&gt;
“push says the Builder changed since pull.” Preserve your edits, pull into a new empty directory, merge, and push that newer copy.&lt;br&gt;
“I typed /help and my shell rejected it.” Slash commands work only at the builder&amp;gt; prompt. From your normal terminal, use nexus builder --help.&lt;br&gt;
“I pressed Up but yesterday's command is gone.” Chat history is only kept for the current CLI process. The Builder project's files and checkpoints persist; use the project ID to resume them.&lt;br&gt;
For broader platform guidance, see the documentation. For exact Builder flags in your installed CLI, run nexus builder --help or nexus builder  --help&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
