Sneruz
Sneruz
NNuxt
Created by Sneruz on 1/15/2025 in #❓・help
Reducing Redundant API Handlers While Preserving TypeScript Types
I have multiple handlers that are nearly identical except for the types they use and the endpoint paths. Here's a simplified example:
// types.ts
export interface RequestTypeA { /* fields */ }
export interface ResponseTypeA { /* fields */ }

export interface RequestTypeB { /* fields */ }
export interface ResponseTypeB { /* fields */ }

export interface RequestTypeC { /* fields */ }
export interface ResponseTypeC { /* fields */ }

// server/api/A.get.ts
import { RequestTypeA, ResponseTypeA } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeA;
const data = await $fetch<ResponseTypeA>('/api/A', { params });
return data;
});

// server/api/B.get.ts
import { RequestTypeB, ResponseTypeB } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeB;
const data = await $fetch<ResponseTypeB>('/api/B', { params });
return data;
});

// server/api/C.get.ts
import { RequestTypeC, ResponseTypeC } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeC;
const data = await $fetch<ResponseTypeC>('/api/C', { params });
return data;
});
// types.ts
export interface RequestTypeA { /* fields */ }
export interface ResponseTypeA { /* fields */ }

export interface RequestTypeB { /* fields */ }
export interface ResponseTypeB { /* fields */ }

export interface RequestTypeC { /* fields */ }
export interface ResponseTypeC { /* fields */ }

// server/api/A.get.ts
import { RequestTypeA, ResponseTypeA } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeA;
const data = await $fetch<ResponseTypeA>('/api/A', { params });
return data;
});

// server/api/B.get.ts
import { RequestTypeB, ResponseTypeB } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeB;
const data = await $fetch<ResponseTypeB>('/api/B', { params });
return data;
});

// server/api/C.get.ts
import { RequestTypeC, ResponseTypeC } from '@/types';

export default defineEventHandler(async (event) => {
const params = getQuery(event) as RequestTypeC;
const data = await $fetch<ResponseTypeC>('/api/C', { params });
return data;
});
As you can see, the event handlers for A, B, and C are very similar, with only the types and endpoint paths being different. What I've Tried: I attempted to reduce redundancy by using dynamic route parameters:
// server/api/[endpoint].get.ts
export default defineEventHandler(async (event) => {
const endpoint = getRouteParam(event, 'endpoint')
const params = getQuery(event);
const data = await $fetch(`/api/${endpoint}`, { params });
return data;
});
// server/api/[endpoint].get.ts
export default defineEventHandler(async (event) => {
const endpoint = getRouteParam(event, 'endpoint')
const params = getQuery(event);
const data = await $fetch(`/api/${endpoint}`, { params });
return data;
});
While this simplifies the code, I'm losing the specific TypeScript types for each request and response. My Question: Is there a way to eliminate the redundancy in my API handlers while maintaining specific TypeScript types for each endpoint?
5 replies