Code Monkey home page Code Monkey logo

Comments (21)

felangel avatar felangel commented on May 27, 2024 1

Hey @rodydavis 👋
Thanks for opening an issue!

Can you provide a bit more context regarding what your ideal usage would look like? We were planning to expose the adapters as part of the public API but were waiting for a concrete use case first to ensure we had a good developer experience. Thanks!

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024 1

Thinking in the future of making a package to auto generate a rest api from a sql instance for example (https://www.graphql-mesh.com/docs/handlers/mysql).

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024 1

So here is an example for _middleware.dart:

import 'package:dart_frog/dart_frog.dart';
import 'package:shelf_cors_headers/shelf_cors_headers.dart';

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

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024 1

There is also no way to extend the Pipeline that is generated in bin/server.dart:

Handler buildRootHandler() {
  final pipeline = const Pipeline().addMiddleware(routes_middleware.middleware);
  ...
}

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024 1

I am currently not using a shelf plugin for the caching, but the cors plugin could be used with the adapters.

from dart_frog.

felangel avatar felangel commented on May 27, 2024 1

Sorry for appending on this kinda-old issue. I think it would be nice to include the cors() middleware in dart_frog itself. What do you think @felangel ?

We'd prefer to keep the dart_frog core slim (only contain functionality/APIs required by all applications). There are existing packages that help with adding cors headers such as https://pub.dev/packages/shelf_helmet. Hope that helps 👍

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

To solve caching I had to do a workaround before the response is read due to these issues:

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

Currently it is impossible to modify a request body before it is returned since with shelf you can only read it once.

Adding Cors was possible by a copyWith modification on the response but I still needed to custom write an adapter to deploy for cloud run. Ideally I could use the shelf cors package on pub.dev.

Another use case is caching an expensive database request and I had to hack around it with a custom response handler per request, rather than a cache middleware / handler at the top level.

In general just wanting a bit more control of the pipeline object that is generated.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

@rodydavis can you share how you intend to use shelf_adapters once it's publicly exported? I just want to make sure I fully understand what your desired usage would look like. If you're able to provide some snippets illustrating your desired usage that would be amazing, thanks again! 🙏

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

It would open all existing shelf packages up to be used with dart_frog, plus adding features like being able to modify request bodies.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

There is also no way to extend the Pipeline that is generated in bin/server.dart:

Handler buildRootHandler() {
  final pipeline = const Pipeline().addMiddleware(routes_middleware.middleware);
  ...
}

Yeah this is the part I'm curious about. Are there any specific solutions that you had in mind? We could allow developers to create a server.dart and define their own root handler but it's not obvious how that would interact with the generated handler. We'd need to define things like order of execution/precedence and make sure the proposed solution is both flexible and intuitive. Also, do you have any concrete use-cases for needing to extend the pipeline? Adding middleware can already be achieved.

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

I think being able to use shelf plugins will go a long way.

I think just being able to add a _handler.dart would append addHandler to the pipeline before it goes to the routes.

Really just needed since you want to check if you can handle the route earlier in the pipeline.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

I think being able to use shelf plugins will go a long way. But i think being able to add a _handler.dart would append addHandler to the pipeline before it goes to the routes.

Really just needed since you want to check if you can handle the route earlier in the pipeline.

Yeah totally agree! We recently added Cascade support so you should be able to add handlers in middleware like:

Handler middleware(Handler handler) {
  return Cascade()
    .add(myCustomHandler)
    .add(handler).handler;
}

In conjunction with exposing the shelf adapters, do you think that would address the use-cases you had in mind?

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

So the issue Is how addMiddleware vs addHandler works in the pipeline.

You can only read the response body once per handler in shelf.

The above example would work for middleware only use cases. But it does solve the verbose nature of adding them currently.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

So the issue Is how addMiddleware vs addHandler works in the pipeline.

You can only read the response body once per handler in shelf.

The above example would work for middleware only use cases. But it does solve the verbose nature of adding them currently.

Can you share a simple example for which the above approach would not work? Just want to make sure I fully understand your concerns, thanks!

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

So the example it breaks down is adding a caching handler, where you want to check if you have a cached version of the response before hitting the database.

Express for example:
https://www.npmjs.com/package/express-cache-middleware

The issue is shelf only lets you read the response body a single time per handler and for middleware it throws an exception when it is passed to the routes.

Using a handler in the pipeline you can return early in the pipeline if the cache is found.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

So the example it breaks down is adding a caching handler, where you want to check if you have a cached version of the response before hitting the database.

Express for example: https://www.npmjs.com/package/express-cache-middleware

The issue is shelf only lets you read the response body a single time per handler and for middleware it throws an exception when it is passed to the routes.

Using a handler in the pipeline you can return early in the pipeline if the cache is found.

Got it, thanks for sharing! Do you happen to have a working version of this using shelf that you can share?

from dart_frog.

rodydavis avatar rodydavis commented on May 27, 2024

I created an extension method:

// ignore_for_file: public_member_api_docs, cascade_invocations

import 'dart:convert';
import 'dart:io';

import 'package:dart_frog/dart_frog.dart';

Middleware cache() {
  return (handler) {
    return (context) async {
      final uri = context.request.uri;
      final local = uri.cached();
      if (local != null) return local;
      return handler(context);
    };
  };
}

extension UriUtils on Uri {
  String toFileName() {
    final sb = StringBuffer(pathSegments.join('/'));
    final params = queryParameters.entries.toList();
    if (params.isNotEmpty) {
      sb.write('_');
    }
    for (final entry in params) {
      sb.write('${entry.key}_${entry.value}');
    }
    return sb.toString();
  }

  File get bodyFile => File('.cache/${toFileName()}/body.json');
  File get headersFile => File('.cache/${toFileName()}/headers.json');

  Response? cached() {
    final bf = bodyFile;
    final hf = headersFile;
    if (bf.existsSync() && hf.existsSync()) {
      final bs = bf.readAsStringSync().trim();
      if (bs.validJson()) {
        final bodyJson = jsonDecode(bs) as Map<String, dynamic>;
        final headersString = hf.readAsStringSync();
        final headersJson = jsonDecode(headersString) as Map<String, dynamic>;
        final headers = headersJson.addCorsHeaders();
        return Response.json(
          body: bodyJson,
          headers: headers,
        );
      }
    }
    return null;
  }
}

Cors utils:

// ignore_for_file: public_member_api_docs

import 'package:dart_frog/dart_frog.dart';

Middleware cors() {
  return (handler) {
    return (context) async {
      if (context.request.method == HttpMethod.options) {
        return Response(headers: {'Access-Control-Allow-Origin': '*'});
      }
      final response = await handler(context);
      final headers = response.headers.addCorsHeaders();
      return response.copyWith(headers: headers);
    };
  };
}

extension MapUtils on Map<String, dynamic> {
  Map<String, String> addCorsHeaders() {
    final headers = Map<String, String>.from(this);
    headers['Access-Control-Allow-Origin'] = '*';
    headers['Access-Control-Allow-Methods'] = 'GET, POST, DELETE, OPTIONS';
    headers['Access-Control-Allow-Headers'] = '*';
    return headers;
  }
}

In the _middleware.dart:

import 'package:dart_frog/dart_frog.dart';

Handler middleware(Handler handler) =>
    handler.use(cors()).use(cache()).use(requestLogger());
}

And in the route:

import 'package:dart_frog/dart_frog.dart';

Future<Response> onRequest(RequestContext context) {
     final db = await connect();
     final items = await db.query('SELECT * FROM table'); // <-- Expensive DB query
     return cached(items);
}

With this code I can call the DB once and following requests will hit the cache.

I can also invalidate the cache on UPDATE and POST calls too.

from dart_frog.

felangel avatar felangel commented on May 27, 2024

This is now available in dart_frog v0.0.1-dev.9 🎉

from dart_frog.

enquestor avatar enquestor commented on May 27, 2024

Sorry for appending on this kinda-old issue.
I think it would be nice to include the cors() middleware in dart_frog itself. What do you think @felangel ?

from dart_frog.

enquestor avatar enquestor commented on May 27, 2024

Sorry for appending on this kinda-old issue. I think it would be nice to include the cors() middleware in dart_frog itself. What do you think @felangel ?

We'd prefer to keep the dart_frog core slim (only contain functionality/APIs required by all applications). There are existing packages that help with adding cors headers such as https://pub.dev/packages/shelf_helmet. Hope that helps 👍

How about adding some info to the docs about CORS handling? I could make a PR if you're down 🙂

from dart_frog.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.