Skip to content

Instantly share code, notes, and snippets.

@ashwanth1109
Created September 16, 2022 02:53
Show Gist options
  • Save ashwanth1109/ca13e58b121afb3c4e75f8b74d323f45 to your computer and use it in GitHub Desktop.
Save ashwanth1109/ca13e58b121afb3c4e75f8b74d323f45 to your computer and use it in GitHub Desktop.
useAPI proposal
import { useState, useEffect, useCallback } from 'react';
import axios, { AxiosInstance } from 'axios';
import camelcaseKeys from 'camelcase-keys';
import { ok, err, Result } from 'neverthrow';
interface APIError {
message: string;
}
interface useApiProps {
apiBaseUrl: string;
tokenFetcher: () => Promise<string | null>;
}
interface Account {
id: number;
name: string;
smbName: string;
}
interface IUseAccountServices {
getAccount: () => Promise<Result<Account, APIError>>;
}
interface Instance {
id: number;
account: Account;
name: string;
}
interface IUseInstanceServices {
getInstance: (id: number) => Promise<Result<Instance, APIError>>;
getInstances: () => Promise<Result<Instance[], APIError>>;
}
interface APIServices {
accountService: ReturnType<typeof useAccountServices>;
instanceService: ReturnType<typeof useInstanceServices>;
}
const useAccountServices = ({
api,
}: {
api: AxiosInstance | null;
}): IUseAccountServices => {
const getAccount = useCallback(async () => {
try {
const res = await api?.get('/account');
return ok(res?.data);
} catch (e) {
return err({ message: e.message });
}
}, [api]);
return {
getAccount,
};
};
const useInstanceServices = ({
api,
}: {
api: AxiosInstance | null;
}): IUseInstanceServices => {
const getInstance = useCallback(
async (id: number) => {
try {
const res = await api?.get(`/instances/${id}`);
return ok(res?.data);
} catch (e) {
return err({ message: e.message });
}
},
[api]
);
const getInstances = useCallback(async () => {
try {
const res = await api?.get('/instances');
return ok(res?.data);
} catch (e) {
return err({ message: e.message });
}
}, [api]);
return {
getInstance,
getInstances,
};
};
const useApi = ({ apiBaseUrl, tokenFetcher }: useApiProps): APIServices => {
const [token, setToken] = useState<string | null>(null);
const [api, setApi] = useState<AxiosInstance | null>(null);
const [err, setErr] = useState<Error | null>(null);
const accountService = useAccountServices({ api });
const instanceService = useInstanceServices({ api });
//
const services = {
accountService,
instanceService,
};
// Effect to fetch the token
useEffect(() => {
const fetchToken = async () => {
const token = await tokenFetcher();
setToken(token);
};
fetchToken().catch((e) => setErr(e));
}, [tokenFetcher]);
// Effect to create API endpoint instance once token is available
useEffect(() => {
if (!token) {
return;
}
try {
const api = axios.create({
baseURL: apiBaseUrl,
headers: {
Authorization: token,
},
});
// https://stackoverflow.com/questions/43051291/attach-authorization-header-for-all-axios-requests
// Attach authorization token to all requests
api.interceptors.request.use((cfg) => {
cfg.headers.Authorization = token;
return cfg;
});
// Convert snake case to camel case for all responses
api.interceptors.response.use((res) => {
res.data = camelcaseKeys(res.data, { deep: true });
return res;
});
setApi(api);
} catch (e) {
setErr(e);
}
}, [apiBaseUrl, token]);
return services;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment