Coder Social home page Coder Social logo

async-graphql / async-graphql Goto Github PK

View Code? Open in Web Editor NEW
3.2K 30.0 421.0 12.74 MB

A GraphQL server library implemented in Rust

Home Page: https://async-graphql.github.io/

License: Apache License 2.0

Rust 99.84% Handlebars 0.16%
rust-lang rust-crate async-await graphql server rust hacktoberfest

async-graphql's Issues

Upload panics

I get the following error when trying to upload a .png:
panicked at 'byte index 19 is not a char boundary; it is inside '|' (bytes 18..19) of `test.png:image/png|PNG`

And this is my form data:

Content-Disposition: form-data; name="operations"

{"operationName":"upload","variables":{"file":null},"query":"mutation upload($file: Upload!) {\n  upload(file: $file)\n}\n"}
------WebKitFormBoundaryF5RA2VMgDmAIU1OT
Content-Disposition: form-data; name="map"

{"1":["variables.file"]}
------WebKitFormBoundaryF5RA2VMgDmAIU1OT
Content-Disposition: form-data; name="1"; filename="test.png"
Content-Type: image/png


------WebKitFormBoundaryF5RA2VMgDmAIU1OT--

Use rustdoc comments as graphql desc?

I am looking to replace juniper with this library and I was looking through the docs and it looks like all the examples use proc macro attributes to add a description. Is it currently (or planned) possible to parse rustdoc comments for the graphql description?

All fields are exported by default

Export all fields on Object, SimpleObject, InputObject, Subscription by default, and #[field(skip)] is used to disable this feature.

  • Object
  • SimpleObject
  • InputObject
  • Subscription

Implement conversion from serde_json::Value to Any

I would like to implement impl From serde-json::Value for Any which is currently blocked by graphql-parsers inability to convert from serde_json::Value due to mismatch of underlying types for Number. Once this Issue resolves I will implement the conversion for Any.

DataSource macro hides errors

While using the #[DataSource] macro, any errors inside the implementation of the trait are marked on the trait itself (and in some weird cases, hoisted to line 1 of main).

I looked into the macro and found it is just adding #[async_trait::async_trait] so I used that directly and the errors are highlighted correctly in my code.

Is it possible to pass session data (or other actix middleware data) into the GraphQL execution context?

First, awesome work on this library! I started using it with actix as Juniper doesn't support subscriptions well and so far everything has worked great. But I've recently run into an issue:

Is it possible to pass actix-web session data into the execution context of a GraphQL resolver? I would like to use this to authenticate certain queries against a session token. Poor pseudocode for what I'm looking to accomplish would be:

impl QueryRoot {
    #[field]
    async fn mydata(&self, ctx: &Context<'_>) -> Option<Vec<MyData>> {
        // the line below is what I would like to accomplish. how to get session data in here?
        let token = ctx.data::<Session>().get("token");
        // then validation is easy. I have the db from Schema::build(..).data(...)
        let db = ctx.data::<Database>();
        match validate(token, db) {
            Some(result) => Some(db.get_all().await),
            None => None
        }
    }
}

In Juniper, it seems like you can extend the Context to do something similar. For example, this issue led to the suggestion: https://github.com/davidpdrsn/graphql-app-example/blob/master/src/graphql.rs#L20

Any ideas how to best accomplish this here? I'd be happy to document it with an example if someone could help me figure it out. Ideally, it would be nice to use actix-session or something similar.

Interfaces don't support FieldResult type

It seems that Interfaces don't support the FieldResult type. This might be a scoping issue in the macro expansion?

Here is an example:

use async_graphql::FieldResult;

struct MyObj;

#[async_graphql::Object]
impl MyObj {
    #[field]
    async fn value_a(&self) -> FieldResult<i32> {
        Ok(1)
    }
}

#[async_graphql::Interface(field(name = "value_a", type = "FieldResult<i32>"))]
struct InterfaceA(MyObj);

Provide example with global stream for subscription

Now that subscription handlers return a stream. What is the most idiomatic way for subscribers sharing a stream as opposed to each subscriber starting a new stream? Also is there an idiomatic way of implementing something like the publish method to emit to subscribers from a mutation handler directly?
I can think of two solutions:

  1. Use lazy_static or
  2. Store a channel on the SubscriptionRoot

Is there a better solution I am not aware of? I find it quite hard to migrate my code to 1.6.7 in a clean fashion.
If you could point me in the right direction I could extend the examples

Provide prelude

I think it is time to export the most important types and traits in a prelude to increase usability.

Access to request in schema

How to pass some context to schema with web::Json(req.into_inner().execute(&s).await) ? like a token from request

Examples for inputValueType.

I need to build a query endpoint that would support something like this:

pub struct Criteria {
    pub value: HashMap<String, Vec<Criterion>>,
}
impl QueryRoot {
async fn records(&self, criteria: Criteria, start: i64, skip: i64, order: SortCriteria) 
}

I'm trying to figure out a way to do this without manually implementing InputValueType. I'm sure it's possible, but I couldn't find a relevant example.

Support Upload Stream

The Upload type should provide a stream of bytes instead of a Vec<u8> leaving it to the user to decide what to do with it eg. stream it into a tempfile. I think loading the file contents into memory is a big performance and security issue.

Alternatively let the user define a location for uploads (eg. /tmp) and stream the content into files at this location and pass handles to these files to the user.

The second solution might be easier to integrate into the existing codebase although as a library user I would much prefer the first solution, giving me more control (eg. the opportunity to cancel requests early on before loading the contents / if not enough disk space is available / dynamic filesize checks / streaming to 3rd party APIs etc.)

How to access a future through parking_lot guard

I have a repro here:

https://github.com/dusty-phillips/async-graphql/blob/parking_lot_repro/examples/parking_lot.rs

In short, I'm using the parking_lot crate to get an RwLock. I've put the lock inside an Arc, and tried to access it from both a query and a mutation. However, I'm getting the following compile errors:

master* $ cargo run --example parking_lot
   Compiling async-graphql v0.10.8 (/home/dusty/beanstalk/async-graphql)
error: future cannot be sent between threads safely
  --> examples/parking_lot.rs:26:1
   |
26 | #[Object]
   | ^^^^^^^^^ future returned by `__resolve_field` is not `Send`
   |
   = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()`
note: future is not `Send` as this value is used across an await
  --> examples/parking_lot.rs:30:9
   |
30 |         self.thing.read().get_the_value().await
   |         -----------------^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         await occurs here, with `self.thing.read()` maybe used later
   |         has type `lock_api::rwlock::RwLockReadGuard<'_, parking_lot::raw_rwlock::RawRwLock, Thing>`
31 |     }
   |     - `self.thing.read()` is later dropped here
   = note: required for the cast to the object type `dyn std::future::Future<Output = std::result::Result<serde_json::value::Value, anyhow::Error>> + std::marker::Send`

error: future cannot be sent between threads safely
  --> examples/parking_lot.rs:38:1
   |
38 | #[Object]
   | ^^^^^^^^^ future returned by `__resolve_field` is not `Send`
   |
   = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()`
note: future is not `Send` as this value is used across an await
  --> examples/parking_lot.rs:42:9
   |
42 |         self.thing.write().set_the_value(thing_value).await;
   |         ------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- `self.thing.write()` is later dropped here
   |         |
   |         await occurs here, with `self.thing.write()` maybe used later
   |         has type `lock_api::rwlock::RwLockWriteGuard<'_, parking_lot::raw_rwlock::RawRwLock, Thing>`
   = note: required for the cast to the object type `dyn std::future::Future<Output = std::result::Result<serde_json::value::Value, anyhow::Error>> + std::marker::Send`

error: aborting due to 2 previous errors

error: could not compile `async-graphql`.

To learn more, run the command again with --verbose.

I've tried moving the guard out, but it doesn't seem to work. Intuitively, I can imagine it might not be possible to hold a lock across an await, but there is no compile error when I try to access it in the main() function (line 72) I don't get a compile error there. So the issue seems to be something in the Object macro, but I don't know how to debug. Any suggestions?

Multiple interfaces?

First, I wanted to say thank you for this amazing project! ๐Ÿ™‡

I'm currently implementing a Relay client against my async-graphql server and they require everything to implement the Node { id } interface for every object.

If I implement Node for every interface, I'm not sure how to implement another interface on the same object. Have you considered this for the #[async_graphql::Interface] macro?

How to solve the N+1 problem?

Hey there. Nice project! I like it especially because it addresses features like query complexity and uploading files. But what about the N+1 problem? Are there any plans in this direction?

And a second question I want to put here. Is there a way to pass the request object to the async_graphql::Object implementation? I think it would be pretty helpful sometimes (For example to read a cookie header).

Add "flatten" attribute to field

As discussed in #28, it would be nice to have a flatten attribute on fields. Note: This does not seem to be possible as of now! This issue is just a reminder to implement it when it becomes possible. Here is a very basic example of what we want to accomplish:

#[SimpleObject]
struct Info {
    #[field]
    base_field: String
}


#[SimpleObject]
struct MyInfo {
    #[field(flatten)] // <<<-------------------------
   info: Info,

    #[field]  
    additional_field: String,
}

pub struct QueryRoot;

impl QueryRoot {
    async fn info(&self) -> MyInfo {
        unimplemented!()
    } 
}

Instead of getting this:

{
    info {
        info {
            baseField
        }
        additionalField
    }
}

You would be getting this:

{
    info {
         baseField,
         additionalField
    }

Dynamic schema

Async-graphql lacks an important feature that most graphql implementations do not. Support both static and dynamic schema, and only with dynamic schema can write something like hasaru.

I have some preliminary ideas about the implementation of this feature. Do you think this feature is important?

@nicolaiunrein @phated

Accessing context within subscriptions

Thanks again for the great library and the fast response to my question yesterday! I now have a similar question around subscriptions. How do we pass data into the GraphQL resolver for a subscription? Here is pseudocode for what I would like to do:

pub async fn index_ws(
    schema: web::Data<MySchema>,
    req: HttpRequest,
    payload: web::Payload,
    session: Session,
    db: web::Data<database::Database>
) -> Result<HttpResponse> {
    let user: Option<User> = user_from_session(db.users.clone(), db.sessions.clone(), session).await;
    // something like ".data(...)" to extend context does not seem to exist
    ws::start_with_protocols(WSSubscription::new(&schema).data(user), &["graphql-ws"], &req, payload)
}

And then:

#[async_graphql::Subscription]
impl SubscriptionRoot {
    #[field]
    async fn mydata(&self, ctx: &Context<'_>, mutation_type: Option<MutationType>) -> impl Stream<Item = DataChanged> {
        let current_user = ctx.data::<Option<User>>();
        // logic here, for example use current_user.id to filter a stream ...
    }
}

Is this possible? I reviewed the subscription example and didn't see anything. Looking over the implementation of the library, I also didn't see any way to add additional data to a subscription context, or access the context.

Accept a SimpleObject as input type.

Currently if I want to define a struct with a bunch of fields as an InputType I would also have to define the impl for it and all the fields there, which makes the amount of boilerplate unbearable.

I'd like to be able to simply define a struct and use it's fields as input fields.

Serde alias gets ignored on Enums

I ran into the following bug:

use serde::Deserialize;
  
#[async_graphql::Enum]
#[derive(Deserialize)]
enum Test {
  #[serde(alias = "Other")]
    Real,
  }
  
#[derive(Deserialize)]
struct TestStruct {
  value: Test,
}
  
fn main() {
  serde_json::from_str::<TestStruct>(r#"{ "value" : "Other" }"#).unwrap();
}

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("unknown variant `Other`, expected `Real`", line: 1, column: 19)', src/libcore/result.rs:1188:5

Without the async_graphql::Enum annotation everything is fine.

What happened to `async_graphql_actix_web::HandlerBuilder`?

I was about to upgrade from 1.7.8 to 1.9.3 when I realised that quite a lot has changed. So I looked at the examples and codebase but I could not find any info on how I would transition the below code to 1.9.3. Where do I specify max_file_size and temp_dir?

    HttpServer::new(move || {
        let schema = Schema::build(QueryRoot, MutationRoot, SubscriptionRoot)
            .extension(|| ApolloTracing::default())
            .finish();

        let handler = async_graphql_actix_web::HandlerBuilder::new(schema)
            .enable_ui("http://localhost:3001", Some("ws://localhost:3001"))
            .max_file_size(100_000_000_000) //100 GB
            .enable_subscription()
            .build();

        App::new()
            .wrap(Logger::default())
            .wrap(Cors::new().finish())
            .service(web::resource("/").to(handler))
    })
    .bind(format!("0.0.0.0:{}", 3001))
    .unwrap()
    .run()

Field guard

Add the guard attribute in #[field] as a precondition to resolve the field.

#field(guard(RequireAuth(role = ADMIN)))

Interface doesn't work with SimpleObject?

I was trying to implement an Interface for my structs annotated with SimpleObject and it fails with field, not a method. Is it possible to support Interfaces using SimpleObjects?

Here's an example:

#[async_graphql::SimpleObject]
pub struct MyObj {
  #[field]
  pub id: i32,
  #[field]
  pub title: String,
};

#[async_graphql::Interface(
  field(name = "id", type = "i32"),
)]
pub struct Node(MyObj);

Is it possible to specify a mutable self on a query?

I want to do something like this:

#[Object(desc = "Information about the client's account")]
impl Client {
    #[field(desc = "The client's current balance")]
    async fn balance(&mut self) -> u64 {
        self.client.write().balance()
    }
}

Where the balance function needs to accept self mutably. But I'm getting this error:

   --> src/graphql.rs:151:1
    |
151 | #[Object(desc = "Information about the client's account")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | |
    | `_self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
    | help: consider changing this to be a mutable reference: `&mut graphql::Client`

Is there a way to do this without resorting to interior mutability?

Support the @defer directive in query

Since the stable version of rust does not support generic specialization, this is a bit of a hassle to implement. I'm doing it in other ways, and I'm not sure if I can finish it.

add more test

  • Derive
    • Object
    • Enum
    • InputObject
    • Interface
  • Types
    • Scalar
      • Integer
      • Float
      • String
      • Bool
      • ID
      • DateTime
      • UUID
    • Containers
      • List
      • Non-Null
    • Object
      • Generic Types
      • Lifetime cycle
    • Enum
    • InputObject
      • Field default value
      • Deprecated flag
    • Interface
    • Union
  • Query
    • Fields
    • Arguments
      • Default value
      • Deprecated flag
    • Alias
    • Fragments
    • Inline fragments
    • Operation name
    • Variables
      • Default value
      • Parse value
    • Directives
      • @include
        • FIELD
        • FRAGMENT_SPREAD
        • INLINE_FRAGMENT
      • @Skip
        • FIELD
        • FRAGMENT_SPREAD
        • INLINE_FRAGMENT
    • Schema
  • Validation rules

Add ability to forward field arguments to guard

I'm thinking of something like this:

struct UserGuard {
    id: ID
}

#[async_trait::async_trait]
impl Guard for UserGuard {
    async fn check(&self, ctx: &Context<'_>) -> FieldResult<()> {
        if let Some(current_user) = ctx.data_opt::<User>() {
            if current_user.is_admin() {
                return OK(())
            }

            if current_user.id == self.id {
                return Ok(());
            }
        }

        Err("Forbidden".into())
    }
}

#[async_graphql::Object]
impl QueryRoot {
    #[field(guard(UserGuard(id = "@id")))]
    async fn user(&self, ctx: &Context<'_>, id: ID) -> FieldResult<User> {
        // ...
    }
}

This would allow for more granular ACL implementation at the field guard level.

Remove type safety from Interface

The type safety of the interface causes some functionality to be unavailable. I'll remove the compile-time type safety of the interface and check it when I call Schema::new. It doesn't have any performance loss, but there is no guarantee that a compile-time type safety, I think it is worth to do so.

Defining fields in traits

I just thought it would be really neat to move some common fields from my concrete impl blocks to a shared trait. I can think of a lot of situations where this might be handy:

  • DRYing code
  • Composing types
  • Ensuring consistency
  • Libraries exposing GQL endpoints for user defined types etc.

So Is this possible?

InputObject not validating or i`m missing something

Using the actix subscriptions example, adding validations to InputObject fails to validate

use actix_web::{web, App, HttpServer};
use async_graphql::validators::StringMinLength;
use async_graphql::{publish, Context, InputObject, Result, Schema, ID};
use futures::lock::Mutex;
use slab::Slab;
use std::sync::Arc;

#[derive(Clone)]
struct Book {
    id: ID,
    name: String,
    author: String,
}

#[InputObject]
struct CreateBookInput {
    #[field(
        desc = "name of the book",
        validator(StringMinLength(length = 5))
    )]
    name: String,
    #[field(
        desc = "name of the author",
        validator(StringMinLength(length = 5))
    )]
    author: String,
}

#[async_graphql::Object]
impl Book {
    #[field]
    async fn id(&self) -> &str {
        &self.id
    }

    #[field]
    async fn name(&self) -> &str {
        &self.name
    }

    #[field]
    async fn author(&self) -> &str {
        &self.author
    }
}

type Storage = Arc<Mutex<Slab<Book>>>;

struct QueryRoot;

#[async_graphql::Object(cache_control(max_age = 5))]
impl QueryRoot {
    #[field]
    async fn books(&self, ctx: &Context<'_>) -> Vec<Book> {
        let books = ctx.data::<Storage>().lock().await;
        books.iter().map(|(_, book)| book).cloned().collect()
    }
}

struct MutationRoot;

#[async_graphql::Object]
impl MutationRoot {
    #[field]
    async fn create_book_input(
        &self,
        ctx: &Context<'_>,
        input: CreateBookInput,
    ) -> ID {
        let mut books = ctx.data::<Storage>().lock().await;
        let entry = books.vacant_entry();
        let id: ID = entry.key().into();
        let book = Book {
            id: id.clone(),
            name: input.name,
            author: input.author,
        };
        entry.insert(book);
        publish(BookChanged {
            mutation_type: MutationType::Created,
            id: id.clone(),
        })
        .await;
        id
    }

    #[field]
    async fn create_book_args(
        &self,
        ctx: &Context<'_>,
        #[arg(validator(StringMinLength(length = 5)))] name: String,
        #[arg(validator(StringMinLength(length = 5)))] author: String,
    ) -> ID {
        let mut books = ctx.data::<Storage>().lock().await;
        let entry = books.vacant_entry();
        let id: ID = entry.key().into();
        let book = Book {
            id: id.clone(),
            name: name,
            author: author,
        };
        entry.insert(book);
        publish(BookChanged {
            mutation_type: MutationType::Created,
            id: id.clone(),
        })
        .await;
        id
    }
}

#[async_graphql::Enum]
enum MutationType {
    Created,
    Deleted,
}

struct BookChanged {
    mutation_type: MutationType,
    id: ID,
}

#[async_graphql::Object]
impl BookChanged {
    #[field]
    async fn mutation_type(&self) -> &MutationType {
        &self.mutation_type
    }

    #[field]
    async fn id(&self) -> &ID {
        &self.id
    }
}

struct SubscriptionRoot;

#[async_graphql::Subscription]
impl SubscriptionRoot {
    #[field]
    fn books(
        &self,
        changed: &BookChanged,
        mutation_type: Option<MutationType>,
    ) -> bool {
        if let Some(mutation_type) = mutation_type {
            return changed.mutation_type == mutation_type;
        }
        true
    }
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
        let schema = Schema::build(QueryRoot, MutationRoot, SubscriptionRoot)
            .data(Storage::default())
            .finish();
        let handler = async_graphql_actix_web::HandlerBuilder::new(schema)
            .enable_ui("http://localhost:8000", Some("ws://localhost:8000"))
            .enable_subscription()
            .build();
        App::new().service(web::resource("/").to(handler))
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::dev::Service;
    use actix_web::{
        http::{header, StatusCode},
        test, web, App,
    };

    #[actix_rt::test]
    async fn test_validates_input_with_errors_fails() {
        let schema = Schema::build(QueryRoot, MutationRoot, SubscriptionRoot)
            .data(Storage::default())
            .finish();
        let handler = async_graphql_actix_web::HandlerBuilder::new(schema)
            .enable_subscription()
            .build();
        let mut app = test::init_service(
            App::new().service(web::resource("/").to(handler)),
        )
        .await;
        let req = test::TestRequest::post()
            .uri("/")
            .header(header::CONTENT_TYPE, "application/json")
            .set_payload(r#"{"operationName":null,"variables":{},"query":"mutation { createBookInput(input: {name: \"foo\", author: \"bar\"})}"}"#)
            .to_request();
        let res = app.call(req).await.unwrap();
        assert_eq!(res.status(), StatusCode::BAD_REQUEST);
    }

    #[actix_rt::test]
    async fn test_validates_arguments_with_errors() {
        let schema = Schema::build(QueryRoot, MutationRoot, SubscriptionRoot)
            .data(Storage::default())
            .finish();
        let handler = async_graphql_actix_web::HandlerBuilder::new(schema)
            .enable_subscription()
            .build();
        let mut app = test::init_service(
            App::new().service(web::resource("/").to(handler)),
        )
        .await;
        let req = test::TestRequest::post()
            .uri("/")
            .header(header::CONTENT_TYPE, "application/json")
            .set_payload(r#"{"operationName":null,"variables":{},"query":"mutation { createBookArgs(author: \"foo\", name: \"bar\")}"}"#)
            .to_request();
        let res = app.call(req).await.unwrap();
        assert_eq!(res.status(), StatusCode::BAD_REQUEST);
        let response_body = match res.response().body().as_ref() {
            Some(actix_web::body::Body::Bytes(bytes)) => bytes,
            _ => panic!("Response error"),
        };
        assert_eq!(
            response_body,
            r#"Invalid value for argument "author", The value length is 3, but the length must be greater than or equal to 5
Invalid value for argument "name", The value length is 3, but the length must be greater than or equal to 5
"#
        );
    }
}

Support Error Extensions

Hey,
first of all, I really enjoy using your library so far. I think it is already very comprehensive. One thing I am missing though is returning extensions for errors as described here in the spec. Something like:

  "errors": [
    {
      "message": "Name for character with ID 1002 could not be fetched.",
      "locations": [ { "line": 6, "column": 7 } ],
      "path": [ "hero", "heroFriends", 1, "name" ],
      "extensions": {
        "code": "CAN_NOT_FETCH_BY_ID",
        "timestamp": "Fri Feb 9 14:33:09 UTC 2018"
      }
    }
  ]
}

In terms of api, I think it would be nice to have a trait with a single method with_extensions which you could implement on your errors. Something like:

// somewhere in async-graphql
pub trait ErrorExtensions {
    fn with_extensions(&self) -> Option<serde_json::Value> {
        None
    }
}

impl<T: Into<Error>> ErrorExtensions for T {}
// somewhere in users code...
impl ErrorExtensions for MyError {
  fn with_extensions(&self) -> Option<serde_json::Value> {

    let extensions = serde_json::json!({
       "code": self.code.to_string(),
       "timestamp": get_current_timestamp()
    });

    Some(extensions)
  }
}

I am happy to contribute, so if you want any help just let me know.

Undocumented feature: all fields expands to FieldResult

Hello,
I'm not sure if this is realted to the recent changes on FieldResult but I've just noticed that we can use ? in field resolvers even if they don't return a FieldResult:

#[async_graphql::Object]
impl QueryRoot {
    #[field]
    async fn user(&self, ctx: &Context<'_>, id: ID) -> models::User {
        let id = ObjectId::with_string(&id.to_string())?;
        ctx.data::<Loaders>().user.load(id).await?
    }
}

It feel a bit odd, but is also very convenient and makes error management easier. The documentation doesn't seems to mention this at the moment, is this a feature we can rely on or is it subject to change in the future ?

Cheers

Look into approaching federation.

https://www.apollographql.com/blog/apollo-federation-f260cf525d21 This here is a very nice tool, also to help load-balancing as when the schema is split between services - it's easier to have several different entrypoints with different pools of servers.

Even if one does want to code a monolith - this is easily achievable by having features in the source code and just running the same service with different features enabled.

But using the node library is suboptimal, so I've been thinking this might actually be worth implementing in Rust. It's not really an issue for that repo, but something to chew on. I'll be looking into prototyping this sometime soon.

The ability to collect fields from both SimpleObject and Object.

Here's an example: I have an object that has 10 fields, only one of which would need to be defined as a custom Scalar. But in order to expose this object's fields I would have to actually create a getter for each of it's fields. Or otherwise I'd have to use a gql scalar type within my business logic and the database layer, which would also be somewhat painful.

Fragments on subscribtions nevers push data to client

Hello,
When using fragments in subscriptions, I never receive any notifications in the websocket

This works:

subscription s {
  order(id: "5eabbe77103ca80276efdf70") {
    id
    createdAt
  }
}

This doesn't:

subscription s {
  order(id: "5eabbe77103ca80276efdf70") {
    id
    ...orderFrag
  }
}

fragment orderFrag on Order {
  id
  createdAt
}

tested with both playground and an apollo client

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.