Skip to main content
The WorkOS Node SDK is written in TypeScript and provides comprehensive type definitions out of the box.

Overview

The SDK includes:
  • Full TypeScript source code
  • Exported type definitions for all interfaces
  • Strict type checking enabled
  • IDE autocomplete support
  • Discriminated unions for type safety

Requirements

  • TypeScript 5.9.3 or higher (recommended)
  • Node.js 20.15.0 or higher
  • ESM or CommonJS module support

Installation

Types are included automatically when you install the SDK:
npm install @workos-inc/node
No separate @types package is needed.

Configuration

The SDK is built with strict type checking. We recommend similar settings:
tsconfig.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "noImplicitAny": true,
    "noFallthroughCasesInSwitch": true,
    "lib": ["es2022"]
  }
}

ESM vs CommonJS

The SDK supports both module systems with proper type definitions.
{
  "type": "module"
}

Type Definitions

Core Types

The SDK exports all core types for use in your application.
import { WorkOS } from '@workos-inc/node';
import type {
  User,
  Organization,
  OrganizationMembership,
  Connection,
  DirectoryUser,
  DirectoryGroup,
} from '@workos-inc/node';

const workos = new WorkOS('sk_...');

// Type-safe function
async function getUser(userId: string): Promise<User> {
  return await workos.userManagement.getUser({ userId });
}

Interface Examples

User Interface

interface User {
  object: 'user';
  id: string;
  email: string;
  emailVerified: boolean;
  profilePictureUrl: string | null;
  firstName: string | null;
  lastName: string | null;
  lastSignInAt: string | null;
  locale: string | null;
  createdAt: string;
  updatedAt: string;
  externalId: string | null;
  metadata: Record<string, string>;
}

Organization Interface

interface Organization {
  object: 'organization';
  id: string;
  name: string;
  createdAt: string;
  updatedAt: string;
  metadata: Record<string, any>;
}

Directory User Interface

interface DirectoryUser {
  id: string;
  idpId: string;
  directoryId: string;
  organizationId: string | null;
  firstName: string | null;
  lastName: string | null;
  state: 'active' | 'inactive';
  customAttributes?: {
    emails?: Array<{
      type?: string;
      value?: string;
      primary?: boolean;
    }>;
    username?: string;
    jobTitle?: string;
    [key: string]: any;
  };
  rawAttributes: Record<string, any>;
  createdAt: string;
  updatedAt: string;
}

Options Types

All SDK methods accept typed options objects.
import type {
  GetUserOptions,
  ListUsersOptions,
  CreateUserOptions,
  UpdateUserOptions,
} from '@workos-inc/node';

// Create user with type safety
const options: CreateUserOptions = {
  email: '[email protected]',
  firstName: 'Jane',
  lastName: 'Doe',
  emailVerified: true,
};

const user = await workos.userManagement.createUser(options);

List Response Types

Paginated list responses use a generic List type.
import type { List, User } from '@workos-inc/node';

const response: List<User> = await workos.userManagement.listUsers({
  limit: 10,
});

console.log(response.data); // User[]
console.log(response.listMetadata); // { before: string, after: string }

Type-Safe Client Modes

Discriminated Client Types

Use createWorkOS() for compile-time type safety based on client mode.
import { createWorkOS } from '@workos-inc/node';

// Public client - type system restricts available methods
const publicClient = createWorkOS({ 
  clientId: 'client_123' 
});

// Only PKCE methods are available
publicClient.userManagement.getAuthorizationUrlWithPKCE({ ... }); // ✅
publicClient.userManagement.listUsers(); // ❌ Type error

// Confidential client - full API access
const serverClient = createWorkOS({ 
  apiKey: 'sk_...', 
  clientId: 'client_123' 
});

// All methods available
serverClient.userManagement.getAuthorizationUrlWithPKCE({ ... }); // ✅
serverClient.userManagement.listUsers(); // ✅

Type Guards

The SDK uses TypeScript discriminated unions for type safety.
import type { SSOAuthorizationURLOptions } from '@workos-inc/node';

// Type system enforces mutually exclusive options
const options: SSOAuthorizationURLOptions = {
  connection: 'conn_123', // Only ONE of: connection, organization, or provider
  redirectUri: 'https://example.com/callback',
};

// This would be a TypeScript error:
// const invalid = {
//   connection: 'conn_123',
//   organization: 'org_456', // ❌ Cannot specify both
//   redirectUri: 'https://example.com/callback',
// };

Generic Types

Webhook Events

Webhook events use discriminated unions based on event type.
import type { WebhookEvent } from '@workos-inc/node';

function handleWebhook(event: WebhookEvent) {
  // TypeScript narrows the type based on event.event
  switch (event.event) {
    case 'user.created':
      // event.data is typed as User
      console.log(event.data.email);
      break;
    
    case 'connection.activated':
      // event.data is typed as Connection
      console.log(event.data.connectionType);
      break;
    
    case 'dsync.deleted':
      // event.data is typed as Directory
      console.log(event.data.id);
      break;
  }
}

Custom Metadata

Metadata fields use generic Record types.
import type { Organization } from '@workos-inc/node';

const org: Organization = await workos.organizations.createOrganization({
  name: 'Acme Corp',
});

// metadata is Record<string, any>
const customField: string = org.metadata.customField;
For stricter typing, define your own metadata interface:
interface CustomMetadata {
  tier: 'free' | 'pro' | 'enterprise';
  accountManager?: string;
  renewalDate?: string;
}

interface OrganizationWithMetadata extends Omit<Organization, 'metadata'> {
  metadata: CustomMetadata;
}

const org = await workos.organizations.createOrganization({
  name: 'Acme Corp',
  metadata: {
    tier: 'enterprise',
    accountManager: 'Jane Doe',
  },
}) as OrganizationWithMetadata;

console.log(org.metadata.tier); // Type-safe: 'free' | 'pro' | 'enterprise'

IDE Support

Autocomplete

The SDK provides full autocomplete in modern IDEs:
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_...');

// IDE shows all available modules
workos.userManagement. // Autocomplete shows: getUser, listUsers, createUser, etc.

JSDoc Documentation

All methods include JSDoc comments for inline documentation:
/**
 * Get a user by ID.
 *
 * @param options - Options for getting a user
 * @param options.userId - The ID of the user to get
 * @returns The user object
 *
 * @example
 * const user = await workos.userManagement.getUser({ 
 *   userId: 'user_123' 
 * });
 */
getUser(options: GetUserOptions): Promise<User>

Type Imports

Use import type for type-only imports (recommended for better tree-shaking):
import { WorkOS } from '@workos-inc/node';
import type { 
  User, 
  Organization, 
  CreateUserOptions 
} from '@workos-inc/node';

Runtime Types

Cloudflare Workers

Use the /worker export for edge runtime types:
import { WorkOS } from '@workos-inc/node/worker';
import type { User } from '@workos-inc/node/worker';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const workos = new WorkOS(env.WORKOS_API_KEY);
    const user: User = await workos.userManagement.getUser({ 
      userId: 'user_123' 
    });
    return Response.json(user);
  },
};

Deno

Deno works with standard TypeScript imports:
import { WorkOS } from 'npm:@workos-inc/node';
import type { User } from 'npm:@workos-inc/node';

const workos = new WorkOS(Deno.env.get('WORKOS_API_KEY'));
const user: User = await workos.userManagement.getUser({ 
  userId: 'user_123' 
});

Type Checking

Compiler Settings

The SDK is built with these strict compiler options:
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,
    "alwaysStrict": true
  }
}

Type Checking Commands

Check types without emitting files:
# Check types only
npx tsc --noEmit

# Watch mode
npx tsc --noEmit --watch

Common Patterns

Async/Await with Types

import { WorkOS } from '@workos-inc/node';
import type { User, Organization } from '@workos-inc/node';

const workos = new WorkOS('sk_...');

async function getUserWithOrg(userId: string): Promise<{
  user: User;
  organization: Organization | null;
}> {
  const user = await workos.userManagement.getUser({ userId });
  
  const memberships = await workos.userManagement.listOrganizationMemberships({
    userId,
    limit: 1,
  });
  
  const organizationId = memberships.data[0]?.organizationId;
  
  const organization = organizationId
    ? await workos.organizations.getOrganization({ organizationId })
    : null;
  
  return { user, organization };
}

Error Handling with Types

import { WorkOS } from '@workos-inc/node';
import type { User } from '@workos-inc/node';

const workos = new WorkOS('sk_...');

async function safeGetUser(userId: string): Promise<User | null> {
  try {
    return await workos.userManagement.getUser({ userId });
  } catch (error) {
    if (error instanceof Error && error.message.includes('not found')) {
      return null;
    }
    throw error;
  }
}

Type Narrowing

import type { DirectoryUser } from '@workos-inc/node';

function getPrimaryEmail(user: DirectoryUser): string | null {
  const emails = user.customAttributes?.emails;
  
  if (!emails || !Array.isArray(emails)) {
    return null;
  }
  
  const primary = emails.find(e => e.primary);
  return primary?.value ?? emails[0]?.value ?? null;
}

Best Practices

Use Type Imports

Prefer import type for type-only imports to improve tree-shaking and build performance.

Enable Strict Mode

Use strict: true in tsconfig.json for better type safety and error detection.

Define Custom Types

Extend SDK types with your own interfaces for domain-specific metadata and fields.

Type Guards

Use type guards and discriminated unions for safer runtime type checking.

Resources

Need Help?

Having TypeScript issues?

Build docs developers (and LLMs) love