Comments (21)
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.
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.
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.
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.
I am currently not using a shelf plugin for the caching, but the cors plugin could be used with the adapters.
from dart_frog.
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.
To solve caching I had to do a workaround before the response is read due to these issues:
- dart-lang/shelf#9
- https://stackoverflow.com/questions/29759260/dart-shelf-how-to-inspect-a-request-in-a-middleware-without-reading-it
- https://stackoverflow.com/questions/26253389/how-to-create-add-middleware-that-adds-default-headers-to-each-request
from dart_frog.
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.
@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.
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.
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.
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.
I think being able to use shelf plugins will go a long way. But i think being able to add a
_handler.dart
would appendaddHandler
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.
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.
So the issue Is how
addMiddleware
vsaddHandler
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.
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.
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.
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.
This is now available in dart_frog v0.0.1-dev.9
🎉
from dart_frog.
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.
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)
- fix: Test cases failing in dart_frog_cli while running dart test(OS errors)
- fix: need clarity around static files HOT 6
- chore: Dart Frog CLI 1.2.0 release
- max number of websockets HOT 3
- fix: Concurrency Issues HOT 2
- CI / CD Workflow Examples HOT 2
- Dart frog support Isolates ? HOT 2
- docs: Add Globe to Deployment section in Docs
- How to inject variables into context in a certain route HOT 4
- feat: allow $ in `new` commands
- fix: `dart_frog build` fails with "name" field doesn't match expected name HOT 1
- feat: Scheduled Tasks / Cron Jobs HOT 1
- feat: add global exception catching HOT 6
- fix: Error parsing request HOT 2
- fix: zsh: command not found: dart_frog HOT 4
- chore: Release for Dart Frog CLI and Package HOT 1
- fix: When deploying to globe getting issue as: A directory corresponding to fileSystemPath "public" could not be found HOT 5
- docs: an overview document for dart_frog_cli
- fix: dart_frog dev -H HOT 1
- fix: ClientException with SocketException: Connection refused (OS Error: Connection refused, errno = 61), address = 127.0.0.1, port = 57735, uri=http://127.0.0.1:57715/status HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dart_frog.