We can now implement the actual API requests using @effect/platform
(and @effect/schema
).
With @effect/platform
we define a HttpClient
that we use to make requests to the jsonplaceholder API.
HttpClient
is a generic service to perform HTTP requests. It defines shared configurations for all requests.
Inside Api.ts
we create a make
function that initializes the base HttpClient
. We then use make
to type the contents of the Api
service by extracting its success type with Effect.Effect.Success<typeof make>
:
const make = Effect.gen(function* () {
const baseClient = yield* HttpClient.HttpClient;
/// ...
});
export class Api extends Context.Tag("Api")<
Api,
Effect.Effect.Success<typeof make>
>() {}
Inside make
we extend baseClient
to add some configurations to all requests:
- Prepend the jsonplaceholder API url (
.prependUrl
) - Add a
Content-Type
header withapplication/json
(.acceptJson
)
const make = Effect.gen(function* () {
const baseClient = yield* HttpClient.HttpClient;
const client = baseClient.pipe(
HttpClient.mapRequest(
flow(
HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"),
HttpClientRequest.acceptJson
)
)
);
/// ...
});
Using client
we can now implement the getPosts
and getPostById
methods:
const make = Effect.gen(function* () {
const baseClient = yield* HttpClient.HttpClient;
const client = baseClient.pipe(
HttpClient.mapRequest(
flow(
HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"),
HttpClientRequest.acceptJson
)
)
);
return {
getPosts: client.get("/posts"),
getPostById: (id: string) => client.get(`/posts/${id}`),
} as const;
});
client.get
returnsEffect<HttpClientResponse, HttpClientError, Scope>
:
HttpClientResponse
is the response from the APIHttpClientError
contains any error returned by the APIScope
allows you to control the lifetime of the HTTP request, aborting it once the scope is closed
Since both requests require a Scope
, we can use Effect.scoped
to ensure that their finalizers are run as soon as the request completes execution:
It's common to move
Effect.scoped
as close to the client call as possible, in this case immediately after consuming the response.
const make = Effect.gen(function* () {
const baseClient = yield* HttpClient.HttpClient;
const client = baseClient.pipe(
HttpClient.mapRequest(
flow(
HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"),
HttpClientRequest.acceptJson
)
)
);
return {
getPosts: client
.get("/posts")
.pipe(Effect.scoped),
getPostById: (id: string) =>
client
.get(`/posts/${id}`)
.pipe(Effect.scoped),
} as const;
});
This is all we need for a working API service.