2. JavaScript Setup, Providers, and Model Fundamentals
Set up a safe Node.js project, understand model APIs and messages, compare providers, and create a reusable model boundary.
Create a Safe Node.js AI Project
Use a supported Node.js LTS release, a package manager, environment variables, and a server-side module layout. Keep provider-specific code behind a small adapter so your application logic does not depend on one SDK everywhere. Separate development, test, and production configuration. Add linting, type checking if using TypeScript, tests, and a .env.example containing placeholders rather than secrets.
For JavaScript projects, JSDoc can improve editor support; for larger systems, TypeScript is especially useful for tool arguments, structured outputs, and message-state models.
Do not commit .env files containing secrets. Use a secret manager or protected deployment environment for production.
src/
app.js
config/env.js
ai/provider.js
ai/prompts.js
ai/schemas.js
tools/
retrieval/
evals/
tests/
.env.example
package.json- Use server-side configuration for secrets.
- Separate prompts, tools, retrieval, and evaluation from route handlers.
- Test domain logic without depending on live provider calls.
Why place provider calls behind an adapter?
Messages, Roles, Tokens, and Context Windows
A model request typically contains instructions, conversation messages, optional tool definitions, generation settings, and output constraints. Provider APIs differ in terminology and semantics, so treat a provider's current API reference as authoritative. Tokenization is not the same as word count: punctuation, code, and multilingual text may use different token counts.
The context window limits how much input and output can be processed in one request. Long history increases cost and can reduce relevance. Use concise conversation summaries, retrieve only relevant documents, cap output length, and avoid sending secrets or unrelated personal data.
const request = {
instructions: "Answer using only the supplied policy context.",
input: [
{ role: "user", content: "Can I return an opened item?" }
],
metadata: { feature: "returns-help" }
};- Token counts are not equivalent to word counts.
- Context limits apply to input and output.
- Use provider documentation for exact request semantics.
Why should an application avoid sending the entire conversation history on every request?
Provider Abstraction and Model Selection
A provider abstraction should standardize only what your product actually needs: text generation, streaming, structured output, tool calls, embeddings, or image input. Do not flatten meaningful differences between providers into a misleading lowest-common-denominator interface. Record model identifier, version or snapshot when available, configuration, latency, token usage, and evaluation results.
Model choice is a product decision. Compare task quality, latency, availability, context length, tool reliability, multimodal capability, data handling terms, and cost. Keep a fallback policy for temporary outages, but do not silently downgrade to a model that fails your quality or privacy requirements.
export function createTextGenerator(client, model) {
return {
async generate({ instructions, input }) {
const result = await client.responses.create({
model,
instructions,
input
});
return {
text: result.output_text,
responseId: result.id
};
}
};
}- Hide provider-specific response parsing behind an adapter.
- Evaluate models on representative tasks.
- Fallbacks must preserve safety, privacy, and minimum quality requirements.
What should be recorded when comparing model choices in production?