VeryGoodOpenSource / dart_frog

A fast, minimalistic backend framework for Dart 🎯
https://dartfrog.vgv.dev
MIT License
1.86k stars 148 forks source link

feat: Dart API Client Generation #151

Open felangel opened 2 years ago

felangel commented 2 years ago

Description

As a developer, I want to be able to autogenerate a Dart API Client library based on my Dart Frog application code so that I can seamlessly integrate with my Dart Frog backend without having to manually write an API client layer.

Requirements

jakub-stefaniak commented 2 years ago

Will solution be simmilar to something what serverpod is working on?

// Client that can connect to the pod.
var client = Client (‘https://myapi.com/’);

// Calling generated client code.
var result = await client.example.hello(‘World’);

// ‘result’ will be ‘Hello World’.
GabrielRozendo commented 2 years ago

other idea to checkout: https://pub.dev/packages/mid

mid is a tool to build an end-to-end typesafe API in dart. The tool generates an API server and a client library in addition to handling requests and managing communication between the server and the client.

https://pub.dev/packages/mid#motivation

Motivation

To have the ability to call the backend code from the frontend in a type safe manner and as simple as calling a function in pure Dart.

Note: mid is not intended to generate a REST API, but to generate an API server that can be seamlessly used by a Dart or Flutter frontend with a minimal effort.

Luckey-Elijah commented 2 years ago

Will solution be simmilar to something what serverpod is working on?

// Client that can connect to the pod.
var client = Client (‘https://myapi.com/’);

// Calling generated client code.
var result = await client.example.hello(‘World’);

// ‘result’ will be ‘Hello World’.

To chime in, an API like this would be interesting, but for accessing/using those endpoints, I would like to specify the request method type when calling the generated code:

var getResult = await client.example.hello.get(...);
var postResult = await client.example.hello.post(...);

One things that would be very cool would to have the generated client only generate those methods for the supported methods. This would probably be next to impossible to achieve with out some type of static knowledge provided by the developer.

For example, I want to generate the client endpoint that would only be able to execute GET and POST requests on the the example/hello route (the snippet from above). I could provide some hint to the client generator that those are the only two methods the API supports. One could decorate the onRequest method to convey that information

@FrogClientSupport(['GET', 'POST']); // optional, will generate for all possible methods if not specified.
FutureOr<Response> onRequest(RequestContext context) async {
  final method = context.request.method;
  if (method == HttpMethod.get) return _get(context);
  else if (method == HttpMethod.post) return _post(context);
  return Response(statusCode: HttpStatus.methodNotAllowed); // for non-generated clients
}

And the resulting resulting client should never generate methods that are not in the decorator

// The method 'put' isn't defined for the type 'DartFrogClient'.
// Try correcting the name to the name of an existing method, or defining a method named 'put'.
var putResult = await client.example.hello.put(...);

Hopefully that makes sense — let me know your thoughts!

GabrielRozendo commented 2 years ago

I think that decoration (or anything else) would be necessary anyway to generate the API Docs #152 too...

chimon2000 commented 1 year ago

I like the convention-based concept of route handlers in Next.js 13 coupled with the codegen influences of Serverpod.

FutureOr<Response> get(request: Request) async {}
FutureOr<Response> post(request: Request, String name) async {}

In the application, the client-generated API would resolve to

var getResult = await client.example.hello.get(...);
var putResult = await client.example.hello.put(...);
felangel commented 1 year ago

I like the convention-based concept of route handlers in Next.js 13 coupled with the codegen influences of Serverpod.

FutureOr<Response> get(request: Request) async {}
FutureOr<Response> post(request: Request, String name) async {}

In the application, the client-generated API would resolve to

var getResult = await client.example.hello.get(...);
var putResult = await client.example.hello.put(...);

Fwiw that’s how the client generation POC worked and it was what the team was leaning towards since it requires no additional metadata/annotations.

chimon2000 commented 1 year ago

I like the convention-based concept of route handlers in Next.js 13 coupled with the codegen influences of Serverpod.

FutureOr<Response> get(request: Request) async {}
FutureOr<Response> post(request: Request, String name) async {}

In the application, the client-generated API would resolve to

var getResult = await client.example.hello.get(...);
var putResult = await client.example.hello.put(...);

Fwiw that’s how the client generation POC worked and it was what the team was leaning towards since it requires no additional metadata/annotations.

Makes sense! It also seems like this pattern would play nice with OpenAPI documentation and use in non-dart projects, since it produces a "normal" REST api.

easazade commented 1 year ago

I have some experience creating a dart API client code generator in this project (fantom). Is there a plan for this? I would very much like to contribute to this @felangel @renancaraujo

easazade commented 1 year ago

I like the convention-based concept of route handlers in Next.js 13 coupled with the codegen influences of Serverpod.

FutureOr<Response> get(request: Request) async {}
FutureOr<Response> post(request: Request, String name) async {}

In the application, the client-generated API would resolve to

var getResult = await client.example.hello.get(...);
var putResult = await client.example.hello.put(...);

Fwiw that’s how the client generation POC worked and it was what the team was leaning towards since it requires no additional metadata/annotations.

Just to be clear. so to avoid adding annotations there are going to be breaking changes in the way that requests are handled in each route based on above concept? meaning instead of having a single onRequest for each route there can be request handler functions for each HTTP methods.

@felangel