У меня повторяющаяся проблема, поэтому, вероятно, существует стандартный способ, которым люди справляются с этим. У меня есть сервер RESTful, я пишу клиентский API для использования во многих клиентских приложениях.
Используя здесь TypeScript в качестве языка, допустим, у меня есть функция аутентификации, которая ожидает, что сервер вернет тело форма { "token": "JWT ....." }
. Прямо сейчас у меня это смоделировано как
export const authenticate = (baseUrl: string) =>
(username: string, password: string): Promise<HttpClientResponse<{token:string}>> =>
httpClient.get(...)
Это прекрасно работает. В моем клиенте я могу
import { authenticate } from '...'
const result = await authenticate('http://example.com/api/v1.0/')('my-username','some-password')
console.log(result.token)
Но, учитывая, сколько у меня есть этих функций, которые требуют baseUrl
(и большинство из них также jwtToken:string
для помещения в заголовок Authorization
), это кажется, что я должен использовать шаблон считывателя:
export const getProjectsForUser = (baseUrl: string, jwtToken: string) => (userId:number) =>
httpClient.get(...)
// OR
type Defs = {
baseUrl: string,
jwtToken: string,
}
export const getProjectsForUserAlt = (userId: number): Reader<Deps, Promise<...>> =>
(deps: Deps) => httpClient.get(deps.baseUrl+...)
(В случае библиотеки fp-ts
на самом деле есть typealias для Reader<E, Lazy<Promise<A>>>
, то есть ReaderTaskEither<E, A>
(работает немного как ReaderT
(Reader monad преобразователь) Я думаю, но я только упомяну это, чтобы показать, что я знаю об этом для этой библиотеки.)
Тогда, если у меня есть куча этих Readers
, я мог бы хотеть чтобы сгруппировать их для удобства в других местах:
const APIInstance: Reader<Deps, APIInterface> = (deps:Deps) => ({
getProfile: Reader<Deps, Profile> = ...
createContact: Reader<Deps, number> = ...
getContact: Reader<Deps, Contact> = ...
})
Тогда мне не нужно обмениваться baseUrl. Просто создайте экземпляр одного экземпляра API.
Но тогда это заставляет меня задуматься о Reader который возвращает объект Readers с тем же объектом зависимости / структурой / классом данных. Есть ли лучший способ сделать это?
Если клиентское приложение, использующее модуль API клиента, предположительно, передало baseUrl через переменную среды или конфигурацию файл, у вас есть часть "Депс", прежде чем у вас есть что-нибудь еще, так что читатели должны быть обменены? Например, getUser = (baseUrl:string) => Reader<UserId, User>
, а затем вы вызываете с помощью getUser('http:...')(5)
(с помощью fp-ts) или getUser("http://...").run(5)
(с помощью стрелки Kotlin) и т. Д. c.
Каков рекомендуемый способ делать это? Это похоже на шаблон GOF для OOP (не знаю имени) для
class getUserUseCase {
constructor(baseUrl:string) { this.url=baseUrl+'/user' }
async fun run(id: number): User {
// return await http GET this.url
}
}