TypeScript AI API Basics

A practical guide to connecting TypeScript applications to modern artificial intelligence models using type-safe API client patterns.

TL;DR

  1. Store secret keys in process.env to prevent credential exposure.
  2. Define a unified LLMMessage interface for universal provider compatibility.
  3. Use AbortSignal parameters to cancel long-running model network requests.

Client Initialization

    OpenAI client setup

    Initialize official OpenAI client with environment keys.

    import OpenAI from 'openai';
    const openai = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
    Anthropic client setup

    Create Anthropic instance targeting Claude foundation models.

    import Anthropic from '@anthropic-ai/sdk';
    const anthropic = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY,
    });
    Google GenAI setup

    Instantiate Google GenAI client targeting Gemini endpoints.

    import { GoogleGenAI } from '@google/genai';
    const ai = new GoogleGenAI({
      apiKey: process.env.GEMINI_API_KEY,
    });

Universal Message Modeling

    type MessageRole

    Define supported participant roles within prompt conversations.

    type MessageRole =
      | 'system'
      | 'user'
      | 'assistant'
      | 'tool';
    interface ChatMessage

    Represent standardized dialogue item across providers.

    interface ChatMessage {
      role: MessageRole;
      content: string;
      name?: string;
    }
    interface RequestConfig

    Group core hyperparameter options for prompt execution.

    interface RequestConfig {
      model: string;
      temperature?: number;
      maxTokens?: number;
      signal?: AbortSignal;
    }

Executing Completions

    openai.chat.completions

    Execute chat completion against OpenAI chat models.

    const res = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [{ role: 'user', content: 'Hello!' }],
      temperature: 0.7,
    });
    const reply = res.choices[0].message.content;
    anthropic.messages

    Send message requests to Anthropic Claude models.

    const msg = await anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 1024,
      messages: [{ role: 'user', content: 'Hello!' }],
    });
    const text = msg.content[0].text;
    ai.models.generateContent

    Invoke Gemini models using Google standard SDK.

    const geminiRes = await ai.models.generateContent({
      model: 'gemini-2.0-flash',
      contents: 'Summarize TypeScript in 10 words.',
    });
    const summary = geminiRes.text;

Request Lifecycle Control

    AbortController signal

    Enforce strict network timeouts on slow completions.

    const c = new AbortController();
    const t = setTimeout(() => c.abort(), 5000);
    try {
      await openai.chat.completions.create(
        { model: 'gpt-4o', messages: [] },
        { signal: c.signal }
      );
    } finally {
      clearTimeout(t);
    }
    Token usage auditing

    Capture prompt and completion token counts from response.

    type Usage = { prompt: number; total: number };
    const usage: Usage = {
      prompt: res.usage?.prompt_tokens ?? 0,
      total: res.usage?.total_tokens ?? 0,
    };
    Provider adapter pattern

    Expose universal interface to decouple model dependencies.

    interface AIProvider {
      complete(
        prompt: string, opts?: Opts
      ): Promise<string>;
    }

Tips

  1. Wrap provider SDK clients in custom TypeScript adapter classes implementing AIProvider to swap underlying models without breaking calling application code.
  2. Specify explicit max_tokens limits on all model requests to avoid unexpected generation costs and latency spikes.

Warnings

  1. Never expose secret API keys like process.env.OPENAI_API_KEY inside client-side browser code or public single-page application script tags.
  2. Avoid hardcoding raw model string names like 'gpt-4o' directly into business logic without strict TypeScript union types.

In Practice

FAQ