Aqui está um exemplo de uma resposta padrão da API para um caminho feliz e um caso de erro.
// successful response ✅{ status: 'ok', responseCode: 200, data: {...}}// error response ❌{ responseCode: 500, errorMessage: "Something went wrong 😅";}
Em vez de escrever uma interface para cada resposta e adicionar essas chaves, você pode simplesmente usar o Generics para criar algo como isto:
interface ApiResponse<T>{ errorMessage?: string; responseCode?: string; data?: T;}
interface UserData { name: string; email: string;}const response: ApiResponse<UserData> = {}
Vinculando genéricos com funções
Vamos supor que temos uma função que usamos para fazer uma solicitação de API ao nosso back-end.
const getRequestTo = (endpoint: string) => { return fetch(process.env.BE_HOST + endpoint).then(res => res.json())}const userResponse = getRequestTo('/user-data')
O tipo de userResponse
seria any
. Podemos ter uma melhor implementação TypeScript aqui.
const getRequestTo = async <R>(endpoint: string): Promise<ApiResponse<R>> => { const request = await fetch(process.env.BE_HOST + endpoint); const response = await request.json(); return response;};
Podemos criar uma função semelhante para solicitações POST que também usa JSON como parâmetros do tipo B
e o servidor retornará uma resposta JSON do tipo R
:
const postRequestTo = async <B, R>( endpoint: string, body: B): Promise<ApiResponse<R>> => { const request = await fetch(process.env.BE_HOST + endpoint, { method: "POST", body: JSON.stringify(body), }); const response = await request.json(); return response;};
Da mesma forma, também pode haver uma função de solicitação PATCH, que lida com atualizações parciais de qualquer entidade.
const patchRequestTo = async <B, R>(endpoint: string,body: Partial<B>): Promise<ApiResponse> => { const request = await fetch(process.env.BE_HOST + endpoint, { method: "PATCH", body: JSON.stringify(body) }); const response = await request.json(); return response;};
Aqui está como implementar algo assim:
interface RequestBody {}interface Response {}const createPost = await postRequestTo<RequestBody, Response>('/post', postData);const updatePost = await patchRequestTo<RequestBody, Response>('/post', { title: "new name"});
Vamos juntar tudo isso com uma simples classe JavaScript:
class ApiService<T> { constructor(entitySlug: string) { this.entitySlug = entitySlug; } private entitySlug: string; getOne = async (id: string): Promise<APIResponse<T>> => { const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + id); const response = await request.json(); return response; }; getList = async (): Promise<APIResponse<T[]>> => { const request = await fetch(process.env.BE_HOST + this.entitySlug); const response = await request.json(); return response; }; create = async (body: Omit<T, 'id' | 'created' | 'updated'>): Promise<APIResponse<T>> => { const request = await fetch(process.env.BE_HOST + this.entitySlug, { method: 'POST', body: JSON.stringify(body) }); const response = await request.json(); return response; }; update = async ( body: Omit<Partial<T>, 'id' | 'created' | 'updated'> ): Promise<APIResponse<T>> => { const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + body.id, { method: 'PATCH', body: JSON.stringify(body) }); const response = await request.json(); return response; }; delete = async (id: string): Promise<void> => { const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + id, { method: 'DELETE' }); const response = await request.json(); return response; };};
e você pode criar um serviço de entidade como este:
export const postService = new ApiService<Post>('/post');
e está tudo vinculado e pronto para você.
O texto datilografado tem o potencial de aumentar em dez vezes a experiência do desenvolvedor, se configurado adequadamente. Estas são algumas estratégias para tornar esse processo de configuração mais confortável. Espero que isso ajude você a usar o Typecript melhor em sua base de código atual.