effect-http
effect-http copied to clipboard
Services in Security implementation is leaked into the client
When using a service inside a custom Security, it becomes a requirement of the client endpoint although it should be only used on the server side.
Look below how the effect has the type: Effect.Effect<string, ClientError<number>, UserStorage>
import { Schema } from '@effect/schema';
import { Effect, Layer, pipe } from 'effect';
import {
Api,
ApiEndpoint,
Client,
Middlewares,
RouterBuilder,
Security,
} from 'effect-http';
interface UserInfo {
email: string;
}
class UserStorage extends Effect.Tag('UserStorage')<
UserStorage,
{ getInfo: (user: string) => Effect.Effect<UserInfo> }
>() {
static dummy = Layer.succeed(
UserStorage,
UserStorage.of({
getInfo: (_: string) => Effect.succeed({ email: '[email protected]' }),
}),
);
}
const mySecurity = pipe(
Security.basic({ description: 'My basic auth' }),
Security.map((creds) => creds.user),
Security.mapEffect((user) => UserStorage.getInfo(user)),
);
const api = Api.make().pipe(
Api.addEndpoint(
Api.post('endpoint', '/my-secured-endpoint').pipe(
Api.setResponseBody(Schema.String),
Api.setSecurity(mySecurity),
),
),
);
const app = RouterBuilder.make(api).pipe(
RouterBuilder.handle('endpoint', (_, security) =>
Effect.succeed(`Logged in`),
),
RouterBuilder.build,
Middlewares.errorLog,
);
const client = Client.make(api);
// BAD: Effect.Effect<string, ClientError<number>, UserStorage>
const effect = client.endpoint({});
I have written a quick type helper as a workaround:
type ApiClient<T> =
T extends Api.Api<infer Endpoints> ? Api.Api<MapEndpoints<Endpoints>> : never;
type MapEndpoints<T> =
T extends ApiEndpoint.ApiEndpoint<
infer Endpoint,
infer Req,
infer Resp,
Security.Security<infer A, infer E, unknown>
>
? ApiEndpoint.ApiEndpoint<
Endpoint,
Req,
Resp,
Security.Security<A, E, never>
>
: never;
const client = Client.make(api as ApiClient<typeof api>);
// GOOD: Effect.Effect<string, ClientError<number>, never>
const effect = client.endpoint({});
I will try to do a PR when I find the time.