shelf_api_builder 1.0.0+1 copy "shelf_api_builder: ^1.0.0+1" to clipboard
shelf_api_builder: ^1.0.0+1 copied to clipboard

A code generator to create RESTful API endpoints to be integrated with shelf.

shelf_api #

CI/CD for shelf_api_builder Pub Version

A code generator to create RESTful API endpoints to be integrated with shelf.

Table of contents #

Table of contents generated with markdown-toc

Features #

  • Code generation to build a shelf based REST API
  • Supports various body types (text, binary, streams and JSON)
  • Supports custom path and query parameters (all string-parsable types)
  • Supports use of custom type converters
  • Automatic de/serialization of JSON types
  • Generates dio based API client for easy consumption.
  • Provides various middlewares via shelf_api for convenient use:
    • rivershelf to integrate riverpod with shelf.
    • handleFormatExceptions to generate badRequest responses from FormatExceptions

Installation #

As this is a code generation package, you need to install the following packages. In addition to the shelf_api packages themselves you will also have to add a few other packages that will be referenced by the generated code:

dependencies:
  # Code annotation package
  shelf_api: <latest>
  # Required API packages (if generateApi is enabled - defaults to true)
  shelf: <latest>
  shelf_router: <latest>
  # Required client packages (if generateClient is enabled - defaults to true)
  dio: <latest>

dev_dependencies:
  build_runner: <latest>
  shelf_api_builder: <latest>

Usage #

Using the package consists of three simple steps: First, you have to declare one more Endpoints. Endpoints are basically REST-Controllers, that can serve multiple routes. These Endpoints are then combined to a single API class, which is used by the generator to create the shelf handler from the Endpoints as well as the Dio client wrapper. Finally, you will have to mount the handler as part of your shelf server setup.

Note: You can check out the

Example to get a full example that showcases most of the features.

1. Creating Endpoints #

Endpoint definitions are fairly straight forward:

import 'package:shelf_api/shelf_api.dart';

/// Optional annotation, if left out, no path will be prefixed
@ApiEndpoint('/basic')
class ExampleEndpoint extends ShelfEndpoint {
  ExampleEndpoint(super.request);

  /// @Get marks this method as "GET" endpoint for "/basic/"
  /// Named parameters are mapped to URL query parameters
  /// Invoking "GET /basic?name=Car" would respond with "Hello, Car!"
  @Get('/')
  String get({String name = 'World'}) => 'Hello, $name!';

  /// You can also have path parameters, a body and asynchronous returns
  @Post('/comments/<topic>')
  Future<Comment> postComment(String topic, @bodyParam Comment comment) async {
    /// If the rivershelf middleware is activated, you can access a ref here:
    final myService = ref.read(myServiceProvider);

    // ...
  }

  /// and much, much more - Check out the example!
}

2. Building an API #

Simply declare a placeholder class somewhere and add the endpoints to it:

@ShelfApi(
  /// List all endpoints this API should serve
  [
    ExampleEndpoint,
  ],
  /// Optional base path for all Endpoints in this API
  basePath: '/api/v1/',
)
// ignore: unused_element
class _ExampleApi {}

This will generate two files. Both are standalone libraries and not part files:

  • example_api.api.dart: Contains the shelf handler class named ExampleApi
  • example_api.client.dart: Contains the dio client wrapper named ExampleApiClient

3. Using the API #

To create a basic server, simply create a shelf pipeline ad serve it:

void main() async {
  final app = const Pipeline()
      .addMiddleware(handleFormatExceptions()) /// Recommended to gracefully handle JSON errors
      .addMiddleware(logRequests()) /// If you use this, it must come AFTER the handleFormatExceptions
      .addMiddleware(rivershelf()) /// Required if you want to be able to access a ref in you endpoints
      .addHandler(ExampleApi().call); /// Required. Registers the actual API as handler

  final server = await serve(app, 'localhost', 8080);
  print('Serving at http://${server.address.host}:${server.port}');
}

Generator Configuration #

The generator supports the following configuration options

Key Type Default Value Description
generateApi bool true If set to true (the default), the Shelf API handler code will be generated
generateClient bool true If set to true (the default), the Dio client code will be generated

Note: Checkout https://pub.dev/packages/build_config for more details on how to configure builders. If you disable api or client code generation, you can safely remove the unneeded dependencies (as declared in the Installation section).

Taking advantage of the riverpod integration #

While the integration itself is straightforward - simply access the ref property in you endpoints, there are a few usage hints to get the most out of it.

Understanding scoping of providers #

First, one has to understand how scoping of services work. By default, all request providers are managed globally. This means keep-alive providers are the same for all requests. Auto disposable providers are always kept alive for at least as long as the request is still processing, but will be cleaned up afterwards. However, that does not make the transient. If two requests want to access an auto disposable provider at the same time, they will get the same instance.

To get a unique instance for every request, you can use the shelfRequestProvider as manual dependency. Doing so will ensure that the provider will be unique for the request and will be cleaned up afterwards, regardless of whether it is kept alive or not:

@Riverpod(dependencies: [shelfRequest])
User requestUser(RequestUserRef ref) {
  /// You do not have to use the shelfRequestProvider here. Declaring it as
  /// dependency is enough to make it work.
  /// However, if you are using it to access the original request, you MUST
  /// add the dependency as well!
  // ...
}

Please note that there is no watch method on the the request ref. This is because the concept of watch only applies to something that can be rebuild, like a widget. Thus, it makes no sense in this context. Currently, listen is not supported as well, because requests should be short living and not watch for changes on a provider.

Accessing providers outside of endpoints #

Under the hood, the middleware is independent from Endpoints. This means you can access providers in any shelf handler (or middleware), as long as the rivershelf middleware has been registered before. To do so, you can simply use the ref extension on the request:

Future<Response> myHandler(Request request) async {
  final myService = request.ref.read(myServiceProvider)
}

Documentation #

API docs #

The documentation for the annotations and helper classes is available at https://pub.dev/documentation/shelf_api/latest/.

A full example of the code generator can be found at https://pub.dev/packages/shelf_api_builder/example. An example that displays the riverpod integration is available at https://pub.dev/packages/shelf_api/example

Using the package in combination with dart_frog #

It is possible to easily integrate this generate APIs and middlewares with dart_frog by using their shelf wrapper helper functions fromShelfHandler and fromShelfMiddleware. For example, if you would like to use the ExampleApi from before

/// in the _middleware.dart, add the following

Handler middleware(Handler handler) {
  return handler.use(fromShelfMiddleware(rivershelf()));
}

/// in your route.dart, wrap the api as such:
final onRequest = fromShelfHandler(ExampleApi().call);
1
likes
0
points
66
downloads

Publisher

verified publisherskycoder42.de

Weekly Downloads

A code generator to create RESTful API endpoints to be integrated with shelf.

Repository (GitHub)
View/report issues

Topics

#server #shelf #dio #rest #api

License

unknown (license)

Dependencies

analyzer, build, build_config, code_builder, meta, path, shelf, shelf_api, source_gen, source_helper

More

Packages that depend on shelf_api_builder