medz / prisma-dart

Prisma Client Dart is an auto-generated type-safe ORM. It uses Prisma Engine as the data access layer and is as consistent as possible with the Prisma Client JS/TS APIs.
https://prisma.pub
BSD 3-Clause "New" or "Revised" License
436 stars 29 forks source link

Generated toJson() method not encoding DateTime and Enum types correctly for JSON responses #365

Closed atharvapalaskar closed 1 month ago

atharvapalaskar commented 1 month ago

Issue

The toJson() method generated for classes containing DateTime and Enum fields does not encode these fields correctly for HTTP JSON responses, resulting in encoding errors.

Error Details

Error with Enum

When attempting to encode an Enum field, the following error is thrown:

ERROR - 2024-05-28 13:04:13.656400
GET /users/BIHTcphhQPwPzto
Error thrown by handler.
Converting object to an encodable object failed: Instance of 'Role'
dart:convert                               jsonEncode
package:dart_frog/src/response.dart 70:32  new Response.json
routes/users/[id].dart 18:23               onRequest

Error with DateTime

When attempting to encode a DateTime field, the following error is thrown:

ERROR - 2024-05-28 13:26:45.489214
GET /users/BIHTcphhQPwPzto
Error thrown by handler.
Converting object to an encodable object failed: Instance of 'DateTime'
dart:convert                               jsonEncode
package:dart_frog/src/response.dart 70:32  new Response.json
routes/users/[id].dart 18:23               onRequest

Output of Generated Model Class

The current output of the generated toJson() method is as follows:

  Map<String, dynamic> toJson() => {
        ... 
        'role': role,  
        'created_at': createdAt,
        'updated_at': updatedAt,
        ...
      };

Expected Behavior

The toJson() method should correctly encode DateTime fields using toIso8601String() and Enum fields using their string representations.

The expected output of the generated toJson() method should be:

  Map<String, dynamic> toJson() => {
        ... 
        'role': role?.name,  
        'created_at': createdAt?.toIso8601String(),
        'updated_at': updatedAt?.toIso8601String(),       
         ...
      };

Steps to Reproduce Issue (Example)

1. Create Prisma schema with datasource Postgres along with User model:

model User{

  id    Int     @id @default(autoincrement())
  mobile  String  @unique  
  email   String?
  role  Role @default(USER)
  name  String?     
  created_at DateTime @default(now()) @db.Timestamptz(0)
  updated_at DateTime @default(now()) @db.Timestamptz(0)  

  @@map("users")
}

enum Role {
ADMIN
USER
}

2. Using dart_frog to Create a Server:

Example: A Route to Fetch User Data

Create route in the routes folder: users/[id].dart

Future<Response> onRequest(RequestContext context, String id) async {  
  switch (context.request.method) {
    case HttpMethod.get:
      final res = await UsersController.details(context, id);
      return Response.json(
        statusCode: res.status == ResStatus.success ? 200 : 400,
        body: res.toJson(),
      );
    default:
      return Response.json(statusCode: 405, body: Res(status: ResStatus.error, msg: 'Method not allowed'));
  }
}

controller

class UsersController {
  static Future<Res> details(RequestContext context, String userId) async {
    final db = context.read<Database>(); 
    final res = Res(status: ResStatus.error, msg: "Couldn't get user details");

    try {   
      final user = await db.db.user.findUniqueOrThrow( 
        where: UserWhereUniqueInput(userId: userId),
      );

      final data = user.toJson();

      return res
        ..status = ResStatus.success
        ..msg = 'User $userId details'
        ..data = data;
    } catch (e) {
      print(e);
    }

    return res;
  }
}

print of data (i.e user.toJson()) :

{id: 18, user_id: BIHTcphhQPwPzto, mobile: 1234567890, email: null, role: Role.user, name: null, created_at: 2024-05-08 16:00:52.000Z, updated_at: 2024-05-08 16:00:52.000Z}

Environment