Skip to content

Easily use Effect for Next.js Route Handlers, Server Actions, Pages and Forms

Notifications You must be signed in to change notification settings

useflytrap/next-effect

Repository files navigation

Next Effect cover

Next Effect

npm version npm downloads Github Actions

Easily use Effect for Next.js Route Handlers, Server Actions, Pages and Forms

Next Effect is a library that makes it easy to use Effect for Next.js Route Handlers, Server Actions, Pages and Forms. It provides helpful utilities for working with Next.js using Effect.

Features

⚡️ Quickstart

  1. Install the Neft Effect package
$ pnpx jsr add next-effect
  1. Create a Server Action handler
import { makeServerActionHandler } from "next-effect"

export class InternalServerError extends S.TaggedError<InternalServerError>()("InternalServerError", {
  success: S.Boolean,
  message: S.String,
  reason: S.String,
}) {}

export class InvalidPayload extends S.TaggedError<InvalidPayload>()("InvalidPayload", {
  success: S.Boolean,
  message: S.String,
  reason: S.String,
}) {}

export const invalidPayload = new InvalidPayload({ success: false, message: "Invalid payload", reason: 'invalid-payload' })
export const internalServerError = new InternalServerError({ success: false, message: "Internal server error", reason: 'internal-server-error' })

const makeServerAction = makeServerActionHandler({
  errors: {
    invalidPayload: ({ error, schema, payload }) => invalidPayload,
    unexpected: (cause: Cause.Cause<unknown>) => internalServerError,
  },
})

// lib/actions/create-team.ts
export const CreateTeamPayload = S.Struct({
  name: S.String,
  userId: S.String,
})

export const CreateTeamFailedError extends S.TaggedError<CreateTeamFailedError>()("CreateTeamFailedError", {
  success: S.Boolean,
  message: S.String,
  reason: S.String,
}) {}

export const CreateTeamSuccess = S.Struct({
  success: S.Literal(true),
  message: S.String,
})

export const createTeam = makeServerAction(CreateTeamPayload, async (payload: S.Schema.Type<typeof CreateTeamPayload>) => Effect.gen(function* () {
  const random = yield* Random.next
  if (random > 0.5) {
    return yield* Effect.fail(new CreateTeamFailedError({ success: false, message: "Failed to create team", reason: 'create-team-failed' }))
  }
  return CreateTeamSuccess.make({
    success: true,
    message: "Team created successfully.",
  })
}))

// components/create-team.tsx
export const CreateTeamForm = () => {
  async function createTeam() {
    const result: CreateTeamSuccess | CreateTeamFailedError | InvalidPayload | InternalServerError = await createTeam({ name: 'John Doe', userId: '123' })

    // 👇 We can easily handle errors, since all our errors are fully typed
    if (result._tag === "CreateTeamFailedError") {
      sonner.error("Oops! Failed to create team: " + result.message)
    }

    // Continue…
  }
}
  1. Create a Route Handler
export class InternalServerError extends S.TaggedError<InternalServerError>()("InternalServerError", {
  success: S.Boolean,
  message: S.String,
  reason: S.String,
}) {}

export class InvalidPayload extends S.TaggedError<InvalidPayload>()("InvalidPayload", {
  success: S.Boolean,
  message: S.String,
  reason: S.String,
}) {}

export const invalidPayload = new InvalidPayload({ success: false, message: "Invalid payload", reason: 'invalid-payload' })
export const internalServerError = new InternalServerError({ success: false, message: "Internal server error", reason: 'internal-server-error' })

const createRouteHandler = makeRouteHandler({
  errors: {
    invalidPayload: ({ error, schema, payload }) => invalidPayload,
    unexpected: (cause: Cause.Cause<unknown>) => internalServerError
  },
  // 👇 Define your response types here (these are used for extracting response status codes, encoding etc.)
  responses: [Unauthorized, TestSuccess, TextResponse, BytesResponse]
})

export const GET = createRouteHandler(Effect.sync(() => {
  const name = yield* Next.ensureRequestSchema(S.Struct({ name: S.String }))
  return { success: true, message: `API key created for ${name.name}.` }
}))
  1. Create a Form Handler
// todo: write this

🎥 Write Server Actions using Effect

TODO Write Docs

⛓️ Write Forms using Effect

TODO Write docs

💻 Development

  • Clone this repository
  • Install dependencies using deno install
  • Run the tests using deno test

License

Made with ❤️ in Helsinki, Finland.

Published under MIT License.

TODO

  • OTEL setup
    • Work with Effect Dev Tools
  • Request tracing
    • Easily add context to traces
  • Way to define response types / errors
  • Schemas to define response types (raw, text), status codes
  • Headers

About

Easily use Effect for Next.js Route Handlers, Server Actions, Pages and Forms

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published