{"slug": "node-js-ai-integration-add-ai-to-a-live-app-no-rewrite", "title": "Node.js AI Integration: Add AI to a Live App, No Rewrite", "summary": "A developer from Geminate Solutions explains how to integrate AI features into existing Node.js applications without rewriting the stack, arguing that calling hosted models is an HTTP request well-suited to Node and that adding a separate Python service introduces unnecessary operational overhead. The guide covers practical concerns such as streaming, timeouts, retries, cost caps, and data privacy, and notes that official Node.js SDKs from OpenAI, Anthropic, and Google offer parity with Python counterparts.", "body_md": "Every Node.js AI integration guide starts a fresh project and ends at the first completion. This one starts from a live app with users on it, and covers the eight things that break after the demo.\n\n**Your product runs on Node. The team wants a summarise button, a smart search, a draft-this-reply feature. The first tutorial you opened told you to stand up a Python service. The prototype endpoint you built instead takes thirty seconds to answer and the invoice for it doubled between the first month and the second.** None of that means you picked the wrong stack. It means the tutorials stop where the real work starts.\n\nHere is the short version. Calling a model is an HTTP request, and Node.js is the runtime built for waiting on HTTP requests. You add the feature inside the app you already have, in the route you already have, with the provider's own Node SDK or a thin layer over it. The part that separates a demo from a feature is not the call. It is streaming so the user sees the first word in under a second, a timeout that is not the SDK's ten-minute default, a retry policy that does not double your bill during an outage, a cap so one user cannot spend the month's budget, and a rule about which customer data is allowed into a prompt.\n\nThis page is for a live app, with users on it, adding its first or second AI feature. If you are starting a fresh product and the AI is the product, some of this still applies and a lot of it is premature. And if you are building an agent, something that loops and decides its own next step, our [Node.js AI agent guide](https://geminatesolutions.com/blog/ai-agents-nodejs) is the right page and this one hands off to it at the end.\n\nNo. And the advice that you do deserves a fair hearing, because it is not wrong so much as aimed at someone else.\n\nPython earned its place in machine learning because training and fine-tuning models, running numerical code, and working with the research libraries all happen there. If your team is going to train anything, Python is the right room to be in. Adding a feature that calls a hosted model is a different job. The model lives on the provider's servers. Your code sends text over HTTPS and reads text back, sometimes as a stream. OpenAI, Anthropic and Google each publish an official Node.js SDK for exactly that, and the Node SDKs carry the same streaming, retry, timeout and tool-calling features as their Python counterparts. Anthropic's TypeScript SDK documentation lists Node.js 20 LTS and later, Deno, Bun, Cloudflare Workers and the Vercel Edge Runtime as supported, and the OpenAI Node README documents the same retry and timeout controls this page quotes further down.\n\nWhat a second service costs you is the part the tutorial never prices in. A Python service next to a Node app is a second deployment, a second set of secrets, a second place logs go, a second on-call surface, and a network hop between your request handler and the model call that now has its own timeout to get wrong. For a team that already runs Node in production, that is a real weight to carry for a feature that is, underneath, one HTTP call.\n\nThe exception is honest and narrow. If the feature needs a model that only runs locally, or a numerical pipeline that only exists in Python, you will end up with a Python process somewhere. Put it behind a queue, treat it as a worker rather than a request-time dependency, and keep the user-facing route in Node. Our guide to [on-premise LLM deployment](https://geminatesolutions.com/blog/on-premise-llm-deployment) covers when that is worth doing. For a hosted model, which is what nearly every first feature uses, stay in Node.\n\nFor one feature on one provider, the provider's SDK. For several features or a provider you expect to swap, a thin agnostic layer. Raw fetch only for a tool with a cap and no customers.\n\nThere are three ways to make the call, and picking one is mostly a question of how many features you will have in a year and how much of the plumbing you want to own.\n\nTwo things the table cannot say. First, the provider SDKs are more alike than different, so choosing one is not the lock-in it looks like. Both the OpenAI and Anthropic Node SDKs stream with an async iterable, retry the same classes of error twice by default, time out after ten minutes by default, and expose typed error classes. Swapping later is a day of work in a well-factored route, not a rewrite. Second, the agnostic layer is worth it when it is replacing plumbing you would otherwise write three times. The AI SDK's core documentation describes generateText for one-shot calls and streamText for real-time ones, exposes the result as a textStream that is both a ReadableStream and an async iterable, and ships helpers to pipe that stream into a Node response. If you are writing that glue for a second feature, stop and adopt it.\n\nWhichever path you pick, put the call behind one function of your own with one signature: a feature name, a user id, the input, and an options object. Every section below adds something to that function. If the model call is scattered across six routes, none of them can be added.\n\nA senior engineer reads your actual code and sends back what is genuinely broken, what is fine, and what can wait. Free, within 48 hours, and no obligation follows it.\n\nIf your app has few users, takes no payments and stores no personal data, you probably do not need us yet. We will say so.\n\nSet the headers, flush them, write each chunk as it arrives, honour backpressure, and abort the upstream call when the client goes away. The event loop is never blocked, because the work is waiting, not computing.\n\nA model reply can take ten to thirty seconds to finish, and a route that awaits the whole thing before writing anything gives the user a blank screen for all of it. Streaming fixes that without any change to your architecture. Both provider SDKs return an async iterable when you pass stream: true, and Anthropic's documentation notes that this form uses less memory than the helper that accumulates a final message for you. The shape of the route, with the details that matter, looks like this.\n\napp.post('/api/summarise', async function (req, res) { const stream = await client.messages.create( { model: MODEL, max_tokens: 600, stream: true, messages: [{ role: 'user', content: buildPrompt(req.body) }] }, { timeout: 30 * 1000, maxRetries: 0 } ); req.on('close', function () { stream.controller.abort(); }); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Cache-Control', 'no-store'); res.flushHeaders(); try { for await (const event of stream) { const text = textDeltaOf(event); if (text && !res.write(text)) { await new Promise(function (resolve) { res.once('drain', resolve); }); } } res.end(); } catch (err) { if (!res.headersSent) return res.status(502).end(); res.end(); } });Five lines in there are the ones the tutorials leave out. The per-request timeout of thirty seconds replaces a default of ten minutes. maxRetries is zero on a streamed, user-facing call, because a retry after the first chunk has been written cannot be made invisible to the user, so it is better to fail fast and let them click again. The close handler aborts the upstream request when the browser tab is shut, which Anthropic's SDK documents as stream.controller.abort() and which stops you paying for tokens nobody will read. The drain wait is backpressure: res.write returns false when the socket buffer is full, and a route that ignores that on a slow mobile connection grows memory until the process falls over. And the catch branch has to handle two different worlds, before headers are sent and after, because once the stream has started you cannot change the status code.\n\nIf you sit behind a reverse proxy, check that it does not buffer the response, or the stream arrives all at once at the end and you have gained nothing. And if the route already has a body parser with a size limit, keep it. A prompt built from an unbounded request body is the cheapest way for a user to run up your bill, which the cost section comes back to.\n\nMore teams than the agencies ranking for this query would like to admit.\n\nIf the feature is an internal tool, used by your own staff, calling the model a few hundred times a day at most, and never touching customer data, do this instead. Use the provider SDK, set a thirty-second timeout, set a monthly spending limit in the provider's dashboard, and ship it. You do not need streaming, a cost model, an eval suite or a vendor abstraction. You need the button to work by Friday.\n\nIf you are pre-launch and the AI feature is the product, most of this page is also premature. Get the prompt right, get users, find out whether the feature is the reason they stay. The cost and rate limit sections become real the day a second customer signs up, and not before.\n\nCome back when one of five things happens. A customer sees the feature. The request handler starts holding connections open for longer than your load balancer allows. The invoice moves in a direction you did not predict. Someone asks what happens to the data you send the provider. Or the feature grows a second step, where the model's first answer decides what the code does next. Those are the triggers for the rest of this page, in roughly that order.\n\nChange the defaults, because they were chosen for a script that can wait, not for a request handler that cannot.\n\nBoth SDKs document the same starting point. The OpenAI Node README says requests time out after 10 minutes by default, and that certain errors are automatically retried 2 times by default with a short exponential backoff: connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit and 5xx errors. Anthropic's TypeScript SDK documentation says the same, 10 minutes and 2 retries on the same classes of error, and adds that when you set a large max_tokens without streaming, the default timeout is calculated dynamically and can reach 60 minutes. Both expose a timeout option and a maxRetries option, per client and per request.\n\nDo the arithmetic for a request handler. A slow upstream call, a ten-minute timeout, and two retries is thirty minutes during which one HTTP request is held open, one worker is occupied, and, on the retries, tokens are being charged twice or three times for an answer the user gave up on twenty-nine minutes earlier. Multiply by every user who clicked the button during a provider incident. That is how an AI feature takes down a Node app that was fine before it, and it happens without any error in your own code.\n\nThe settings that hold up. For a user-facing route, a timeout of twenty to forty seconds, streamed, with retries off, because a retry on a stream is visible. For a background job, a queue worker, a nightly classification run, keep the two retries and let the timeout be generous, because nobody is waiting and a retry is invisible. Anthropic's SDK also throws an error if a non-streaming request is expected to run past roughly ten minutes, and its documentation says to use streaming for long requests, because some networks drop idle connections. Take the hint. Anything that might run long streams, even if the consumer is a job and not a person.\n\nTwo details, one line each. On timeout, Anthropic's SDK throws an APIConnectionTimeoutError, and the timed-out request is retried by the SDK's own default policy, so if you leave maxRetries at 2 a timeout is not one wait but three. And both SDKs attach a request id to every response, on Anthropic as a _request_id property read from the request-id header. Log it on every failure. It is what the provider will ask you for, and without it a support conversation about a bad answer goes nowhere.\n\nThe SDK retries it twice with backoff, and then it is your problem. The fix is to read the headers, queue the work you can, and shed the work you cannot.\n\nOpenAI's rate limits guide measures limits in several units at once: RPM, requests per minute, RPD, requests per day, TPM, tokens per minute, TPD, tokens per day, and IPM for images. You can run out of any of them independently, and a feature that makes few requests with long prompts hits the token limit long before the request limit. Anthropic's SDK surfaces a 429 as a RateLimitError, one of the typed subclasses of APIError, with the status and headers attached, so you can catch that class specifically instead of string-matching a message.\n\nThe headers are the useful part. OpenAI documents x-ratelimit-limit-requests, x-ratelimit-remaining-requests and x-ratelimit-reset-requests, the same three for tokens, and project-scoped token versions. A route that reads the remaining count on every response knows it is about to be throttled before it is. Its guide says this about the backoff itself: wait at least as long as the reset header says, and add a small random delay so multiple clients do not retry at the same time. That jitter is the difference between a limit clearing and a thundering herd that keeps it pinned.\n\nWhat to do with the work. User-facing calls should fail fast with a message the user can act on, not sit in a retry loop. Background work should go on a queue with a concurrency limit set below the provider's RPM, so the queue absorbs the burst and the provider never sees it. OpenAI's guide names two more levers that both reduce load and cost: set max_tokens as close to your expected response size as possible, and, when you have token headroom but no request headroom, batch several tasks into one request. For work that does not need an answer today, both providers offer a batch API. Anthropic's SDK exposes it under messages.batches with a custom_id per request and results you iterate once processing has ended. Nightly classification of a backlog belongs there, not in your request handlers.\n\nMeasure per request, cap per user and per feature, keep answers short, put the stable part of the prompt first, and send easy tasks to a smaller model. All of it in week one, because none of it is fun to retrofit.\n\nThe bill doubles in month two for an ordinary reason: month one was the team testing, month two was customers. Nothing was wrong. Nobody was measuring. Both SDKs return a usage object on every response, Anthropic's documentation shows it as input_tokens and output_tokens, and the single most valuable line of code in an integration is the one that writes those two numbers to your database with a user id, a feature name and a timestamp. Once that exists, every question about cost has an answer, and a cap becomes a query.\n\nCaps go in the route, not in the dashboard. The provider's monthly spending limit is a fuse for the whole company, and when it blows every feature dies at once, usually at the end of the month when it hurts most. A per-user daily cap and a per-feature monthly cap, checked in your own function before the call is made, degrade one user or one feature and leave the rest running. Pair them with a request body limit, because the cheapest attack on an AI feature is a user pasting a book into the summarise box.\n\nThen the levers that reduce the number itself. max_tokens first, because you pay for every output token and a summary that needs two hundred does not need a limit of four thousand. Prompt caching second. OpenAI's guide explains that the provider preserves the model's intermediate state for a reusable prefix, that the minimum prompt length for caching is 1,024 tokens on current models, that cached input tokens are charged at a tenth of the standard rate, that a cache persists for thirty minutes after its last use, and that the usage response reports what was reused under input_tokens_details.cached_tokens. The practical rule is the same on every provider that offers it: system instructions, examples and reference material go first and never change between calls, the user's input goes last. A prompt that puts the changing part first gets no cache hits, and the bill shows it. Model routing third. Classification, extraction and short rewrites do not need the largest model, and a route that picks the model by feature is one line once the call sits behind your own function. Our [Claude API guide for SaaS](https://geminatesolutions.com/blog/claude-api-saas) goes deeper on the Claude-specific version of all three, and our [OpenAI versus Claude](https://geminatesolutions.com/blog/openai-vs-claude) comparison is the place to start if the choice is still open.\n\nWhat it costs to leave it. Not the invoice. The meeting where somebody who was excited about the feature asks whether it should be switched off, and nobody in the room can say which users or which prompts are responsible.\n\nDecide what a prompt may contain, enforce it in the one function every call goes through, and treat the model's output as untrusted input on the way back.\n\nA live app has customer data in it, and the fastest way to build a summarise feature is to hand the model the whole record. That is also the fastest way to send names, emails, addresses and account notes to a third party, into its logs, and into your own. The fix is a rule, written down, about which fields a prompt may contain per feature, and a function that builds prompts from an allow-list of fields rather than from the whole object. Where a task needs a person's name to read naturally, replace it with a token before the call and put it back after. It is unglamorous and it works.\n\nYour own logs are the second leak, and the quieter one. Anthropic's SDK documentation warns that at the debug log level all HTTP requests and responses are logged including headers and bodies, that some authentication headers are redacted, and that sensitive data in request and response bodies may still be visible. Keep debug logging off in production, and when you log a request for cost or support, log the token counts, the request id, the feature and the user id, never the prompt body. If you need prompts for debugging, store them separately with a short retention and access control, not in the application log that ships to three vendors.\n\nThe way back matters as much as the way in. Model output can contain instructions, and a feature that pastes an answer into HTML, runs it as a query, or lets it choose which record to update has handed a stranger a way into your app. Escape it like user input, because it is user input by way of a model. Our guide to [prompt injection in AI agents](https://geminatesolutions.com/blog/ai-agent-prompt-injection) covers the attack in detail, and the single-call version is the same lesson at smaller scale. And if the feature answers questions over your own documents, the retrieval step has its own rules about who may see which document, which our [RAG pipeline guide](https://geminatesolutions.com/blog/rag-pipeline-guide) lays out.\n\nOne contractual point, because engineering cannot fix it. Read the provider's data retention terms for the API tier you are on, and if you serve customers under a data processing agreement, check that the model call is covered by it. Where it is not, the answer is often a regional or enterprise endpoint, not a different architecture.\n\nBuild a small set of real inputs with answers a person has approved, run every prompt change against it, and never ship on the strength of the three examples that looked good in the console.\n\nA model feature fails differently from ordinary code. It does not throw. It returns a confident, well-formed, wrong answer, and it does so on inputs you did not try. The only defence is to try more inputs than you would for a normal function, and to try the same ones every time anything changes. Take thirty to fifty real examples from your data, with the customer data handled as the previous section says, write down what a good answer looks like for each, and run the prompt against all of them in a test you can execute from the command line. When you change the prompt, the model, or the temperature, run it again and read the diff.\n\nGrade with code where you can and with a person where you must. Extraction and classification have exact answers, so the test compares fields. Summaries and drafts do not, so the test records the output and a person reads the ones that changed. Some teams use a second model to grade the first, which is useful for scale and unreliable as the only judge. Keep a person in the loop for anything a customer will read.\n\nShip with a way off. Put the feature behind a flag, roll it out to a share of users, and watch three numbers: how often the user accepts the output without editing, how often they close it, and the token cost per accepted output. Those three tell you more than any offline score, and they are the numbers the person paying for the feature will ask about in month three.\n\nWhen one call plus one tool call cannot finish the job, and the model has to look at what it just learned to decide what happens next. Most features never get there, and the ones that do need a different set of guardrails.\n\nSummarising, classifying, extracting, drafting, translating and answering a question over your documents are single calls, possibly with one tool call for lookup. They are the majority of what a product team asks for, and everything on this page is enough to run them well. An agent is the thing that loops: it calls a tool, reads the result, decides on the next tool, and keeps going until it judges the task done. The moment your feature needs that, it needs budgets on steps and tokens, a wall-clock limit, idempotent tools so a retry cannot send two emails, and schema validation of every argument the model produces, because none of those problems exist in a single call and all of them exist in a loop.\n\nBoth provider SDKs will take you to the first step of that road without a framework. Anthropic's SDK ships a tool runner that takes Zod or JSON schemas, passes the model's chosen inputs into the right tool, and hands the result back to the model, and the OpenAI SDK has equivalent helpers. Use them for a single tool call inside a feature. When the loop becomes the feature, read our [Node.js AI agent guide](https://geminatesolutions.com/blog/ai-agents-nodejs), which compares the frameworks, builds the core loop, and covers the coordination and error-handling that a loop demands. This page ends where that one begins.\n\nWhen the feature is already live, customers are using it, and the questions in the sections above do not have answers anyone on the team can point to in the code.\n\nIf the disqualifier section described you, use the SDK, set a timeout, set a spending limit, and ship. If you have one feature and a team that reads READMEs, this page is a checklist and you can work through it in a sprint. The teams that hand it over are the ones with a prototype in production that surprised them, a second and third feature queued behind it, and a quote on the table for a Python service or a platform migration that would solve none of the eight problems above and add a ninth.\n\nThat is the work we do at Geminate Solutions. We add AI features to products that already have users, inside the stack they already run, and we do not sell rebuilds or second stacks. We have shipped 50+ products, run an EdTech platform at 250,000+ daily users and an exam system absorbing 10 million requests a minute, and hold Top Rated Plus on Upwork at 4.9. You own the code from the first commit. Our [AI builder to production service](https://geminatesolutions.com/services/ai-builder-to-production) lays out how an engagement runs.\n\nThe first step is a written review of your integration as it stands. Which path you are on and whether it fits. What the timeout and retry settings are on every model call. Whether a slow client can grow your memory. What happens on a 429. Where the usage numbers go and what caps exist. What customer data reaches the provider and your logs. And whether the feature has a test set or three good examples. We send it back within 48 hours and it stays yours whether or not we ever talk again.\n\n**Do I need Python to add AI to a Node.js application?**\n\nNo. Calling a hosted model is an HTTP request, and OpenAI, Anthropic and Google all ship official Node.js SDKs with the same streaming, retry and timeout features as their Python ones. Python becomes relevant only if you are training or fine-tuning models yourself or running a numerical pipeline, which is not what adding a feature to a live product involves.\n\n**Should I use the OpenAI or Anthropic SDK directly, or the Vercel AI SDK?**\n\nFor one feature on one provider, the provider's own SDK is the smallest dependency and its README documents the defaults you need to change. Use a provider-agnostic layer such as the AI SDK when you already know you will switch or mix providers, or when several features share the same streaming and tool-calling plumbing. Raw fetch is fine for an internal tool with a call cap.\n\n**Why does my AI endpoint hang for minutes when the provider is slow?**\n\nBecause both the OpenAI and Anthropic Node SDKs time out after 10 minutes by default and retry twice, so one slow upstream call can hold a request open far longer than your load balancer or your user will wait. Set the timeout per request to a few tens of seconds, stream the response so the first token arrives early, and abort the upstream call when the client disconnects.\n\n**How do I stop the AI bill from growing month over month?**\n\nLog usage per request with a user id and a feature name, set a cap per user and per feature and enforce it in the route, keep max_tokens close to the size of the answer you need, put the stable part of every prompt first so provider prompt caching applies, and route simple tasks to a smaller model. Do these in the first week, because they are hard to add after a bill has become a habit.\n\n**When does an AI integration need to become an agent?**\n\nWhen the task cannot be finished in one model call plus one tool call, and the model has to decide what to do next based on what it just learned. Summarising, classifying, drafting, extracting and answering questions over your data are single calls. Most features never cross that line, and a feature that does needs budgets, step limits and idempotent tools before it ships.\n\nCEO and co-founder of Geminate Solutions, a software and product development partner. He has led teams shipping custom web apps, mobile apps, SaaS platforms, and AI products that serve over 250,000 daily active users.\n\nSend us the repository or a description of the feature and the route it lives in. A senior engineer reads the model calls, the timeout and retry settings, the streaming path, the 429 handling, where usage is recorded and what caps exist, and what customer data reaches the provider. Then writes back with what will hold under real traffic and what will not. No pitch, no commitment.\n\nDrop your app s URL and work email. We reply within 48 hours.\n\nSend us your website link on WhatsApp. Within 24 hours we tell you exactly what is costing you customers and what we would fix first. No obligation and no sales script.\n\n4.9 rated · 50+ products shipped · 250K+ daily users served\n\nMost teams that reach us have a working product and a growing list of things that scare them. We read the code first and tell you what actually needs fixing, including the parts that do not. Rebuilding from scratch is rarely the honest answer.\n\nNot indexed, or indexed and not ranking? Why every SEO scanner reports an empty shell while Google sees the page, why every route shows the same title, which fix each symptom actually needs, and who should do nothing at all.\n\nA customer paid and the app still says free, a subscription cancelled in Stripe and the user kept access, checkout works with 4242 and not a real card, the webhook returns 401. What Lovable Payments decides for you, who can ignore all of it, and the order to fix it in without a rebuild.\n\nGoogle login bounces to the preview URL, sign-ups stop confirming, a customer wants an admin, an investor asks about two-factor. Why each one happens in a Lovable app, the Supabase limits behind them, who can ignore all of it, and the order to fix it in without logging anyone out.\n\nWhat the Samsara and Geotab APIs let you build without touching the hardware. Their documented rate limits and pagination side by side, why one pushes events and the other only polls, who should use Fleetio or Zapier instead, and what to build first.\n\nLovable gives you a real backend, until it does not. The documented ceilings of the edge function model, the five signals your app has outgrown it, the three ways to add a custom backend without a rebuild, and the honest case for doing nothing yet.\n\nEvery comparison ranks these tools on the demo. None say what you are left holding. What Lovable, Bolt.new, v0, Replit Agent, Base44 and Firebase Studio each generate, where no-code AI app builders differ from code-generating ones, and the four failures that show up the week real users arrive.\n\nYour system prompt telling the model to ignore injected instructions does not hold, and a classifier will not save you either. Where the trust boundary actually belongs, why closing the exit beats guarding the entrance, and the six published patterns that trade capability for a guarantee.\n\nEvery comparison is a table of checkmarks. None say whether the decision deserves the three weeks you are about to give it. What each platform actually is, where lock-in really accumulates, what breaks first once the fleet is real, and when the right answer is to skip both.\n\nThe tutorials say the wrap takes five minutes. They are right, and that is not the hard part. What ports out of a React codebase, what quietly does not, the App Store rule nobody mentions until after the work is done, and how to tell which of the three answers your product actually needs.\n\nEvery page tells you ChatGPT is not HIPAA compliant. None tell you what compliant looks like in your stack. Which vendors sign a BAA, what that signature reaches, why zero data retention decides whether it means anything, and the leak sitting in your own observability tooling.\n\nGoogle deletes Firebase Studio workspaces on 22 March 2027. Your data survives and your app keeps running. The record of why it was built the way it was does not. What the export contains, what it leaves behind, and what breaks first under real users.\n\nYou pressed export and got a repository. Then you read the environment file and found it still points at Base44. What the export actually contains, what stays behind, what the Wiz security disclosure means, and the four-stage sequence for moving off the platform.\n\nThe demo landed and the budget followed. Nine months later it is still a pilot. Why a pilot is a complete answer to a different question, what has to exist around the model before real users arrive, how to tell a retrieval problem from a generation one, and the order the work has to happen in.\n\nYou pressed Export Code and found out it only goes one way. Somebody has already said the word rebuild. It is real Flutter and it compiles, so this is almost never a rewrite. What actually lands in your repository, why the helper library is now yours to maintain, where custom code stops, and the order a takeover has to happen in.\n\nFifteen customers on fifteen deployments, or one bespoke build you now want to sell as a product. Somebody has told you this needs a rewrite. It usually does not. Why the tenant column is the easy afternoon, why enabling row-level security does not apply it to the role that owns the table, and what breaks that is not the database at all.\n\nIt ran fine in Expo Go and now it will not ship. The JavaScript is not the problem and almost none of it needs rewriting. Why you no longer eject, why your environment variables were baked in at build time, what the New Architecture migration really costs, and who should not do any of this.\n\nThe data cannot leave our infrastructure is four requirements in one sentence, and only one of them needs your own hardware. What the cloud already guarantees by default, why strict retention quietly switches off specific frontier models, and the three cases where on-premise is genuinely the only answer.\n\nThree walls, in a fixed order. Connection exhaustion first, query shape second, real architectural limits a distant third. Why upgrading compute is usually the wrong first move, why Prisma times out when nothing else does, and the three cases where leaving Supabase is genuinely the answer.\n\n*Originally published on [Geminate Solutions](https://geminatesolutions.com/blog/nodejs-ai-integration).*", "url": "https://wpnews.pro/news/node-js-ai-integration-add-ai-to-a-live-app-no-rewrite", "canonical_source": "https://dev.to/geminate_solutions_9b6035/nodejs-ai-integration-add-ai-to-a-live-app-no-rewrite-56ko", "published_at": "2026-09-07 12:15:01+00:00", "updated_at": "2026-09-07 12:27:56.846635+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-products"], "entities": ["Geminate Solutions", "Node.js", "OpenAI", "Anthropic", "Google"], "alternates": {"html": "https://wpnews.pro/news/node-js-ai-integration-add-ai-to-a-live-app-no-rewrite", "markdown": "https://wpnews.pro/news/node-js-ai-integration-add-ai-to-a-live-app-no-rewrite.md", "text": "https://wpnews.pro/news/node-js-ai-integration-add-ai-to-a-live-app-no-rewrite.txt", "jsonld": "https://wpnews.pro/news/node-js-ai-integration-add-ai-to-a-live-app-no-rewrite.jsonld"}}