Skip to content

[FEATURE REQUEST] Better serialization in workflows #197

@aldebout

Description

@aldebout

It'd be really nice to be able to add a custom serializer/deserializer to workflow steps when passing data between them, especially since the type inference is currently happy with Dates and others even though it actually breaks in real life.

Example:

import { serve } from "@upstash/qstash/nextjs";
import devalue from "devalue";
import { z } from "zod";

const endpointPayloadSchema = z.object({
  eventId: z.string(),
});

export const workflowEndpoint = serve<z.infer<typeof endpointPayloadSchema>>(
  async (context) => {
    const payload = endpointPayloadSchema.parse(context.requestPayload);

    const data = await context.run(
      "fetch-data",
      async () => {
        const event = await db.get(payload.eventId);

        return { event };
      },
      { serialize: devalue.stringify, deserialize: devalue.parse }
    );

    await context.run(
      "send-email",
      async () => {
        await sendEmail({
          templateId: 1,
          templateData: {
            date: Intl.DateTimeFormat("en-US", {
              day: "numeric",
              weekday: "long",
              month: "long",
              year: "numeric",
            }).format(data.event.startDate),
          },
        });
      },
      { serialize: devalue.stringify, deserialize: devalue.parse }
    );
  }
);

Currently, this code (minus the serialize/deserialize bit) works typescript-wise, but fails in real life because event.startDate is not deserialized as a Date but as a string. I'm assuming the underlying logic is performing a JSON.stringify/JSON.parse.

This could also be added at the serve level, to ensure consistency between the steps.

I've tried doing it in userland, it works but is pretty annoying type-wise. I have not found a better way than explicitly defining all my return types and do return devalue.stringify(response as ResponseType) and then const parsedData = devalue.parse(data) as ResponseType which is difficult when the return types are complex.

One outstanding question: what does this mean for the actual qstash platform? Currently, you can see the output data on teh platform as what looks like a Go print, which sounds to me like the output is parsed by qstash and that would make the use of user-defined serialization hard to work with.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions