
Show HN: TypeScript to GraphQL conversion tool with type inference - acro5piano
https://github.com/acro5piano/typed-graphqlify
======
altschuler
An alternative to this and Apollo codegen is GraphQL Code Generator[1]. Like
codegen it takes the opposite approach by generating typescript from graphql.
It's similar to codegen, with the main two differences being 1) it's not
centered around queries, it will generate types for all schema parts as well
as queries defined client-side. This is useful for getting typescript types
for all the individual schema parts without being "wrapped" in the type of a
query, and 2) it's template based and relatively easy to extend modify how the
code is generated.

[1]: [https://github.com/dotansimha/graphql-code-
generator](https://github.com/dotansimha/graphql-code-generator)

~~~
ndonnellan
We use graphql-code-generator to make Typescript. We also upload versions of
our typescript-query files (written separately from other client TS code) for
each published version/branch (e.g. 1.1, 1.2, dev) and then can use an eslint
plugin on the graphql server to validate it doesn't break backwards
compatibility with existing client-side code (unless intentional). This has
definitely saved us more than once.

------
arthens
What's the advantage of using this tool instead of apollo-cli/apollo-codegen?

> The main idea is to have only one source of truth by defining the schema
> using GraphQL-like object and a bit of helper class.

The source of truth already exists, and it's the schema.

Why do I need to specify the type in every query? What happens if I make a
mistake and write the wrong type? What happens if the schema changes after I
write the query? Will it somehow fail and force me to update it?

------
lf-non
This is very interesting.

I have been writing a library [1] to bridge relational databases efficiently
to GraphQL APIs, and adopted a similar approach towards eliminating the
redundancy in the schema definitions.

I currently rely on io-ts [2] which I also use for runtime verification of
configuration options in an attempt to facilitate type verification to both
JavaScript and Typescript consumers.

[1] [https://gql-dal.github.io/greldal/](https://gql-dal.github.io/greldal/)
[2] [https://lorefnon.tech/2018/03/25/typescript-and-
validations-...](https://lorefnon.tech/2018/03/25/typescript-and-validations-
at-runtime-boundaries/)

~~~
acro5piano
GRelDAL is an interesting project! N+1 problem in GraphQL is difficult
problem. It remind me Lighthouse PHP [1] which is a GraphQL framework top of
Laravel. Lighthouse handles N+1 problem to create a lot of "union all"
queries.

[1]
[https://github.com/nuwave/lighthouse](https://github.com/nuwave/lighthouse)

------
mwilliamson
The solution we use at work is to put all of the GraphQL strings in separate
`.graphql.ts` files. A script then scans the code base for such files,
requires them, and converts queries/fragments into TypeScript types in a
corresponding `.graphql.types.ts` file using apollo-codegen.

The advantage of this over plain .graphql files, or statically extracting
queries, is that you can use all of the normal tools of TypeScript for
composing queries (mainly imports and template literals), and you don't need
to have globally unique names for all your queries and fragments. Having said
that, I haven't looked at the recommended Apollo workflow in a little while,
so I'm not sure if those issues still exist.

~~~
acro5piano
I didn't know Apollo codegen also interpret GraphQL in .ts file. Thanks!

------
acro5piano
> What's the advantage of using this tool instead of apollo-cli/apollo-
> codegen?

> Unfortunately I'm not yet able see any benefits you'd get over Apollo's
> codegen in return

> Doesn’t Apollo’s Codegen tool already solve this?

I am sorry for my late response.

I add the section to README.md which explains why I created this library even
there is Apollo CLI's codegen.

[https://github.com/acro5piano/typed-graphqlify#why-not-
use-a...](https://github.com/acro5piano/typed-graphqlify#why-not-use-apollo-
clientcodegen)

In short,

\- Simplicity make this tool works as expected

\- Can handle multiple schemas

\- Works without schema file

I wrote a lot there, but I just wanted to try the paradigm TypeScript ->
GraphQL conversion, because there are no tools to do that, unlike GraphQL ->
TypeScript conversion tools.

However, this is not just a hobby project, cause I use this in my current real
world project, and it reduces a lot of boilorplate. So I submit here.

# I thought Apollo codegen needs .graphql extension and webpack loader, but it
reads .ts files too... Awesome.

------
drostie
This is actually really interesting and mirrors a more general effort that I
am pursuing[1] to build runtime representations of type in TypeScript, though
my context is not so much GraphQL but rather just a more traditional HTTP API.

It looks like you try to keep it so that the runtime type object itself has
the correct TypeScript type? That is, I would expect that under the hood you
either have `types.number = 0`, `types.string = ''`, and so on and then you
just use `typeof runtimeTypeObject` to derive this, or something more sinister
like `types.number = ('number' as any) as number`. Either way that's fairly
clever and I like it. I probably won't do it that way in `tasso` because one
of my abstract points of power is that `tasso` should be able to validate that
a given type object is a valid `tasso` schema, so there should be a
metaschema. But it is really nice to have this thing saying just `typeof
runtimeTypeObject` and not `ValueOfSingleType<typeof runtimeTypeObject>` to
derive, from the schema's type, the instances' type.

I may steal one thing for `tasso` from this library, and that is the way your
`constant()` works without specifying type metadata; I was under the
impression that even with the `<t extends string>` TypeScript would still say
"well `const x = constant('abc')` doesn't say anything else about `'abc'` so I
am going to infer that it is a `string` and then string does extend string so
`x` has type `string`," much like it does when you just write `const x =
'abc'`. I didn't realize that you can "hint" that you want the more generic
type for the thing. In tasso this manifests when you are writing self-
recursive schemas, like

    
    
        // the following line is a sort of hack
        const refs = tasso.refs<'cell' | 'list'>();
        // but then we can write stuff like this
        const schema = {
          cell: tasso.number,
          list: tasso.maybe(tasso.object({
            head: refs.cell,
            rest: refs.list
          }))
        };
    

It will be nice to replace that with `tasso.refs('cell')` and
`tasso.refs('list')` without that "refs object", I think.

[1]: [https://github.com/crdrost/tasso](https://github.com/crdrost/tasso)

~~~
acro5piano
Runtime Type Check looks very useful when developing a system which
communicate with other systems, cause we cannot perfectly predict other
systems output.

I am grad to give you some development hints!

------
lars_francke
Definitely looks interesting. I started with Apollo only a few weeks ago and
am already tired of the redundancy and the verbosity.

I wonder how schema information/introspection from the back end could be used
to improve this further.

Apollo has the codegen tool which uses the schema to automatically generate
the required classes.

~~~
fro0116
The first thing that popped in my head was also how this compares to Apollo
CLI's type generation ([https://github.com/apollographql/apollo-
tooling](https://github.com/apollographql/apollo-tooling), formerly apollo-
codegen).

On a cursory glance, it seems like with this tool, you write a JS object in
the same shape as the query, manually specifying the type of each field in
that object, and then it generates the query for you to pass to graphql
clients, as well as the types associated with the result of that query for you
to use in your app. Whereas with Apollo's codegen, you write the query in
graphql syntax, then run a CLI command to generate the types associated with
that query for you to import and use.

It seems with this tool, you'd still have to manually state the type of each
field (when that information can be perfectly inferred from the schema as
demonstrated by Apollo's codegen), and you'd lose the ability to take
advantage of tools that work with the graphql syntax to provide things like
autocomplete based on your graphql schema while you're writing the query
itself ([https://github.com/apollographql/vscode-
graphql](https://github.com/apollographql/vscode-graphql), for instance).
Unfortunately I'm not yet able see any benefits you'd get over Apollo's
codegen in return. Happy to be enlightened if I'm missing anything.

~~~
lars_francke
That's my understanding as well. This would help for tools other than Apollo
which do not have the codegen capability but as it is I'm not sure what the
benefit is for Apollo users.

~~~
fro0116
Apollo CLI isn't even coupled to Apollo Client though. You can use it to
generate types for any graphql query as long as you can also provide a schema
for it to infer types from.

------
fantalamera
Doesn’t Apollo’s Codegen tool already solve this? Works like a charm
[https://github.com/apollographql/apollo-
tooling?files=1#apol...](https://github.com/apollographql/apollo-
tooling?files=1#apollo-clientcodegen-output)

------
YorkianTones
See also
[https://github.com/convoyinc/ts2gql](https://github.com/convoyinc/ts2gql)

------
bitflower
Couldn‘t `json-schema` play major role in modeling the redundant things?
FeathersJS goes this way....

~~~
acro5piano
As far as I know, json-schema just checks type in runtime right? I don't know
how we let TypeScript check json-schema type in compile-time.

------
picardo
Is there a similar conversion tool for Flow types?

------
bdr
This looks great. Thanks!

~~~
acro5piano
Thank you!

