Skip to content

Structured errors

There is a basic example of fetching JSON with schema validation.

We have all fetch errors handled through the Effect error channel. Potencial problems with JSONs are also handled.

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schema
Schema
} from "effect";
import {
import Fetch
Fetch
,
import Request
Request
} from "fx-fetch";
class
class User
User
extends
import Schema
Schema
.
const Class: <User>(identifier: string) => <Fields>(fieldsOr: Fields | HasFields<Fields>, annotations?: ClassAnnotations<User, { [K in keyof Schema.Struct<Fields extends Schema.Struct.Fields>.Type<Fields>]: Schema.Struct.Type<Fields>[K]; }> | undefined) => Schema.Class<User, Fields, Schema.Struct.Encoded<Fields>, Schema.Schema<in out A, in out I = A, out R = never>.Context<Fields[keyof Fields]>, Schema.Struct.Constructor<Fields>, {}, {}>

@example

import { Schema } from "effect"
class MyClass extends Schema.Class<MyClass>("MyClass")({
someField: Schema.String
}) {
someMethod() {
return this.someField + "bar"
}
}

@since3.10.0

Class
<
class User
User
>("User")({
id: typeof Schema.Int
id
:
import Schema
Schema
.
class Int

@since3.10.0

Int
,
firstName: typeof Schema.String
firstName
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
lastName: typeof Schema.String
lastName
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
}) { }
// ┌─ (id: number) => Effect.Effect<
// │ User,
// │ | Fetch.AbortError
// │ | Fetch.FetchError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const
const getUser: (id: number) => Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>
getUser
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fn: <YieldWrap<Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>>, User, [id: number]>(body: (id: number) => Generator<YieldWrap<Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>>, User, never>) => (id: number) => Effect.Effect<...> (+20 overloads)
fn
(function* (
id: number
id
: number) {
const
const req: Request.Request
req
=
import Request
Request
.
function unsafeMake(input: Request.Request.Input): Request.Request
export unsafeMake

Creates a immutable Request object. Throws an error if the input is invalid.

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake({
url: 'https://example.com',
method: 'POST'
});

@example

import { Request } from 'fx-fetch';
// ┌─── Request.Request
// ▼
const request = Request.unsafeMake('https://example.com');

@since0.1.0

unsafeMake
({
url: string
url
: `https://dummyjson.com/users/${
id: number
id
}` });
const
const payload: User
payload
= yield*
import Fetch
Fetch
.
fetchJsonWithSchema<User, {
readonly id: number;
readonly firstName: string;
readonly lastName: string;
}, never>(request: Request.Request, schema: Schema.Schema<User, {
readonly id: number;
readonly firstName: string;
readonly lastName: string;
}, never>): Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch> (+1 overload)
export fetchJsonWithSchema

Fetches and reads a JSON response with the given schema.

@since0.1.0

@seereadJsonWithSchema

@example

import { Effect, Schema } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
const UserSchema = Schema.Struct({
id: Schema.Int,
name: Schema.String,
});
// ┌─── Effect.Effect<
// │ void,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError
// │ | MalformedJsonError
// │ | ParseError,
// │ Fetch.Fetch
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: './my-endpoint' });
// ┌─── typeof UserSchema.Type
// ▼
const payload = yield* Fetch.fetchJsonWithSchema(request, UserSchema);
});

fetchJsonWithSchema
(
const req: Request.Request
req
,
class User
User
);
// User ╶─┐
// ▼
return
const payload: User
payload
;
});
await
const getUser: (id: number) => Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>
getUser
(1).
Pipeable.pipe<Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, Fetch.Fetch>, Effect.Effect<User, Fetch.AbortError | Fetch.FetchError | Fetch.NotAllowedError | NotOkError | MalformedJsonError | ParseError, never>, Promise<User>>(this: Effect.Effect<...>, ab: (_: Effect.Effect<User, Fetch.AbortError | ... 4 more ... | ParseError, Fetch.Fetch>) => Effect.Effect<...>, bc: (_: Effect.Effect<...>) => Promise<...>): Promise<...> (+21 overloads)
pipe
(
// Handle errors here
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const provideService: <Fetch.Fetch, (request: Request.Request) => Effect.Effect<Fetch.Fetch.SuccessType, Fetch.Fetch.ErrorType, never>>(tag: Tag<Fetch.Fetch, (request: Request.Request) => Effect.Effect<Fetch.Fetch.SuccessType, Fetch.Fetch.ErrorType, never>>, service: (request: Request.Request) => Effect.Effect<Fetch.Fetch.SuccessType, Fetch.Fetch.ErrorType, never>) => <A, E, R>(self: Effect.Effect<...>) => Effect.Effect<...> (+1 overload)

Provides an implementation for a service in the context of an effect.

Details

This function allows you to supply a specific implementation for a service required by an effect. Services are typically defined using Context.Tag, which acts as a unique identifier for the service. By using this function, you link the service to its concrete implementation, enabling the effect to execute successfully without additional requirements.

For example, you can use this function to provide a random number generator, a logger, or any other service your effect depends on. Once the service is provided, all parts of the effect that rely on the service will automatically use the implementation you supplied.

Example

import { Effect, Context } from "effect"
// Declaring a tag for a service that generates random numbers
class Random extends Context.Tag("MyRandomService")<
Random,
{ readonly next: Effect.Effect<number> }
>() {}
// Using the service
const program = Effect.gen(function* () {
const random = yield* Random
const randomNumber = yield* random.next
console.log(`random number: ${randomNumber}`)
})
// Providing the implementation
//
// ┌─── Effect<void, never, never>
// ▼
const runnable = Effect.provideService(program, Random, {
next: Effect.sync(() => Math.random())
})
// Run successfully
Effect.runPromise(runnable)
// Example Output:
// random number: 0.8241872233134417

@seeprovide for providing multiple layers to an effect.

@since2.0.0

provideService
(
import Fetch
Fetch
.
class Fetch
export Fetch

Fetch service for making or mocking HTTP requests.

@example

import { Effect } from 'effect';
import { Fetch, Request } from 'fx-fetch';
Effect.gen(function* () {
const request = Request.unsafeMake({ url: 'https://example.com' });
const fetch = yield* Fetch.Fetch; // ◀︎── Get the Fetch service
const response = yield* fetch(request);
}).pipe(
Effect.provide(Fetch.layer), // ◀︎── Provide built-in Fetch layer
Effect.runPromise
);

@since0.1.0

Fetch
,
import Fetch
Fetch
.
const FetchLive: (request: Request.Request) => Effect.Effect<Fetch.Fetch.SuccessType, Fetch.Fetch.ErrorType, never>
export FetchLive

Live implementation of the Fetch service that performs actual HTTP requests.

@example

import { Effect } from 'effect';
import { Fetch, Request, Response } from 'fx-fetch';
// ┌─── Effect.Effect<
// │ Response.Response,
// │ | Fetch.FetchError
// │ | Fetch.AbortError
// │ | Fetch.NotAllowedError
// │ | Response.NotOkError,
// │ never
// │ >
// ▼
const program = Effect.gen(function* () {
const request = Request.unsafeMake({ url: 'https://example.com' });
// ┌─── Response.Response
// ▼
const response = yield* Fetch.FetchLive(request);
return response;
});

@since0.1.0

FetchLive
),
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <A, E>(effect: Effect.Effect<A, E, never>, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<A>

Executes an effect and returns the result as a Promise.

Details

This function runs an effect and converts its result into a Promise. If the effect succeeds, the Promise will resolve with the successful result. If the effect fails, the Promise will reject with an error, which includes the failure details of the effect.

The optional options parameter allows you to pass an AbortSignal for cancellation, enabling more fine-grained control over asynchronous tasks.

When to Use

Use this function when you need to execute an effect and work with its result in a promise-based system, such as when integrating with third-party libraries that expect Promise results.

Example (Running a Successful Effect as a Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

Example (Handling a Failing Effect as a Rejected Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@since2.0.0

runPromise
,
);

If you are familiar with EffectTS then this example should look very straightforward. The Fx-Fetch library uses same principles and patterns.

Happy effectful coding!