Getting DataCloneError while using instance of a class as return value of RPC method
Error message: DataCloneError: Could not serialize object of type "st". This type does not support serialization.
Hono route
RPC class
app.get("/pay/:id", async (c) => {
const id = c.req.param("id");
const services = c.get("services");
const paymentService = await c.env.PAYMENTS.newPaymentService(services.logger);
try {
const response = await paymentService.queryPaymentStatus({
pspReference: id,
reference: id,
sessionId: id
});
return c.json({
status: "successful",
message: "api request completed",
rpcResponse: response
})
} finally {
paymentService[Symbol.dispose]()
}
})
app.get("/pay/:id", async (c) => {
const id = c.req.param("id");
const services = c.get("services");
const paymentService = await c.env.PAYMENTS.newPaymentService(services.logger);
try {
const response = await paymentService.queryPaymentStatus({
pspReference: id,
reference: id,
sessionId: id
});
return c.json({
status: "successful",
message: "api request completed",
rpcResponse: response
})
} finally {
paymentService[Symbol.dispose]()
}
})
export class PaymentService extends WorkerEntrypoint<Env> {
fetch(request: Request): Response | Promise<Response> {
return new Response("Hello from payments RPC")
}
async newPaymentService(logger: Logger) {
initCache(this.ctx, this.env);
return new Payments(logger);
}
}
export class PaymentService extends WorkerEntrypoint<Env> {
fetch(request: Request): Response | Promise<Response> {
return new Response("Hello from payments RPC")
}
async newPaymentService(logger: Logger) {
initCache(this.ctx, this.env);
return new Payments(logger);
}
}
5 Replies
What is a
Logger
?This is what services look like:
Logger is a custom logger, wrapped around console.log
this is how Payments class looks like too
cc @Hard@Work
works well when i don't pass services.logger, does this mean i can't pass any variable to the class instance ? @Hard@Work
also works when i pass logger as a function, a bit weird that passing a class instance to the RPC stub does not work because the structured clone algorithm can copy a class instance
solution:
import { Env } from "../env";
import type { Logger } from "@paycashless/worker-logger";
import type { Metrics } from "../metrics";
export type ServiceContext = {
logger: Logger;
metrics: Metrics;
};
export type HonoEnv = {
Bindings: Env;
Variables: {
requestId: string;
services: ServiceContext;
/**
* IP address or region information
*/
location: string;
userAgent?: string;
};
};
import { Env } from "../env";
import type { Logger } from "@paycashless/worker-logger";
import type { Metrics } from "../metrics";
export type ServiceContext = {
logger: Logger;
metrics: Metrics;
};
export type HonoEnv = {
Bindings: Env;
Variables: {
requestId: string;
services: ServiceContext;
/**
* IP address or region information
*/
location: string;
userAgent?: string;
};
};
import { Log, type LogSchema } from "@paycashless/logs";
import type { Fields, Logger } from "./interface";
export class ConsoleLogger implements Logger {
private requestId: string;
private readonly application: LogSchema["application"];
private readonly defaultFields: Fields;
constructor(opts: {
requestId: string;
application: LogSchema["application"];
defaultFields?: Fields;
}) {
this.requestId = opts.requestId;
this.application = opts.application;
this.defaultFields = opts.defaultFields ?? {};
}
private marshal(
level: "debug" | "info" | "warn" | "error" | "fatal",
message: string,
fields?: Fields,
): string {
return new Log({
type: "log",
application: this.application,
requestId: this.requestId,
time: Date.now(),
level,
message,
context: { ...this.defaultFields, ...fields },
}).toString();
}
public debug(message: string, fields?: Fields): void {
console.debug(this.marshal("debug", message, fields));
}
public info(message: string, fields?: Fields): void {
console.info(this.marshal("info", message, fields));
}
public warn(message: string, fields?: Fields): void {
console.warn(this.marshal("warn", message, fields));
}
public error(message: string, fields?: Fields): void {
console.error(this.marshal("error", message, fields));
}
public fatal(message: string, fields?: Fields): void {
console.error(this.marshal("fatal", message, fields));
}
public setRequestId(requestId: string): void {
this.requestId = requestId;
}
}
import { Log, type LogSchema } from "@paycashless/logs";
import type { Fields, Logger } from "./interface";
export class ConsoleLogger implements Logger {
private requestId: string;
private readonly application: LogSchema["application"];
private readonly defaultFields: Fields;
constructor(opts: {
requestId: string;
application: LogSchema["application"];
defaultFields?: Fields;
}) {
this.requestId = opts.requestId;
this.application = opts.application;
this.defaultFields = opts.defaultFields ?? {};
}
private marshal(
level: "debug" | "info" | "warn" | "error" | "fatal",
message: string,
fields?: Fields,
): string {
return new Log({
type: "log",
application: this.application,
requestId: this.requestId,
time: Date.now(),
level,
message,
context: { ...this.defaultFields, ...fields },
}).toString();
}
public debug(message: string, fields?: Fields): void {
console.debug(this.marshal("debug", message, fields));
}
public info(message: string, fields?: Fields): void {
console.info(this.marshal("info", message, fields));
}
public warn(message: string, fields?: Fields): void {
console.warn(this.marshal("warn", message, fields));
}
public error(message: string, fields?: Fields): void {
console.error(this.marshal("error", message, fields));
}
public fatal(message: string, fields?: Fields): void {
console.error(this.marshal("fatal", message, fields));
}
public setRequestId(requestId: string): void {
this.requestId = requestId;
}
}
class Payments extends RpcTarget {
logger: Logger;
constructor(logger: Logger) {
super();
this.logger = logger;
}
}
class Payments extends RpcTarget {
logger: Logger;
constructor(logger: Logger) {
super();
this.logger = logger;
}
}
const paymentService = await c.env.PAYMENTS.newPaymentService(() => services.logger);
const paymentService = await c.env.PAYMENTS.newPaymentService(() => services.logger);
Is
Logger
extending RpcTarget
too? If not, that might be your issueIt does not extend RpcTarget
Yeah, try making it extend
RpcTarget
, and see if it works