Git Product home page Git Product logo

genql's Introduction





Type safe Graphql query builder

Write Graphql queries with type validation and auto completion



Important

Genql is building a cloud platform to automate SDK generation in multiple languages, docs website with examples, changelog website and more. Fill this form if you are interested!

Read the quick start guide to generate your client and start writing queries.

You can stay up to date with the latest changes subscribing to the Genql changelog.

Features

Example

First generate your client with the genql cli.

You can find other cli options here

npm i -D @genql/cli # cli to generate the client code
genql --schema ./schema.graphql --output ./generated

Then you can use your client as follow

import { createClient, everything } from './generated'
const client = createClient()

client
    .query({
        countries: {
            // pass arguments to the query
            __args: {
                filter: {
                    currency: {
                        eq: 'EUR',
                    },
                },
            },
            name: true,
            code: true,
            nestedField: {
                // fetch all scalar fields
                __scalar: true,
            },
        },
    })
    .then(console.log)

The code above will fetch the graphql query below

query {
    countries(filter: { currency: { eq: "EUR" } }) {
        name
        code
        nestedField {
            scalarField1
            scalarField2
        }
    }
}

Why

Genql has a lot of benefits over writing graphql queries by hand:

  • Writing queries is faster thanks to TypeScript auto completion
  • You can safely update your schema and be sure your queries are still valid
  • You can fetch all scalar fields in a type with __scalar: true
  • No graphql package dependency, no runtime parsing of queries
  • You have to generate the client only after your schema changes, not after every query change

Sponsors

Notaku

Notaku

Vercel


Licensed under MIT.

genql's People

Contributors

benjaminbradley avatar boredland avatar bump-version avatar danielrose avatar dhmacs avatar julianbenegas avatar julien-r44 avatar kjones1876 avatar remorses avatar ruheni 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  avatar  avatar

genql's Issues

Library fails typecheck

Hi there, I just bumped from v1 to "@genql/runtime": "^2.3.3" and "@genql/cli": "^2.3.3" and after generating types for my project, running a typecheck ("typescript": "^4.0.3") fails with this error:

node_modules/@genql/runtime/dist/client/typeSelection.d.ts:16:4 - error TS2344: Type 'Exclude<keyof DST, FieldsToRemove>' does not satisfy the constraint 'keyof SRC'.
  Type 'keyof DST' is not assignable to type 'keyof SRC'.
    Type 'string | number | symbol' is not assignable to type 'keyof SRC'.
      Type 'string' is not assignable to type 'keyof SRC'.
        Type 'keyof DST' is not assignable to type 'never'.
          Type 'string | number | symbol' is not assignable to type 'keyof SRC'.
            Type 'string' is not assignable to type 'keyof SRC'.
              Type 'string' is not assignable to type 'never'.
                Type 'Exclude<keyof DST, FieldsToRemove>' is not assignable to type 'never'.
                  Type 'keyof DST' is not assignable to type 'never'.
                    Type 'string | number | symbol' is not assignable to type 'never'.
                      Type 'string' is not assignable to type 'never'.
                        Type 'string | number | symbol' is not assignable to type 'never'.
                          Type 'string' is not assignable to type 'never'.

16 }, Exclude<keyof DST, FieldsToRemove>>;

As a workaround, I can set skipLibCheck to true in my tsconfig.json, but it would be great if I could eventually remove that option again :) Thanks in advance!

Bug - Can't get typing of raw query when the field is an array

If we take a look at this example:

const q = {
countries: {
name: true,
code: 1,
},
}
const { query, variables } = generateQueryOp(q)
const { data, error } = useQuery<QueryResult<typeof q>>(gql(query), {
variables,
})

then it looks like we get the typing by <QueryResult<typeof q>> which works great in this example.

But If we change the query to something like (according to https://genql.now.sh/docs/usage/usage-with-other-clients):

 const q = { 
     countries: [
        { argument: 'exampleArgument' },
        { 
           name: true, 
           code: 1, 
         }
     ] as const, 
 }

We get an an error:

The type 'readonly [{}, { readonly name: true; code: 1 }]' is 'readonly' and cannot be assigned to the mutable type ...

Is there a typescript flag that I should've changed?


A more specific error (based on my code):

image

Closed connections in a test suite

Hello,

I use your library for my test suite with jest.

Jest creates a process for each test and I have a memory leak and running processes.

I think that closing the GenQl client connection for each test could solve the problem.

Is there any way to close the client connection?

Thank you.

Support for GraphQL aliases take 2

I thought I had a use-case for #10 but then didn't. Still interested in having aliases supported though.

I can't remember right now but if we can make the types "extensible" (records with known fields) then we could treat ANY UNKNOWN key as being an alias. Its type would be a union of objects for each peer field, where each object accepts ONLY ONE.

{
  things: [ 
    {}, 
    {
      foo: true,
      foo2: { foo: true },
      bar2: { bar: { /* selection set */ } },
      qux2: { qux: [{/* args */},{ /* selection set */ }]}
    }
  ]
}

I can't remember right now but if we can make the types "extensible" (records with known fields) then we could treat ANY UNKNOWN key as being an alias.

If not, then, unfortunately:

{
  things: [ 
    {},
    {
      foo: true,
    }, 
    {      
      foo2: { foo: true },
      bar2: { bar: { /* selection set */ } },
      qux2: { qux: [{/* args */},{ /* selection set */ }]}
    }
  ]
}

I am pretty sure that what will happen is the record's known fields vs extensible ones will have to share the same type, and that is NOT what we want here. Thus, a third parameter would be needed.

False on select should mean exclude

Hey, great lib!

I have a suggestion: when there is __scalar used and for example id: false, it would be great if it would return everything except the id.

Great for testing.

Allow custom serializer/deserializer for custom scalars

Using the CLI option --scalar, it is possible to set the Typescript type of custom scalars. However, that only modifies the generated interfaces. The actual value of the scalar is unchanged.

For easier working with the scalar values after a query/subscription, they may need to be deserialized differently than the default. The same with a mutation: the scalars may need to be serialized differently than the default. While it is possible to do this manually, it would be much easier and less error-prone if this could be done by passing custom serializer/deserializer functions.

For example, a custom scalar could be "Date". The actual values will be transferred as strings. For easier usage, it would be much better if they were deserialized after a query into a Javascript Date or Luxon DateTime. For a mutation, the Date/DateTime would probably need to be serialized as string again (instead of hoping the default serialization will work).

Error: Must provide valid Document AST.

I don't see the problem.

Version:

    "genql-runtime": "^1.1.11",
    "genql-cli": "^1.1.11",

Schema:

### This file was generated by Nexus Schema
### Do not make changes to this file directly


type Query {
  users: [User!]
}

"""foobar"""
type User {
  id: ID
  name: String
}

Output:

show
❯ npm run generate:client                  

> [email protected] generate:client /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world
> genql --schema api.graphql --output client

[15:02:35] generating the client in `client` [started]
[15:02:35] loading schema [started]
[15:02:35] loading schema [failed]
[15:02:35] β†’ Must provide valid Document AST.
[15:02:35] preparing client directory [started]
[15:02:35] preparing client directory [completed]
[15:02:35] writing files [started]
[15:02:35] writing schema.graphql [started]
[15:02:35] writing schema.ts [started]
[15:02:35] writing guards.esm.js [started]
[15:02:35] writing guards.cjs.js [started]
[15:02:35] writing types.json [started]
[15:02:35] writing index.js [started]
[15:02:35] writing index.esm.js [started]
[15:02:35] writing index.d.ts [started]
[15:02:36] writing schema.graphql [failed]
[15:02:36] β†’ Cannot read property 'getDirectives' of undefined
[15:02:36] writing schema.ts [failed]
[15:02:36] β†’ Cannot read property 'getTypeMap' of undefined
[15:02:36] writing guards.esm.js [failed]
[15:02:36] β†’ Cannot read property 'getTypeMap' of undefined
[15:02:36] writing guards.cjs.js [failed]
[15:02:36] β†’ Cannot read property 'getTypeMap' of undefined
[15:02:36] writing types.json [failed]
[15:02:36] β†’ Cannot read property 'getTypeMap' of undefined
[15:02:36] writing index.d.ts [failed]
[15:02:36] β†’ Cannot read property 'getQueryType' of undefined
[15:02:36] writing files [failed]
[15:02:36] β†’ Cannot read property 'getDirectives' of undefined
[15:02:36] generating the client in `client` [failed]
ListrError: Something went wrong
    at /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/listr/index.js:102:18 {
  name: 'ListrError',
  errors: [
    Error: Must provide valid Document AST.
        at devAssert (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/node_modules/graphql/jsutils/devAssert.js:12:11)
        at Object.buildASTSchema (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/node_modules/graphql/utilities/buildASTSchema.js:42:94)
        at /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/schemaTask.js:43:37
        at Generator.next (<anonymous>)
        at fulfilled (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/schemaTask.js:5:58),
    TypeError: Cannot read property 'getDirectives' of undefined
        at printFilteredSchema (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/node_modules/graphql/utilities/printSchema.js:54:27)
        at Object.printSchema (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/node_modules/graphql/utilities/printSchema.js:40:10)
        at Object.exports.renderSchema (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/render/schema/renderSchema.js:6:32)
        at /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/clientTasks.js:44:32
        at Generator.next (<anonymous>)
        at /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/clientTasks.js:8:71
        at new Promise (<anonymous>)
        at __awaiter (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/clientTasks.js:4:12)
        at Task.task (/Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/genql-cli/dist/tasks/clientTasks.js:42:28)
        at /Users/jasonkuhrt/projects/graphql-nexus/examples/hello-world/node_modules/listr/lib/task.js:167:30 {
      context: [Object: null prototype] {}
    }
  ],
  context: [Object: null prototype] {}
}

Can I add a header after client is created?

I use this code to add a header to my query.

const { createClient } = require("./generated");

const authorization = "";
const client = createClient({
    url: "https:/example.com/graphql",
    headers: { authorization },
});

But I need to add the header after the client is created. Can it be done?

[RFC] Built in react client

To see an example possible client usage check out this code

Using genql with generic react fetching libraries like react-query and swr is not optimal:

  • the useQuery and useSwr functions use genetics to get the result types, this way typescripts loses auto completion for the request fields when you use an inline function (because of a cyclic generic inference, I thought my vscode was buggy but it behaves this way by design)
  • there is no clear way to make mutations like in apollo, where you can defer the mutation execution (when a button is clicked for example)
  • there is currently no way to handle subscription using hooks, (like useSubscription from urql does)

This is why I want to build a native react client based on swr, the exported functions will be similar to the apollo ones, but caching, polling, etc will be handles by swr

The exported functions will be

  • useQuery, tiny wrapper around useSwr
  • useMutation, does not cache results, can be deferred unit an execute function
  • useSubscription, similar to useSubscription from urql, gets a reducer argument that aggregates subsequent subscription results into an accumulator

This client will be generated when you add a --react flag, it will require an swr peer dependency

Two quick questions -- useLazyQuery() and variable binding

I am planning to port some old code from Zeus to Genql, but there are two things which I haven't been able to figure out how to mirror 1-to-1 from the example integrations you have in the Next.js project code.

If you're willing to take a second to answer if this is possible/roughly how, would really appreciate it πŸ™

  1. useLazyQuery for running query on event, and passing variables:
const [runLoginQuery, { data, loading, error }] = useTypedLazyQuery({
  adminLogin: [
    {
      params: {
        email: $`email`,
        password: $`password`,
        name: $`name`,
      },
    },
    {
      token: true,
      refreshToken: true,
    },
  ],
});

const submitForm = async (user) => {
  try {
    const { email, password } = user;
    await runLoginQuery({
      variables: {
        email,
        password,
      },
    });
  } catch (error) {
    console.log(error);
  }
};
  1. Equivalent to variable binding when initializing Apollo useMutation and useLazyQuery hooks:
// useTypedMutation is the same thing as UseGenqlMutation, it provides types to Apollo hooks
const [signupMutation, { data, loading, error }] = useTypedMutation({
  signup: [
    {
      params: {
        email: $`email`,
        password: $`password`,
        name: $`name`,
      },
    },
    {
      token: true,
      refreshToken: true,
    },
  ],
});

const submitForm = async (user) => {
  try {
    const { name, email, password } = user;
    const response = await signupMutation({
      variables: {
        name,
        email,
        password,
      },
    });
  } catch (error) {
    console.log(error);
  }
};

Thank you ❀️

Incorrect subscription payload type

Generated subscription types seem to be incorrect or runtime incorrectly passes data to subscribe callback:

subscriptionsClient
    .subscription({
      balance: {
        before: true,
        after: true,
      },
    })
    .subscribe((payload) => {
      console.log("balance", payload.balance); // TS suggests this
      console.log("balance", payload.data.balance); // this is actually how to access data
    });
genql-cli: ^1.0.34
genql-runtime: ^1.0.32

Selection set static type error

When extracting a selection set the inferred type is changed. This fails to type check but should work. Note how workspaces IS part of the selection set.

image

Now, if we inline the selection set, then we can access the .workspaces field on the result as expected:

image

Null instead of Undefined

This is the image field in Shema.
image

This is the image field which is generated by genql. It must be null, not undefined. Do you have an option to config it?

See also: graphql/graphql-js#1298 (comment)

image

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Now I have to use this function to convert null to undefined:

import traverse from 'traverse';

function nullToUndefined<D extends any>(data: D) {
  return traverse(data).map(function (value) {
    if (value === null) this.update(undefined);
  }) as D;
}

Alternative API ("syntax") for graphql argument passing

I was day-dreaming about a nicer way to pass graphql arguments in the API. I realized we could approach GraphQL syntax by leveraging function parameter defaults.

See the following as TypeScript Playground.

Here's the idea

v1 graphql args as tuple member 1 (current system)

query({
  getUser: [{ id: "1", deep: { a: { y: true, z: 1 } } }, {}],
})

query({
  getUser: [
    { id: "1", deep: { a: { y: true, z: 1 } } },
    {
      location: {
        city: [{ lang: "en" }],
      },
    },
  ],
})

query({
  getUser: [
    { id: "1", deep: { a: { y: true, z: 1 } } },
    {
      location: {
        city: true,
      },
    },
  ],
})

v2a graphql args as param defaults

query2({
  getUser(id = 1, deep = { a: { y: true, z: 1 } }) {},
})

query2({
  getUser: (id = 1, deep = { a: { y: true, z: 1 } }) => ({
    location: {
      city(lang = "en") {},
    },
  }),
})

query2({
  getUser: (id = 1, deep = { a: { y: true, z: 1 } }) => ({
    location: {
      city: true,
    },
  }),
})

v2b graphql args as default to args param

query({
  getUser(args = { id: 1, deep: { a: { y: true, z: 1 } } }) {},
})

query({
  getUser: (args = { id: 1, deep: { a: { y: true, z: 1 } } }) => ({
    location: {
      city(lang = "en") {},
    },
  }),
})

query({
  getUser: (args = { id: 1, deep: { a: { y: true, z: 1 } } }) => ({
    location: {
      city: true,
    },
  }),
})
// types that allow exploring the autocompletion etc. experience
// in practice kind of thing genql generates

type Deep = { a: { z: number; y: boolean } }

type schema = {
  getUser(
    id: number,
    deep: Deep,
    beta?: boolean
  ): {
    location?: {
      city?: ((lang: "en" | "fr") => void) | true
    }
  } | void
}

function query2(o: schema) {}

// placeholder

function query(...args: any[]): any {}

Notes:

  • I think the param-default variants are a lot more readable

  • writing default parameters get the same autocompletion experience as would passing arguments at a call site

  • in GraphQL argument order does not matter, but for v2a it would. I tried writing an overload with all possible combinations of a 3-arg field. The problem is for writing parameter defaults. It is not possible to get autocompletion/type-safety for parameter defaults of overloaded functions. So genql would need to pick an order. Any order. But an order. Perhaps matching the order as it is found in the SDL/introspection query would be the best option.

  • Another constraint in v2a would be nullable args would have to come after required ones.

  • In v2b this constraint is lifted, restoring the arg-giving ordering behaviour of GraphQL. The cost is that we're forced into one level of nesting at all times. And since a lot of GraphQL APIs use the input pattern, it means args with two levels of nesting often... maybe that's a fine tradeoff though! Also it reduces the amount of = needed for the developer to enter. And I'm sure that typing = in TS to supply values is going to go against typing muscle memory. So minimizing this is a good aspect.

  • another advantage of v2b is that it could allow genql to reserve other parameters for other uses. For example, aliases. In the following, city field is aliased to town:

    query({
      getUser: (args = { id: 1, deep: { a: { y: true, z: 1 } } }) => ({
        location: {
          city(args = { lang: "en" }, alias = "town") {},
        },
      }),
    })

Criticisms:

  • It will have some performance cost since functions need to be executed (but, a build step could transform these calls into an optimized version, perhaps like the genql API today, thus removing the cost)
  • when selecting scalars with args, it requires a trailing {} rather than just true which is ugly and confusing since it looks like the beginning of an object selection.
  • finally, this idea is kind of mad πŸ€·β€β™‚οΈ ... but I like it πŸ˜…

Generate object literals for enums

We're currently rolling out this pattern.

export * from '@genql/client'
import type { PlanetscaleRegion as PlanetscaleRegion_ } from '@genql/client'

export type PlanetscaleRegion = PlanetscaleRegion_

// TODO get object literals for enums from Genql core.
export const PlanetscaleRegion = {
  us_east: 'us_east',
  us_west: 'us_west',
  ap_northeast: 'ap_northeast',
  ap_south: 'ap_south',
  ap_southeast: 'ap_southeast',
  eu_west: 'eu_west',
} as const

This is so that frontend code can create enum members and more (such as create zod schema types based on the enum for frontend form validation).

It would be great to have Genql ship (generate) these object literals by default.

Feature request: Batching operations

GraphQL API supports batched operations (e.g. Hasura).

This would be really really useful (especially for large interactive apps). Apollo implements it via the batch http link.

I think it would be awesome if this was supported directly by genql as a config, but another viable solution could be to expose the fetcher.

on_<interface> for unions where _some_ members share an interface

Take a type like this where the errors all share an interface:

image

Note that Genql does not generate the expected on_ClientError because (presumably) Project in the union did not implementat that interface. However, GraphQL doesn't actually care about that.

Note, on_Project is missing here because its already supplied, off view from the screenshot.

image

Assuming the GraphQL Playground is being spec compliant here, notice how it correctly maintains the ability to select on ClientError:

image

Feature Request: Sort attributes in generated schema

Gidday @remorses, thanks for this product, loving it so far. I like to regularly regenerate and commit the artefacts generated by this tool, and sometimes it produces noisy diffs like this, which are effectively no-ops:

Screenshot 2020-08-11 at 14 00 59

If the order of the attributes of the schema were predictable (sorted alphabetically, for example) then these kinds of diffs would never occur. This sorting could be opt-in (or opt-out) by way of a CLI flag. What are your thoughts?

Per-request headers

It is possible to setup headers at construction time, but not possible to set headers for a particular request.

It is possible to work around this by wrapping Genql with a function that creates an instance anew for each request, but obviously not ideal.

type EnhancedGenqlClient = {
  mutation<R extends Genql.MutationRequest>(
    request: R & { __name?: string },
    config?: { headers?: Record<string, string> }
  ): Promise<GenqlRuntime.FieldsSelection<Genql.Mutation, R>>
  query<R extends Genql.QueryRequest>(
    request: R & { __name?: string },
    config?: { headers?: Record<string, string> }
  ): Promise<GenqlRuntime.FieldsSelection<Genql.Query, R>>
}
  const enhancedGenql: EnhancedGenqlClient = {
    mutation(selectionSet, config) {
      const genql = Genql.createClient({
        url: `http://localhost:${serverPort}/api/graphql`,
        headers: {
          Authorization: `Bearer ${token}`,
          ...config?.headers,
        },
      })
      return genql.mutation(selectionSet)
    },
    query(selectionSet, config) {
      const genql = Genql.createClient({
        url: `http://localhost:${serverPort}/api/graphql`,
        headers: {
          Authorization: `Bearer ${token}`,
          ...config?.headers,
        },
      })
      return genql.query(selectionSet)
    },
  }

Open to a pull-request for this?

Thanks!

Bug: Type collision

Hasura let's you specify big integers that are exposed on the GraphQL endpoint with type bigint.

Given that bigint is a reserved word in typescript (see here), the following generated code breaks:

export type bigint=any

It gives a Type alias name cannot be 'bigint' error.

Possible solution

To work around this scalars can be defined like GraphQL codegen does:

export type Scalars = {
  ID: string,
  String: string,
  Boolean: boolean,
  Int: number,
  Float: number,
  uuid: string,
  bigint: any,
};

So that they are used like this: Scalars["bigint"].

As a cool addition, you can make scalar types configurable e.g.

{
  uuid: "string",
}

So they are typed correctly and you don't loose type inference due to the use of any

Error handling with runtime fetcher

The "createFetcher" function swallows everything and return a string in case of an rest error.

throw new Error(`${res.statusText}: ${await res.text()}`)

For example. right now I can't reach the error status code for a requests the outputs an error.

I think that it would be better to just throw the entire Error and let the user handle it themself to not enforce this very narrow information output.

Suggestion - Get the query operation type while maintaining the autocompletion

This issue is about advanced typing - https://genql.now.sh/docs/usage/advanced-typings

Given the following code:

// based on the issue https://github.com/remorses/genql/issues/49
function tuple<T1, T2>(data: [T1, T2]): typeof data
function tuple(data: Array<any>) {
    return data
}

const q = {
    advertisers: tuple([
        { skip: 1},
        { isActive: true }
    ])
};

const { query, variables} = generateQueryOp(q);
const { data, error} = useQuery<QueryResult<typeof q>>(gql(query), { variables: variables });

Everything will work as expected. I can't mistype and the type of the data will be correct. Although, there's one disadvantage with this approach.

Since q isn't explicitly typed, we lose the ability to autocomplete (although if we mistype something, the compiler will complain).

Is there a way we could dance at two weddings by both having the data typed and having the autocompletion?

Feedback and a request

Wow, this library is awesome! I've been looking for a good typesafe GraphQL client for a long time :D

Some feedback:

Is it necessary to have 2 different clients for subscriptions and queries/mutations? I think it would be better/easier to have just one. This way authentication and other stuff don't have to be set up twice. Also, it would be nice if it was possible to configure query/mutations to be executed through WebSocket (it doesn't always make sense but if your app already uses subscriptions why not!).

I will play around with it some more and will let you know if I find something. Once again, good job!

Option to Transform Generated Interface Naming Strategy

The graphql-codegen/typescript generates interfaces in Capital Case to be more in line with naming conventions from the community. For example if the GraphQL server had a type called user this plugin would generate an interface called User.

Would it be possible for genql to also enable changing the interface types naming convention? For users of Hasura all the GQL types are lower case and there is currently no support to override this naming convention there. I wonder if it would be easier to support it as part of the genql codegen workflow?

I'm willing to help with the implementation effort if this is something that you think could be useful in the code base

Add server support with a GraphQL schema

Awesome work!

I have a different use case. I wonder if we can get it working for this specific use case.

I am developing stripe-graphql. A Stripe client for the server. My idea is to generate a NPM package to work on the server. To do so, I would need to pass the GraphQL schema object to createClient, instead of url.

Is there any way to achieve this right now?

Cannot install genql-runtime

Hey @remorses thanks for building this. I'm one of the core developers of https://nexusjs.org. We might integrate this tool into ours, I took genql for a spin today but got stuck on the following.

  1. I followed the cli workflow

  2. I generated against a local graphql schema genql --schema api.graphql --output client

  3. The output resulted in this client/index.d.ts module:

    image

  4. I wasn't sure if this is normal, as I saw no mention yet of needing to install this dep (missed something?). But I figured I'd try, but then got this error while trying to install:

    image

Enhancement: remove falsy values from fields selection

I would like to be able to decide whether to fetch a field or not based on context (e.g. user authorization)

const auth = useAuth()

// ...

await sdk.query({
  org: {
    id: auth.isAuthenticated,
    handle: true,
    name: true,
    legal_name: auth.isAuthenticated
  }
});

The current behavior is that every field is fetched, even though it's set to undefined or false.
It would be awesome if genql could automatically strip fields that are not set to true.

Ability to query interfaces that a union implements

We're using genql's typings on an upcoming app and its been working really well! It fits right in, and typed queries and mutations are awesome!

There's one area where type support is limited though, and that has to do with interfaces and unions.
When querying against a resource that returns a union, you should also be able to query interfaces that the union's types implement. Currently, it is only possible to query individual types within the union.

Perhaps an example will make this clearer.

So assume this (part of) GraphQL schema

interface ClientError {
  message: String!
}

type ClientErrorNameAlreadyTaken implements ClientError {
  message: String!
}

type ClientErrorNameInvalid implements ClientError {
  message: String!
}

union GenericError =
    ClientErrorNameAlreadyTaken
  | ClientErrorNameInvalid

type Query {
  error: GenericError
}

Here, this query is currently possible:

query q {
  error {
    ... on ClientErrorNameAlreadyTaken { message }
    ... on ClientErrorNameInvalid { message }
  }
}

as

client.query({
  error: {
    on_ClientErrorNameAlreadyTaken: { message: true }
    on_ClientErrorNameInvalid: { message: true }
  }
})

However, the following is also a valid query in GraphQL that genql disallows:

query q {
  error {
    ... on ClientError { message }
  }
}

It'd be awesome if genql could let me query it this way:

client.query({
  error: {
    on_ClientError: { message: true }
  }
})

If I'm being unclear, please let me know!

I'm more than happy to send a PR your way to support this if you like :)

runtime incompatible with vite

I've been trying to get my generated client to work in vite (ESM dev tool), but when importing from "./generated/index.esm.js" my app is'nt working, because vite has several problems in resolving some dependencies:

grafik

These dependencies unfortunately don't export as esm.
Is there any way we could get this to work ?

[RFC] Import enums in the browser

It would be nice if genql would generate a object alongside the type definition for enums. This would enable to iterate over enums to easily display them in the browser

  1. Lets say I have a schema.graphql that looks something like this
...
enum UserRole {
  Admin
  Trusted
  Untrusted
  User
}
...
  1. Now genql would generate something like this for the enum in schema.ts:
...
export const UserRole: {
  Admin: 'Admin',
  User: 'User',
  Trusted: 'Trusted',
  Untrusted: 'Untrusted'
};

export type UserRole = (typeof UserRole)[keyof typeof UserRole]
...

instead of what its currently doing:

export type UserRole = 'Admin' | 'Trusted' | 'Untrusted' | 'User'
  1. This would enable me to do something like this client-side:
let userRole: UserRole = "Admin"

Object.values(UserRole).map(role => {
  console.log(role);
})

Very minimal but I hope you get the point ^^

Maybe make this feature configurable so its disabled by default ?
Please tell me what you think :)

Consider migrating generated `types.json` to an exported object in `types.js` for easier build with `tsc`

Ran into a few hiccups post-compile with Typescript, one was the JS files weren't included by default (fixed with allowJs: true easily) but the other was the types.json which is read won't get copied into the built output without these settings:

{
  // Add explicit include for JSON files to copy over "generated/types.json"
  "include": ["./src", "./src/**/*.json"],
  "compilerOptions": {
    // Allow JS to copy the JS files from "generated"
    "allowJs": true,
    // Not sure if this 100% needed but it works
    "resolveJsonModule": true
  }
}

If the generated file was a types.js like this:

module.exports = {
  // types.json object here
}

Then the explicit include and the resolveJsonModule wouldn't be required.
Though I am not sure if there are technical limitations or other factors behind current types.json implementation.

Request: Please publish Changelog or Release Notes

Gidday again @remorses, still really enjoying using this package, thanks a lot for it.

There have been quite a few releases over the last couple of weeks, including a "major" version bump, but there is no Changelog, nor are there any Release Notes, so I'm unsure what the new features/bugfixes are, and what backward-incompatible changes have been made (particularly in the case of the major version bump). Could you please annotate the changes in your releases using one of these mechanisms?

Cheers, Mitch.

Generate SDK from authenticated endpoint

There's no mention in the docs on how you can generate the SDK from an authenticated endpoint (e.g. Hasura with admin secret).. The help command of the CLI shows that there's some config, where can I find how to setup the authentication header during generation?

By the way this is a really cool project πŸ‘ πŸ‘

Wrong types in results when using fragment syntax

I have generated SDK for Shopify Admin API and I see some issues with the type of the response.

When I do not request common interface fields, only the one from fragment (selected type - CustomerVisit), in the response I get all the fields, thus I can try to access them but they will always be undefined so my app will work incorrectly:

image

However, for the leaves, scalars it works ok, both inside the fragment and for the other properties:

image

Feature request: silent option for cli

Currently when you generate a local client using genql-cli, every single step is getting output to the console. It would be nice if there would be an option like β€œgenql-cli -o client β€”silent” to disable it or at least turn it into a one-liner output

Types regression: Field selection not working anymore

The latest version 1.1.21 does not correctly create selected fields types matching the query. The whole type is returned instead. The last working version that I know of is 1.0.58. See images below

v1.0.58
Screen Shot 2020-07-13 at 09 29 09

v1.1.21
Screen Shot 2020-07-13 at 09 23 03

Fix code example copy/paste in docs

Hello there. Very cool project!

I noticed something in the docs that breaks copy/paste. When highlighting code the line numbers are also copied

image

If you add this one CSS rule user-select: "none;" this will fix itself.

After with user-select none on those numbers

image

I wasn't able to find where this style is generated to fix myself πŸ˜…

GraphQL aliases

How do you specify aliases ?

e.g.:

{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

How to write multiple queries?

How do I write multiple queries in query(), or chain method?
A general use case is to get aggregate - count with the query -- common use case for pagination

query MyQuery {
  table(limit: 10, offset: 20, where: {field: {_eq: 5}}) {
    id
  }
  tableAggregate(where: {field: {_eq: 5}}) {
    aggregate {
      count
    }
  }
}

the problem here is that

  • I can't write where, limit, offset in normal query function.
  • and i cant write multiple queries in chain method where i can write where condition.

Fetch all the field when no argument is given to .get()

Hello πŸ‘‹,

We're using genql for our integration tests against our GraphQL API, it's a real pleasure to use, awesome work @remorses !

We were wondering if we could have an extra option option to the generate command to automatically have { __scalar: true } as the default value when using the chain syntax and calling .get().

I totally understand the choice to be explicit on the client-side about the fields, but when we are in a backend context, it adds noise to the code.

const [userConnection, userLocation] = await Promise.all([
        genql.chain.query
          .user_connections({
            where: { user_id: { _eq: existingUser.id } },
          })
-          .get({ __scalar: true }),
+         .get(),
        genql.chain.query
          .user_locations({
            where: { user_id: { _eq: existingUser.id } },
          })
-          .get({ __scalar: true }),
+         .get(),
      ]);

Using "headers" with a function seems to only evaluate once, during createClient()

I'm not sure why this is happening, the below looks like it's set up to evaluate headers function before sending fetch:

fetcher = async (body) => {
if (typeof headers == 'function') {
headers = headers()
}
headers = headers || {}

Here's a reproduction, the headers function is only invoked once, and you can see from checking timestamp header on network requests sent:

https://codesandbox.io/s/wizardly-grass-pzu7s?file=/src/index.ts

Without headers invoked per-call, cannot set auth token after signing in for example

Code Generation Options - Allow Overriding Scalar Type Definitions

It would be awesome to have more options available to developers to allow more control over the code generation process.

The GraphQL Code Generator plugin for React Apollo allows for overriding the underlying TS types that are given to custom scalars. Eg:

config:
  scalars:
    DateTime: Date
    JSON: "{ [key: string]: any }"
    uuid: string

Currently custom scalars all come out using the any type when a more logical built in TS type would be a better match.

@remorses I'm interested in helping to implement this feature if you think it would be a good fit for the library

Expose API

We'd like to incorporate genql into Nexus. Ideally though we wouldn't have to spawn to the genql CLI. Mostly because of performance reasons. Adds roughly 1-2s of latency.

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.