Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can't distinguish between responses with the same (void) schema #4039

Open
ryskajakub opened this issue Dec 1, 2024 · 3 comments
Open

can't distinguish between responses with the same (void) schema #4039

ryskajakub opened this issue Dec 1, 2024 · 3 comments
Labels
bug Something isn't working platform

Comments

@ryskajakub
Copy link
Contributor

What version of Effect is running?

"@effect/platform": "^0.69.25",

What steps can reproduce the bug?

I add endpoint:

    HttpApiEndpoint.get('home', '/')
      .addSuccess(HttpApiSchema.Created)
      .addSuccess(HttpApiSchema.NoContent)

implement it:

.handle('home', () => Effect.succeed(null)

What is the expected behavior?

I should have an option to return something different to trigger the no content response.

What do you see instead?

The handler always returns the Created status

Additional information

I can do some workaround to make it work,

  1. Either I can make the second response to be error response and then use Effect.fail
  2. Or I can add a distinguishable schema to each response, so the result of handler can be dispatched to the correct status code
@ryskajakub ryskajakub added the bug Something isn't working label Dec 1, 2024
@tim-smart
Copy link
Member

To allow for multiple empty responses on an endpoint you first have to create a discriminated union to distinguish between the different response types, then "cast" them to empty responses:

import {
  HttpApi,
  HttpApiBuilder,
  HttpApiEndpoint,
  HttpApiGroup,
  HttpApiScalar,
  HttpApiSchema,
  HttpServer
} from "@effect/platform"
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
import { Effect, Layer, Schema } from "effect"
import { createServer } from "http"

class NoContent extends Schema.TaggedClass<NoContent>()("NoContent", {}) {}

class ImATeapot extends Schema.TaggedClass<ImATeapot>()("ImATeapot", {}) {}

class EmptyApi extends HttpApiGroup.make("empty").add(
  HttpApiEndpoint.get("no-content", "/no-content")
    .addSuccess(NoContent.pipe(HttpApiSchema.asEmpty({
      status: 204,
      decode: () => new NoContent()
    })))
    .addSuccess(ImATeapot.pipe(HttpApiSchema.asEmpty({
      status: 418,
      decode: () => new ImATeapot()
    })))
) {}

class TopLevelApi extends HttpApi.empty.add(EmptyApi) {}

const EmptyApiLive = HttpApiBuilder.group(
  TopLevelApi,
  "empty",
  (handlers) => handlers.handle("no-content", () => Effect.succeed(new ImATeapot()))
)

const ApiLive = HttpApiBuilder.api(TopLevelApi).pipe(
  Layer.provide(EmptyApiLive)
)

HttpApiBuilder.serve().pipe(
  HttpServer.withLogAddress,
  Layer.provide(HttpApiScalar.layer()),
  Layer.provide(ApiLive),
  Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
  Layer.launch,
  NodeRuntime.runMain
)

@gcanti gcanti added the platform label Dec 5, 2024
@mikearnaldi
Copy link
Member

To allow for multiple empty responses on an endpoint you first have to create a discriminated union to distinguish between the different response types, then "cast" them to empty responses:

import {
  HttpApi,
  HttpApiBuilder,
  HttpApiEndpoint,
  HttpApiGroup,
  HttpApiScalar,
  HttpApiSchema,
  HttpServer
} from "@effect/platform"
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
import { Effect, Layer, Schema } from "effect"
import { createServer } from "http"

class NoContent extends Schema.TaggedClass<NoContent>()("NoContent", {}) {}

class ImATeapot extends Schema.TaggedClass<ImATeapot>()("ImATeapot", {}) {}

class EmptyApi extends HttpApiGroup.make("empty").add(
  HttpApiEndpoint.get("no-content", "/no-content")
    .addSuccess(NoContent.pipe(HttpApiSchema.asEmpty({
      status: 204,
      decode: () => new NoContent()
    })))
    .addSuccess(ImATeapot.pipe(HttpApiSchema.asEmpty({
      status: 418,
      decode: () => new ImATeapot()
    })))
) {}

class TopLevelApi extends HttpApi.empty.add(EmptyApi) {}

const EmptyApiLive = HttpApiBuilder.group(
  TopLevelApi,
  "empty",
  (handlers) => handlers.handle("no-content", () => Effect.succeed(new ImATeapot()))
)

const ApiLive = HttpApiBuilder.api(TopLevelApi).pipe(
  Layer.provide(EmptyApiLive)
)

HttpApiBuilder.serve().pipe(
  HttpServer.withLogAddress,
  Layer.provide(HttpApiScalar.layer()),
  Layer.provide(ApiLive),
  Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
  Layer.launch,
  NodeRuntime.runMain
)

why not doing this by default?

@tim-smart
Copy link
Member

You have to explicitly specify what the status code will rehydrate to.

With the recently added predefined error types in the HttpApiError module it is easier to accomplish now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working platform
Projects
None yet
Development

No branches or pull requests

4 participants