On this page
An LLM API looks like another HTTP dependency until you try to ship it. Response lengths vary, generated text is not inherently trustworthy, and the same request can consume different amounts of time and money. A useful integration starts with those differences rather than hiding them behind a chat box.
Imagine adding a documentation assistant to a web application. The browser submits a question, your server calls a provider, and the interface displays an answer. That simple path needs explicit boundaries for credentials, input size, output handling, and failure. Establish these before experimenting with elaborate prompts.
Put the provider behind your server
Keep API credentials on the server, in the deployment platform's secret store. A key bundled into browser JavaScript is public even if its variable name suggests otherwise. The browser should call an application endpoint that authenticates users and decides which requests they may make.
Your endpoint is also the place for usage controls. Apply per-user limits, bound request size, and decide whether anonymous visitors can access the feature. Do not let clients supply arbitrary model identifiers, privileged instructions, or provider URLs. Accept a narrow application payload and construct the provider request yourself.
Check the provider's data handling settings before sending customer content. Retention, regional processing, and logging policies are separate from whether an API offers encryption in transit. Redact unnecessary identifiers and avoid putting secrets into prompts, traces, or bug reports.
The OpenAI API documentation illustrates provider-specific request formats and authentication. Treat those details as adapter concerns rather than spreading them throughout your components. A replaceable adapter makes provider changes and integration tests substantially easier.
Understand what goes into a request
A typical text request includes a model choice, instructions, user content, and an output limit. Some APIs organise content as messages; others use different envelopes or typed content blocks. Follow the exact endpoint contract rather than assuming that every provider accepts an interchangeable chat transcript.
Use instruction boundaries to distinguish application rules from user material. Instructions might require concise explanations grounded in supplied documentation. User content supplies the question. Retrieved passages supply evidence. These distinctions help organisation, but they are not an access-control mechanism: malicious content can still attempt to redirect the model.
For the documentation assistant, start with a small, measurable task. Ask for an answer plus an explicit statement when the supplied context is insufficient. Keep the first implementation text-only so that image handling, tool execution, and document uploads do not obscure basic request failures.
A useful application-level request contract is deliberately modest:
type AssistantRequest = {
question: string;
pageId?: string;
};
// Server responsibilities:
// authenticate, validate, retrieve permitted context,
// call provider, normalise result, record usageUse runtime validation as well as TypeScript. A caller can bypass your form entirely, so enforce character limits and permitted page identifiers on the server. Return a validation error before making a billable upstream request.
Budget tokens rather than guessing from characters
Tokens are the units used to represent model input and output. They are not consistently equivalent to words or characters. Language, punctuation, code, and formatting all influence tokenisation. A rough character estimate can support interface hints, but use the provider's tokenizer or usage reporting when accuracy matters.
The context window limits the material the model can consider in one request, with exact accounting varying by API and model. Instructions, conversation history, retrieved documents, and generated output all compete for capacity. Some models also expose additional reasoning-related usage; read the current accounting rules rather than assuming visible text explains the whole bill.
Set an output budget matched to the task. A short navigation answer should not have the same allowance as a migration guide. A generous maximum does not guarantee useful detail, while a tiny limit can truncate an otherwise valid response halfway through a sentence or object.
Track actual input and output usage per feature. Long conversation histories often become the hidden cost centre because the server resends them on subsequent turns. Summarise or trim intentionally, preserving decisions and user constraints rather than blindly deleting the oldest message. The structured output guide explains why machine-readable answers also need a completion budget.
Treat responses as typed outcomes
Do not assume a successful HTTP status contains a finished answer. Inspect the response shape and its termination information. Providers may return text blocks, refusals, tool requests, partial output, or other content types. A truncated result needs different treatment from a complete answer.
Create a normalised result for your application: completed text, refusal, incomplete output, or failure. Include a request identifier for support investigations and usage data for monitoring. Keep raw provider details out of the browser unless the interface actually needs them.
Generated content remains untrusted output. Render plain text by default. If you support Markdown, use a renderer configured to reject dangerous HTML and validate link destinations. Never execute generated code or database queries merely because the model formatted them convincingly. OWASP's generative AI security guidance provides a broader threat model for these integrations.
When another program consumes the answer, validate its structure and business rules before applying changes. A syntactically valid response can still invent an account identifier, unsupported option, or permission that does not exist.
Measure latency and plan failures
Record server validation time, provider duration, and total user-visible duration separately. Use the API latency tester to establish a baseline for a safe public test endpoint; do not paste production credentials into a browser-based utility. Your server traces should measure authenticated provider calls under realistic loads.
Watch tail latency, not just averages. Test short questions, long context, burst traffic, and cancelled requests. Streaming can reduce the wait before visible text without reducing total generation time; the SSE implementation guide covers the additional delivery work.
Set explicit deadlines and propagate cancellation where supported. Retry transient failures with bounded exponential backoff and jitter, respecting retry guidance. Avoid retrying malformed requests. A timeout can leave an upstream operation running, so repeated attempts may create duplicate costs even when the browser received nothing.
Before launch, maintain a small evaluation set containing ordinary questions, missing information, malicious instructions, and oversized inputs. Compare answers after prompt or provider changes. The MDN Fetch documentation covers browser request handling, but your application must still define useful empty, loading, error, and retry states. Reliability is a product behaviour, not simply a successful fetch.
Key takeaways
- Keep provider credentials and request construction on your server.
- Budget input, history, and output using measured token usage.
- Handle refusals, truncation, and failures as distinct outcomes.
- Validate generated content before rendering or taking action.
- Measure realistic latency and test failure paths before launch.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor