npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

openapi-domainify

v0.1.1

Published

Split OpenAPI specs into domain-based TypeScript services. Zero config required.

Readme

openapi-domainify

Split your OpenAPI spec into domain-based TypeScript services.

Most OpenAPI generators dump everything into a single file or force you to restructure your spec. This tool reads your existing spec and generates modular, domain-separated service classes based on URL path prefixes.

// Your config
domains: [
  { name: 'auth', prefix: '/auth/' },
  { name: 'billing', prefix: '/billing/' },
  { name: 'users', prefix: '/users/' },
]

// Generated usage
api.auth.login({ email, password })
api.billing.getInvoice(invoiceId)
api.users.updateProfile(userId, data)

Why This Exists

Every OpenAPI TypeScript generator on npm does one of these:

| Tool | Approach | Problem | |------|----------|---------| | openapi-typescript | Types only | No service layer | | openapi-fetch | Single client | One massive file | | @hey-api/openapi-ts | Tag-based grouping | Requires spec changes | | orval | React Query focused | Framework lock-in | | openapi-generator | Mustache templates | 15MB dependency, Java heritage |

openapi-domainify uses path prefixes from your existing URLs. No spec modifications. No framework coupling. Just TypeScript.


Installation

npm install -D openapi-domainify
# or
pnpm add -D openapi-domainify
# or
bun add -D openapi-domainify

Quick Start

1. Create config file

npx openapi-domainify init

This creates domainify.config.ts:

import { defineConfig } from 'openapi-domainify'

export default defineConfig({
  // OpenAPI spec source (URL or local file path)
  input: 'https://api.example.com/openapi.json',
  
  // Where to write generated code
  output: './src/api',

  // Domain splitting rules (order matters - first match wins)
  domains: [
    { name: 'auth', prefix: '/auth/', className: 'Auth' },
    { name: 'users', prefix: '/users/', className: 'Users' },
  ],

  // Catch-all for unmatched paths
  fallback: { name: 'api', prefix: '/', className: 'Api' },
})

2. Generate

npx openapi-domainify generate

Output structure:

src/api/
├── index.ts              # API client (regenerated each time)
├── http.ts               # HTTP client template (safe to edit)
├── generated/            # Auto-generated (DO NOT EDIT)
│   ├── openapi.json
│   ├── openapi.ts
│   ├── auth/
│   │   ├── service.ts
│   │   └── types.ts
│   └── users/
│       ├── service.ts
│       └── types.ts
└── overrides/            # Your customizations (safe to edit)
    ├── auth/
    │   └── service.ts    # Extend/override AuthService
    └── users/
        └── service.ts    # Extend/override UsersService

Overrides

When the generated code has issues or you need custom methods, create an override instead of editing generated files:

// overrides/users/service.ts
import { UsersService as GeneratedUsersService } from '../generated/users/service'
import type { HttpClient } from '../../http'

export class UsersService extends GeneratedUsersService {
  constructor(http: HttpClient) {
    super(http)
  }

  // Override a broken method
  async getUser(id: string | number) {
    // Custom implementation
    return this.http.get(`/users/${id}`)
  }

  // Add a method not in the OpenAPI spec
  async customEndpoint() {
    return this.http.post('/users/custom')
  }
}

The generator:

  • Never overwrites existing override files
  • Always regenerates generated/ and index.ts
  • Auto-detects if you've customized an override and uses it in index.ts

Generated Code

Service Classes

// generated/users/service.ts
import type { HttpClient } from '../../http'
import type * as T from './types'

export class UsersService {
  constructor(private http: HttpClient) {}

  // GET /users
  users(params?: T.GetUsersQuery): Promise<T.GetUsersResponse> {
    return this.http.get('/users', { params })
  }

  // GET /users/{id}
  getUser(id: string | number): Promise<T.GetUsersByIdResponse> {
    return this.http.get(`/users/${id}`)
  }

  // POST /users
  createUser(data: T.PostUsersRequest): Promise<T.PostUsersResponse> {
    return this.http.post('/users', data)
  }
}

Type Extraction

Types are extracted directly from your OpenAPI spec using the TypeScript Compiler API:

// generated/users/types.ts

// Request types
export type PostUsersRequest = {
  email: string
  name: string
  role?: 'admin' | 'user'
}

// Response types
export type GetUsersResponse = {
  data: Array<{
    id: number
    email: string
    name: string
  }>
}

HTTP Client

The generator creates an http.ts template with a FetchHttpClient implementation. Edit it or replace with your preferred HTTP library:

// http.ts (generated once, safe to edit)
export interface HttpClient {
  get<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>
  post<T>(url: string, body?: Record<string, unknown>): Promise<T>
  put<T>(url: string, body?: Record<string, unknown>): Promise<T>
  patch<T>(url: string, body?: Record<string, unknown>): Promise<T>
  delete<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>
}

Configuration Reference

import { defineConfig } from 'openapi-domainify'

export default defineConfig({
  // Required: OpenAPI spec source
  input: 'https://api.example.com/openapi.json',

  // Required: Output directory
  output: './src/api',

  // Required: Domain splitting rules
  domains: [
    { 
      name: 'auth',           // Directory name
      prefix: '/auth/',       // URL prefix to match
      className: 'Auth'       // Generated class name suffix
    },
  ],

  // Optional: Catch-all for unmatched paths
  fallback: { name: 'api', prefix: '/', className: 'Api' },

  // Optional: Strip this prefix from all paths
  stripPrefix: '/api/v1',

  // Optional: Where to save the downloaded spec
  specOutput: './src/api/generated/openapi.json',

  // Optional: Custom HTTP client import path
  httpClientImport: '../../http',

  // Optional: Override files directory (relative to output)
  overridesDir: '../overrides',

  // Optional: Generate index.ts (default: true)
  generateIndex: true,
})

Method Naming Convention

Generated method names follow RESTful conventions:

| HTTP Method | Path | Generated Method | |-------------|------|------------------| | GET | /users | users() | | GET | /users/{id} | getUser(id) | | POST | /users | createUser(data) | | PUT | /users/{id} | updateUser(id, data) | | PATCH | /users/{id} | patchUser(id, data) | | DELETE | /users/{id} | deleteUser(id) | | GET | /users/{id}/orders | getUserOrders(id) |


How It Works

  1. Fetch spec — Downloads OpenAPI JSON from URL or reads local file
  2. Generate types — Runs openapi-typescript to create base TypeScript types
  3. Parse with TS Compiler API — Loads generated types into TypeScript's type checker
  4. Extract endpoints — Iterates over paths interface, extracting methods, params, bodies
  5. Group by domain — Matches each path against configured prefixes
  6. Generate services — Creates service classes with fully typed methods
  7. Generate scaffolding — Creates override templates (only if they don't exist)
  8. Wire up index — Generates index.ts that imports from overrides or generated

Comparison

| Feature | openapi-domainify | openapi-fetch | @hey-api/openapi-ts | orval | |---------|-------------------|---------------|---------------------|-------| | Domain splitting | ✅ Path prefix | ❌ | ⚠️ Tags only | ❌ | | No spec changes | ✅ | ✅ | ❌ | ✅ | | Override system | ✅ | ❌ | ❌ | ❌ | | Framework agnostic | ✅ | ✅ | ✅ | ❌ React Query | | Service classes | ✅ | ❌ | ✅ | ✅ |


License

MIT