Giter VIP home page Giter VIP logo

rescript-apollo-client's Introduction

rescript-apollo-client's People

Contributors

arnarkari93 avatar dfalling avatar gtdev87 avatar idkjs avatar illusionalsagacity avatar jasoons avatar jeddeloh avatar jfrolich avatar jovanbaj avatar maarekj avatar mariahmartinez avatar tienle avatar yousleepwhen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rescript-apollo-client's Issues

Incompatibility/Bug with @apollo/client > 3.5.0

apollographql/apollo-client@91945a1#diff-fb7fa652de6d84c08b87344a06a4d120b664c6e8bf491d16575794c755141477R356

A change was made in @apollo/client > 3.5 that means the query property in the options object will override the first argument and because rescript encodes query: undefined in that options object there's a runtime error. Technically I think the responsibility lies with apollo here, but humans probably wouldn't write useQuery(documentNode, { query: undefined }) either.

The easiest way to fix this looks like replacing query: None with query: Some(Operation.query), but would also be fixed by removing the query property entirely from the options type definitions. I'd be happy to do either just let me know what you'd prefer.

Also affects useMutation, useLazyQuery, etc

Passing explicit Null as Mutation parameter

I have a mutation that updates an entity. When passing input parameters I want one of the fields to be sent as Null. However, when I try to running something like this:

        updateEntity({
           id: id
           exampleField: None
        })

This is the variable object I find in the network request

operationName: "UpdateEntityField"
query: "mutation UpdateEntity($id: ID!, $field: String)
variables: {id: "exampleid"}

While I wanted to send something along these lines

operationName: "UpdateEntityField"
query: "mutation UpdateEntity($id: ID!, $field: String)
variables: {id: "exampleid",  field: null}

I found a workaround for this which passes Js.Nullable.null->Obj.magic instead of None. I know this violent usage of Js.Nullable.null is not the nicest way to handle this issue, but as we are passing this straight to the Javascript world I don't think this can cause any issues down the line. I think it would be better if we could introduce a better way to handle something like this and make it type-safe.

Local State Management

Here's what I think is still needed for a good experience here:

  • Add makeVar bindings
  • Add types for TypePolicies if it hasn't already been added by this point (it's currently just an abstract type)
  • Add examples

pollInterval None should cancel polling?

Hi folks
I have been using react state to gate pollInterval on some queries, as some tasks progress on the back end. It took me a while to figure that None does not cancel a set interval. Should it? It seems like it should?

Seems likely a one liner. I can take a look if youre interested in a PR.

Thanks
Alex

Configuration

This is just a proposal, but there are three things I would love to be able to do:

  • Configure the type for context across the library (see this comment)
  • Use reason-promise or another library by default, but allow people to opt out or provide their own promise transformation function
  • Allow people to customize the behavior of parse. Right now it throws an exception, but maybe you'd like do something else—return a variant, log before throwing, etc.

Right now, turning all the file modules into functors seems like the only way to achieve this, but it's probably worth thinking about more.

Move rescript-apollo-client off the reasonml-community org

Hey @jeddeloh!

As discussed in this forum post, we'd like to move active ReScript projects to their respective maintainer's github org to free up the reasonml-community org.

Would it be viable to move the repo to your personal / company github org? That way it's easier to tell if a project is actively maintained, especially if it is backed by a company.

You can then explicitly add contributors later on to keep collaboration going!

Let me know what you think!

cheers

Nullable field being discarded from mutation

I have a nullable field that I'm trying to change from a value to null. The query simply drops the field, rather than passing the new null value. How can I make Apollo send a null value rather than just omitting the field? I've hunted in the Apollo docs and don't see anything, so I'm hoping there are some rescript tricks I'm missing.

Thanks!

Use Js.Nullable.t

This is more a question than an issue per se (sorry, I didn't know where to ask this), but is it possible to use Js.Nullable.t with ApolloClient.query?
I'm asking this because I'm using it with Next.js and wanted to do some SSR. Since getServerSideProps converts the props to JSON before sending them to React, I'm getting errors:

SerializableError: Error serializing `.projects._0[0].description` returned from `getServerSideProps` in "/projects".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value all together.

Converting all of the optionals to Js.Nullable.t worked but, since they are generated as Nullables by graphql-ppx, I would like to know if there's any way to access them in this library.

Thank you very much!

Link headers

Hello.
I have issue with HttpLink headers.

Using this example from the documentation, I created a link to my GraphQL endpoint.

let httpLink = ApolloClient.Link.HttpLink.make(
  ~uri=_ => Env.graphqlEndpoint,
  ~headers=Obj.magic({"Accept": "application/json"}),
  (),
)

I expect the Accept header to be exactly application/json, but */* is automatically added to the value.
image

This is a bug? If not, how can this be fixed?

Thank you!

Runtime error with ApolloClient.Utilities.getOperationDefinition function

Hello

Once again many thanks for your great bindings. 👏🥳

I am currently experiencing a bug in the use of the ApolloClient.Utilities.getOperationDefinition function.

let terminatingLink = ApolloClient.Link.split(~test=({query}) =>
  switch ApolloClient.Utilities.getOperationDefinition(query) {
  | Some({kind, operation, name: {value}}) =>
    kind === "OperationDefinition" &&
    operation === "query" &&
    value->Js.String2.startsWith("Analytics")
  | _ => false
  }
, ~whenTrue=httpAnalyticsLink, ~whenFalse=httpGatewayLink)

It creates an error if I have a graphql query that takes this form (no operation name).

query ($id: ID!) {
  user(id: $id) { ... }
}

In this cas, the name property is undefined. So it is not possible to access value property.
So I applied this patch ⬇️

diff --git a/node_modules/rescript-apollo-client/src/graphql/language/ApolloClient__Graphql_Language_Ast.res b/node_modules/rescript-apollo-client/src/graphql/language/ApolloClient__Graphql_Language_Ast.res
index 1c007fa..80f26c6 100644
--- a/node_modules/rescript-apollo-client/src/graphql/language/ApolloClient__Graphql_Language_Ast.res
+++ b/node_modules/rescript-apollo-client/src/graphql/language/ApolloClient__Graphql_Language_Ast.res
@@ -172,7 +172,7 @@ module OperationDefinitionNode = {
   type t = {
     kind: string,
     loc: option<Location.t>,
-    name: NameNode.t,
+    name: option<NameNode.t>,
     operation: OperationTypeNode.t,
     variableDefinitions: option<array<VariableDefinitionNode.t>>,
     directives: option<array<DirectiveNode.t>>,

And change that ⬇️

let terminatingLink = ApolloClient.Link.split(~test=({query}) =>
  switch ApolloClient.Utilities.getOperationDefinition(query) {
-  | Some({kind, operation, name: {value}}) =>
+  | Some({kind, operation, name: Some({value})}) =>
    kind === "OperationDefinition" &&
    operation === "query" &&
    value->Js.String2.startsWith("Analytics")
  | _ => false
  }
, ~whenTrue=httpAnalyticsLink, ~whenFalse=httpGatewayLink)

Do you need a pull request?

Greetings from France 🇫🇷

Léo

Add "extensions" to GraphQLError type

The GraphQL error type allows for an extensions field, which is an arbitrary key/value object. Apollo leverages this to provide an error code: https://www.apollographql.com/docs/apollo-server/data/errors/#codes.

I'm not entirely sure what the correct type should be here. Apollo provides some pre-defined errors in apollo-server, but you can extend the ApolloError class to define other errors specific to your domain (which I've done in the app I'm using this for). One option might be to allow for passing your own GraphQLError and/or extensions type in the config, which you could define to contain the specific errors your app needs to handle? Aside from that, at least providing the extensions field which contains a code field typed as a string would be something.

Using custom ws implementation

Hi,

Actually, I'm trying to use this module on the ground of Gatsby. I faced the one problem which is the error in process of building the static page. I guess the ws is not existing in node env during building Gatsby static page.

image

So, I tried to add ~webSocketImpl, but no gain.

Can you give me a tip to use a custom ws implementation? using JS modulews or bs-ws which is binding to ws.

reference
apollographql/subscriptions-transport-ws#191

Read variables from within the `useMutation`'s `update` function

I just had a situation where it would be useful to read variables passed to a mutation from its update function. The official TypeScript definitions state:

export type MutationUpdaterFunction<
  TData,
  TVariables,
  TContext,
  TCache extends ApolloCache<any>
> = (
  cache: TCache,
  result: Omit<FetchResult<TData>, 'context'>,
  options: {
    context?: TContext,
    variables?: TVariables,
  },
) => void;

https://github.com/apollographql/apollo-client/blob/main/src/core/types.ts#L170-L182

My use case is that I wanted to create a hook that uses MySharedMutation.use() under the hood, passing it the update function, so that users of that custom hook do not need to think how to update cache for that specific mutation.

let useMySharedMutation = MySharedMutation.use(~update=updateLogic)
/// later, somewhere else...
useMySharedMutation(/* options, without the need to specify `update` logic*/ { /*variables*/ })

Do you think it makes sense to add options param to ReScript bindings as well? I'm asking because it would be a breaking change in the sense that all users of the update param would need to update their functions passed to update to include the third param ~options.

Change React Hook names

Right now for both queries and mutation, there is use function generated. I'm wondering if it would be possible to change it to useQuery for queries and useMutation for mutations. It would make it more readable if this module is a query or mutation.

Usage without React?

You can use @apollo/client without react with @apollo/client/core.

This lib binds to @apollo/client. Would it be crazy to make this work with @apollo/client/core?

Is that a whole different setup? I tried just changing the binding to @apollo/client/core for the stuff exported from core and it did not work so..

Thanks you, sir.

Calling Delete Mutation

I am messing with the demo and created this file from the Mutation.re example:

module Cache = ApolloClient.Cache;

module DeleteTodoMutation = [%graphql
  {|
    mutation DeleteTodo ($id:String!){
      deleteTodoItem(input: { id: $id }) {
        deletedTodoItemId
        clientMutationId
      }
    }
  |}
];

When I call this as is it compiles and fails. To make it work, it has to be

module Cache = ApolloClient.Cache;

module DeleteTodoMutation = [%graphql
  {|
    mutation DeleteTodo ($id:ID!){
      deleteTodoItem(input: { id: $id }) {
        deletedTodoItemId
        clientMutationId
      }
    }
  |}
];

Is this expected? Figured the typing was supposed to catch that. Working example is in #37

MockedProvider bindings

How would we approach writing bindings for MockedProvider? The mocks prop is so dynamic and I am struggling where to begin. Has anybody thought about this?

Bug in ensureApolloError

Since the commit 8508e94 there is a bug in function ensureApolloError

This function is declared like this:

  let ensureApolloError: t => t = error =>
    %bs.raw(`
          function (error, makeApolloError) {
            var error = error || {};
            if (Array.isArray(error.graphQLErrors)) {
              return error;
            } else if (error && typeof error.message === "string" && error.extensions) {
              return makeApolloError({graphQLErrors: [error]});
            } else {
              return makeApolloError({networkError: ensureError(error)}) 
            }
          }
        `)(error, make, ensureError)

The raw function takes 2 parameters in input, and it is called with 3 parameters.

I think in fix it, we need to do this:

 let ensureApolloError: t => t = error =>
   %bs.raw(`
         function (error, makeApolloError, ensureError) {
           var error = error || {};
           if (Array.isArray(error.graphQLErrors)) {
             return error;
           } else if (error && typeof error.message === "string" && error.extensions) {
             return makeApolloError({graphQLErrors: [error]});
           } else {
             return makeApolloError({networkError: ensureError(error)}) 
           }
         }
       `)(error, make, ensureError)

Change graphql tag after build

Hi, first of all thanks for awesome library, really enjoy it by far.

I have question is there a way to modify output of graphql query?
I really want to remove any require statements from my .bs.js file.
Right now my query look like this:

/* Me_Query.re */
[%graphql {|
  query Me {
      me {
        firstName
        id
        lastName
        ...
      }
  }
|}];

and builded file like this

/* Me_Query.bs.js */

import * as ApolloClient__React_Hooks_UseQuery from "reason-apollo-client/src/@apollo/client/react/hooks/ApolloClient__React_Hooks_UseQuery.bs.js";

var Raw = {};

var query = (require("@apollo/client").gql`
  query Me  {
    me  {
      __typename
      firstName
      id
      lastName
    }
  }
`)

Is there any way I can from
require("@apollo/client").gql

change it to something like this:

import {gql} from 'graphql-tag'
var query = gql...

Thanks in advance.

Tests

Sadly no tests yet. Being a bindings library, it doesn't feel to me like tests using mocks will provide a lot of value. It probably needs to be fully end-to-end. I really like this idea to package up a simple Apollo server: https://github.com/zth/graphql-client-example-server and use that.

fetchMore executing request multiple times

Hey there, first I'd like to thank everybody working on this wonderful library! Combined to GraphQL PPX, it's delightful.

I ran into the following issue: fetchMore executes a request multiple times (twice on my project even on simple requests not implying variables or specific stuff and four times on your own example: https://github.com/reasonml-community/rescript-apollo-client/blob/master/EXAMPLES/src/hooksUsage/Query_Typical.res#L35).
Have you ever seen that?

Thanks for reading!

"fetch" not defined when using Jest tests

Hi There,

I'm using a client (non-react/non-hook) query to run tests in jest and got the following error:

Invariant Violation: 
    "fetch" has not been found globally and no fetcher has been configured.

I saw that Apollo in javascript has an argument to directly pass the fetch function:

The same argument doesn't seem to exist as a make() argument for ApolloClient.

let instance = {
  open ApolloClient
  make(
    ~cache=Cache.InMemoryCache.make(),
    ~connectToDevTools=false,
    ~uri=_ => graphql_api_address ++ "/graphql"
    // should fetch argument be supported here?
    (),
  )
}

Is there a workaround to be able to use Apollo in jest tests?

Undefined network status causes runtime errors

Hello,

I just encountered a runtime error with the graphql-ppx use hook, it appears that in a certain case when the ~skip argument is passed as true the NetworkStatus gets undefined.

Here's how I fixed it, I suggest these changes:

src/@apollo/client/core/ApolloClient__Core_NetworkStatus.re

module NetworkStatus = {
  module Js_ = {
-    type t = int;
+    type t = option(int);
  };
  [@bs.deriving jsConverter]
    | [@bs.as 7] Ready
    | [@bs.as 8] Error;

-  let toJs: t => Js_.t = tToJs;
+  let toJs: t => Js_.t = (t: t) => Some(tToJs(t));

-  let fromJs: Js_.t => t = string => tFromJs(string)->Belt.Option.getExn;
+  let fromJs: Js_.t => t =
+    optionalString => {
+      switch (optionalString) {
+      | Some(string) => tFromJs(string)->Belt.Option.getExn
+      | None => Ready
+      };
+    };
};

src/@apollo/client/core/ApolloClient__Core_Types.re

    data,
    error,
    loading: js.loading,
-   networkStatus: js.networkStatus->NetworkStatus.fromJs,
+   networkStatus: Some(js.networkStatus)->NetworkStatus.fromJs,
  };
};

Thanks for the great contribution!

Useing ApolloClient.useMutation()

I am trying to use onCompleted on useMutation. Maybe this is equivalent to some other method in the library?

This compiles but doesnt not look right and cause an invalid hook call error.

  let loginFn = () => {
    let variables: LoginUser.t_variables = {email: "[email protected]"};
    let _ =
      ApolloClient.React.useMutation(
        ~mutation=(module LoginUser),
        ~onCompleted=
          ({login}) => {
            Js.log2("TOKEN_LOGIN", login);
            let token =
              login.token
              ->Belt.Option.map(t => t)
              ->Belt.Option.getWithDefault("");
            Dom.Storage.(localStorage |> setItem("token", token));
            Dom.Storage.(localStorage |> setItem("userId", login.id));
          },
        LoginUser.serializeVariables(variables)->ignore,
        // variables
      )->ignore;
    ();
  };

error:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.`

Being called like this since its the only way i can get it to compile:

module LoginUser = [%graphql
  {|
  mutation LoginUser($email: String!) {
    login(email: $email) {
      id
      token
    }
  }
|}
];
[@react.component]
let make = () => {
  let loginFn = () => {
    let variables: LoginUser.t_variables = {email: "[email protected]"};
    let _ =
      ApolloClient.React.useMutation(
        ~mutation=(module LoginUser),
        ~onCompleted=
          ({login}) => {
            Js.log2("TOKEN_LOGIN", login);
            let token =
              login.token
              ->Belt.Option.map(t => t)
              ->Belt.Option.getWithDefault("");
            Dom.Storage.(localStorage |> setItem("token", token));
            Dom.Storage.(localStorage |> setItem("userId", login.id));
          },
        variables->ignore,
      )->ignore;
    ();
  };
  <div>
    <p>
      <button onClick={_e => loginFn()}>
        "ApolloClient.React.useMutation"->React.string
      </button>
    </p>
  </div>;
};

Thank you in advance!
Super work here!

Also, how is the above function different from the following which works but doesnt have onCompleted as an option?

let logLogin = _ =>
  Client.instance.mutate(~mutation=(module LoginUser), {email: "[email protected]"})
  ->Promise.get(
      fun
      | {data: Some({login}), error: None} => {
          Js.log2("TOKEN_LOGIN", login);
          let token =
            login.token
            ->Belt.Option.map(t => t)
            ->Belt.Option.getWithDefault("");

          Dom.Storage.(localStorage |> setItem("token", token));
          Dom.Storage.(localStorage |> setItem("userId", login.id));
        }
      | {error} => Js.log2("Error: ", error),
    );

Removing catch around parse function, and result types

There is some defensive programming in the client where there is an expectation that the parse function can fail. GraphQL is a protocol with certain guarantees. That means we can assume data shapes and that in turn means that the parse function would never raise an exception unless there is a bug in graphql-ppx.

Can we remove these checks?

Roadmap

This is a living document that is a product of our ever-evolving understanding and welcomes change. The primary purpose is to provide transparency, but also alignment and prioritization of individual efforts.

Current proposed roadmap:

  • More bindings for TypePolicies (see #18 and #19)
  • More configuration: promise transforms, context, etc. (ended up with a different compromise #49)
  • Documentation (see #20)
  • Tests (see #21)

Is there a way to bind graphql query responses to existing record types

Hi There,

I couldn't find if it's possible to bind to existing record types in hooks examples but have use a similar feature with reason-apollo-hooks.

In reason-apollo-hooks, you used to be able to annotate queries like this:

query myQuery {
  vectorType @bsRecord {
    x
    y
  }
}

This would prompt the compiler to look for a record type named vectorType and bind that to GraphQL responses.

Trying to do the same thing in rescript-apollo results in type errors when parsing GraphQL responses:

  This has type:
    array<
  MyQuery.MyQuery_inner.t_myQuery_Type_subtype,
>

  But somewhere wanted:
    array<ReactHooksTemplate.Type.subtype>

Is there another way to explicitly bind graphql queries to records types that exist in my application?

Subscription resends the same response

Hi There,

I'm using the hooks API for subscriptions in the following way:

let subscription = Queries.TraceSubscription.use({
    < variables here >
  },
  ~skip,
  ~fetchPolicy=NoCache
)

Then when I'm checking for changes, multiple updates occur with the same value:

React.useEffect1(() => {
  Js.log(subscription)
  // getting multiple "updates" with the same subscription value
}, [subscription])

Is this intended behavior?

How do I get a query definition as a string?

How do I get access to the query like it is requested to the server.

module TodosQuery = %graphql(`
  query TodosQuery {
    todos: allTodos {
      id
      text
      completed
    }
  }
`)

Js.log(TodoQuery.queryString)

expected output:
"query TodosQuery {\n todos: allTodos {\n id \n text \n completed \n } \n }"

@apollo/client 3.5 features

https://github.com/apollographql/apollo-client/blob/main/CHANGELOG.md#apollo-client-350-2021-11-08

  • DataProxy.updateFragment method
  • DataProxy.updateQuery method
  • MutationResult.reset method
  • useLazyQuery execute function returns a Promise with query result

I think if the minimum version gets bumped to ^3.5.0 that'd require a major version release? The useLazyQuery change certainly is breaking.

I'm noticing some commented out variables properties in src/@apollo/client/cache/core/types/ApolloClient__Cache_Core_Types_DataProxy.res, is this comment

// I think fragment variables are still experimental?

referring to graphql-ppx or apollo/client?

Unable to run the `EXAMPLE`

When I try run the example I get the following output:image

I tried this at various recent commits including latest master and the tagged branch for v0.0.1-alpha.2.

I'm using npm v12.18.2, and simply run yarn in the EXAMPLES directory then yarn start to get the error.

Thanks in advance

namaspace package in bsconfig

Awesome work you've done so far!!
Please consider nameapacing your package in bsconfig like:

  namespace: "Jeddeloh_ReasonApolloClient"

This could avoid potential naming collisions.

The value use can't be found in the module

I have the query

module UserProfileQuery = [%graphql
  {|
    query UserProfile($sub: String!) {
      users_by_pk(id:  $sub) {
        id
        last_seen
        created_at
        name
        picture
        email
        on_boarded
      }
    }
  |}
];

But UserProfileQuery does not have use function.

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.