We are going to configure @paddle/paddle-js
using an effect service.
@paddle/paddle-js
export a initializePaddle
function. initializePaddle
requires a valid client token and allows to provide the inline checkout configuration options.
The service defines a function that requires a clientToken
and returns an effect calling initializePaddle
:
export class Paddle extends Effect.Service<Paddle>()("Paddle", {
succeed: ({ clientToken }: { clientToken: string }) =>
Effect.tryPromise(() =>
initializePaddle({
token: clientToken,
environment: "sandbox",
debug: true,
checkout: {
settings: {
displayMode: "inline",
frameInitialHeight: 450,
frameTarget: PADDLE_CONTAINER_CLASS,
frameStyle:
"width: 100%; min-width: 312px; background-color: transparent; border: none;",
locale: "en",
},
},
})
).pipe(
Effect.flatMap(Effect.fromNullable),
Effect.mapError((cause) => new ErrorPaddle({ cause }))
),
}) {}
We also need an Api
service responsible to interact with the server
API. Api
uses HttpApiClient
to derive a type-safe HTTP client from the shared api-client
definition:
export class Api extends Effect.Service<Api>()("Api", {
effect: Effect.gen(function* () {
const baseUrl = yield* Config.string("API_BASE_URL").pipe(
Config.withDefault("http://localhost:3000")
);
return yield* HttpApiClient.make(MainApi, {
baseUrl,
});
}),
dependencies: [FetchHttpClient.layer],
}) {}
By sharing the
api-client
definition, the HTTP client provides auto-complete for each endpoint parameters, success, and error types.
We then build a ManagedRuntime
containing both Api
and Paddle
services:
import { Layer, ManagedRuntime } from "effect";
import { Api } from "./api";
import { Paddle } from "./paddle";
const MainLayer = Layer.mergeAll(Api.Default, Paddle.Default);
export const RuntimeClient = ManagedRuntime.make(MainLayer);
We can now use RuntimeClient
to execute all the effects inside the client.