Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/core/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as fs from 'fs';
import { ServerConfig } from '../types/config';
import { buildTypeMap } from '../utils/typeMapping';
import pluralize from 'pluralize';
import { toPascalCase } from '../utils/pluralize';

interface OpenAPISchema {
type: string;
Expand Down Expand Up @@ -147,8 +148,15 @@ export function generateOpenAPISpec(config: ServerConfig): Record<string, unknow
properties,
};

// Generate path for array endpoint
// Generate path for array endpoint — skip if the user explicitly defined a
// plural interface for this path (e.g. skip auto-plural for `User` when `Users` exists).
const arrayPath = interfaceNameToPath(interfaceName);
const pluralSegment = arrayPath.slice(1); // e.g. "users"
const explicitPluralTypeName = toPascalCase(pluralSegment); // e.g. "Users"
const hasExplicitPlural =
explicitPluralTypeName !== interfaceName && typeMap.has(explicitPluralTypeName);

if (!hasExplicitPlural) {
paths[arrayPath] = {
get: {
summary: `Get all ${pluralize(interfaceName)}`,
Expand Down Expand Up @@ -184,6 +192,7 @@ export function generateOpenAPISpec(config: ServerConfig): Record<string, unknow
},
},
};
}

// Generate path for single item endpoint
const singularPath = `/${interfaceName.toLowerCase()}`;
Expand Down
18 changes: 16 additions & 2 deletions src/utils/typeMapping.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { RouteTypeMapping, InterfaceMetadata } from '../types/config';
import { parseUrlToType } from './pluralize';
import { parseUrlToType, toPascalCase, extractLastSegment } from './pluralize';

/**
* Recursively scans a directory to find all .ts files
Expand Down Expand Up @@ -137,9 +137,23 @@ export function findTypeForUrl(
url: string,
directory: string
): RouteTypeMapping | null {
const { typeName, isArray } = parseUrlToType(url);
const typeMap = buildTypeMap(directory);
const lastSegment = extractLastSegment(url);

// If the user explicitly defined an interface matching this URL segment (e.g. `Users`
// for `/users`), prefer it over the auto-plural route derived from a singular interface.
const directTypeName = toPascalCase(lastSegment);
const directFilePath = typeMap.get(directTypeName);
if (directFilePath) {
return {
typeName: directTypeName,
isArray: false,
filePath: directFilePath,
};
}

// Fall back to singularization (e.g. `/users` → `User` with isArray: true)
const { typeName, isArray } = parseUrlToType(url);
const filePath = typeMap.get(typeName);

if (!filePath) {
Expand Down