When making a request with HttpClient we can also validate the response schema using Schema.
In a separate schema.ts file we define the schema for a post using Schema.Class:
src/services/schema.ts
import { Schema } from "effect";
export class Post extends Schema.Class<Post>("Post")({
userId: Schema.Number,
id: Schema.Number,
title: Schema.String,
body: Schema.String,
}) {}The schema is based on the response from the jsonplaceholder API:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}Since getPosts returns an array of posts, it's convenient to define a schema for an array of posts directly inside the Post class:
export class Post extends Schema.Class<Post>("Post")({
userId: Schema.Number,
id: Schema.Number,
title: Schema.String,
body: Schema.String,
}) {
static readonly Array = Schema.Array(this);
}We then validate the response type using HttpClientResponse.schemaBodyJson:
- Access the response body using
Effect.flatMap - Apply the schema to validate the response with
HttpClientResponse.schemaBodyJson
src/services/Api.ts
import {
FetchHttpClient,
HttpClient,
HttpClientRequest,
HttpClientResponse,
} from "@effect/platform";
import { Effect, flow } from "effect";
import { Post } from "./schema";
export class Api extends Effect.Service<Api>()("Api", {
dependencies: [FetchHttpClient.layer],
effect: 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.flatMap(HttpClientResponse.schemaBodyJson(Post.Array)),
Effect.scoped
),
getPostById: (id: string) =>
client
.get(`/posts/${id}`)
.pipe(
Effect.flatMap(HttpClientResponse.schemaBodyJson(Post)),
Effect.scoped
),
};
}),
}) {}With this both the getPosts and getPostById methods are now type-safe:
getPostsreturnsEffect<readonly Post[], HttpClientError | ParseError>getPostByIdreturnsEffect<Post, HttpClientError | ParseError>
ParseErroris the error added byschemaBodyJsonwhen the response doesn't match the schema.
