Using Services

Avatar of Hemanta SundarayHemanta Sundaray

In the previous chapter, we defined and implemented the Email service. Now let’s see how to actually use it.

Imagine our application has a form where the user enters their email address to request a password reset. When they submit the form, the request is handled by a function named handleForgotPassword. This form handler is where we would call sendPasswordResetEmail to send the email.

The sendPasswordResetEmail function currently lives inside the Email service’s implementation. Here is how we get access to it:

email.ts
import { Effect } from "effect";
function handleForgotPassword(email: string) {
return Effect.gen(function* () {
const { sendPasswordResetEmail } = yield* Email;
const resetLink = `https://yourapp.com/reset?token=abc123`;
yield* sendPasswordResetEmail(email, resetLink);
});
}

First, we yield* the Email service. What does that actually do?

Remember the Effect we passed as the second argument to Layer.effect() when we implemented the service. That Effect returns the following object:

{
sendPasswordResetEmail;
}

When you write yield* Email, Effect looks up the implementation of the Email service in the Context (using the key "app/Email" we set during definition) and hands you back that exact object. We destructure sendPasswordResetEmail directly out of it:

const { sendPasswordResetEmail } = yield * Email;

handleForgotPassword itself returns an Effect, which means it is also an Effect program. Let’s try to run it using Effect.runPromise:

const program = handleForgotPassword("user@example.com");
Effect.runPromise(program);
// ^^^^^^^ Type Error!

You’ll see a red squiggly line under program in the line Effect.runPromise(program). When you hover over it, you will see this message:

Argument of type 'Effect<void, SendPasswordResetEmailError, Email>' is not assignable to parameter of type 'Effect<void, SendPasswordResetEmailError, never>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Type 'Email' is not assignable to type 'never'.ts(2379)

Focus on the last line: Type 'Email' is not assignable to type 'never'.

Why do we have this error and how do we fix it?

The type signature of program is Effect<void, SendPasswordResetEmailError, Email>. That Email in the third position is the requirements channel. It means Effect needs this requirement to be satisfied before it can run the program.

Satisfying Requirements

To satisfy a requirement, we need to give the program a Layer that knows how to build the service it depends on. We do that with Effect.provide:

const program = handleForgotPassword("user@example.com").pipe(
Effect.provide(EmailLayer),
);
Effect.runPromise(program);

Notice that there is no red squiggly under program this time. That is because we provided the Email dependency to the program using Effect.provide(EmailLayer).

If you hover over program now, you will see its type is Effect<void, SendPasswordResetEmailError, never>. The Email in the requirements channel is gone, replaced by never. never here means “this program has no remaining requirements”, which is exactly what Effect.runPromise expects.

When the program runs, Effect builds the Email implementation from EmailLayer and stores it in the Context. When handleForgotPassword reaches yield* Email, Effect looks up the implementation in the Context and hands it back. The sendPasswordResetEmail function is then called, and the email is sent.

Congratulations! You just used your first Effect service.

Let’s circle back to the promise I made earlier. I said that Effect Service is a system that automatically makes dependencies available to any function that needs them, no matter how deep in the call chain, and tracks all of this at the type level so the compiler tells us if we forgot to provide something.

You saw all three of these in action. When handleForgotPassword reached for the Email service with yield* Email, it did not have to know where the implementation came from or who was supposed to pass it in. Effect made it available automatically. The requirement showed up in the type signature as Effect<void, SendPasswordResetEmailError, Email>, which is how Effect tracks dependencies at the type level. And when we tried to run the program without providing an implementation, the compiler stopped us with Type 'Email' is not assignable to type 'never', exactly as promised.

I hope you now understand the benefits Effect Services provide when it comes to dependency management.

Last updated on May 23, 2026

Sign in to save progress

Stay in the loop

Get notified when new Effect Foundation courses are released.