Doing a little experiment to reduce code smell and in that effort I'm trying to write a typed function that would allow me to define my endpoints in one location within my application. The thought is, if I ever change or add routes, I can define them in one area which is then dispersed across the application.
The example below is where I'm at currently. While it does works, it's still a bit messy and not exactly DRY (repeating enum value as the path in the getApiAddress() function.)
Any thoughts how I can create a function that allows me to add n[] unique id:string params while ago getting rid of the {target: '', path: ''} and just have one value that can accept those id string values?
const apiAddress =
process.env === 'PRODUCTION'
? 'https://some-address.com'
: 'http://localhost:5000';
export enum targetsEnum {
transcribe = '/services/transcribe',
transcribeId = '/services/transcribe/:id',
}
export type ApiTargets = targetsEnum.transcribe | targetsEnum.transcribeId;
export const getApiAddress = (target: ApiTargets, id?: string[]): string => {
const endpoints = [
{ target: targetsEnum.transcribe, path: '/services/transcribe' },
{
target: targetsEnum.transcribeId,
path: `/services/transcribe/${id[0]}`,
},
];
const matchingEndpoint = endpoints.find(
endpoint => endpoint.target === target
);
return `${apiAddress}${matchingEndpoint.path}`;
};
continued to work on this some more.
With the approach below, you can defined you endpoints in one location (targetsEnum) and pass that in anywhere in your application. Then, the second argument is an array of strings. They are injected into the enum's value using injectPathVariables().
example:
const endpoint = getApiAddress(ApiEndpointsEnum.youtubeId,['${youtubeId}']);
const apiAddress =
process.env === 'PRODUCTION'
? 'https://some-endpoint.com'
: 'http://localhost:5000';
export enum testApiEndpointsEnum {
test = '/test',
testId = '/test/:id',
testIdtestId = '/test/:id/test/:id',
}
export enum ApiEndpointsEnum {
youtube = '/services/youtube',
youtubeId = '/services/youtube/:id',
transcribe = '/services/transcribe',
transcribeId = '/services/transcribe/:id',
}
export type ApiTargets =
| testApiEndpointsEnum.test
| testApiEndpointsEnum.testId
| testApiEndpointsEnum.testIdtestId
// * ^ the TWO above this comment are for testing.
| ApiEndpointsEnum.youtube
| ApiEndpointsEnum.youtubeId
| ApiEndpointsEnum.transcribe
| ApiEndpointsEnum.transcribeId
const injectPathVariables = (
target: ApiTargets[1],
ids: string[]
): string => {
let newStr = target;
ids.forEach(i => {
newStr = newStr.replace(':id', i);
});
return newStr;
};
const getApiAddress = (target: ApiTargets, ids?: string[]): string => {
// * no ids provided + confirmation target doesn't need ids.
if (ids === undefined && target.includes(':id') === false) {
return `${apiAddress}${target}`;
}
// * no ids provided, but target does need ids.
if (
(ids === undefined || ids.length === 0) &&
target.includes(':id') === true
) {
return `${apiAddress}`;
}
// * ids have been provided.
const injectedOutput = injectPathVariables(target, ids);
if (injectedOutput.includes(':id')) {
// * but not enough ids were provided for complete injection.
return apiAddress;
}
// * sufficient ids have been provided
return `${apiAddress}${injectedOutput}`;
};