A JS/TS generator that builds a GraphQL query builder for your API
You can install Graphton to your dev-dependencies using a package manager.
yarn add -D @graphtonlib/grapton
# Then to run:
yarn graphton generate https://example.com/graphql
npm i --save-dev @graphtonlib/grapton
# Then to run:
npm run graphton generate https://example.com/graphql
Usage: graphton generate [options] <schemaUri>
Arguments:
schemaUri URL to GraphQL endpoint or path to introspection json.
Options:
-o, --outputFile <path> Path to the generated js/ts file (default: "./src/graphton.generated.ts")
-q, --exportQueryFactoryAs <name> How you want to import your query factory. (default: "Query")
-m, --exportMutationFactoryAs <name> How you want to import your mutation factory. (default: "Mutation")
-s, --exportSubscriptionFactoryAs [name] If you want a subscription factory, if no name provided "Subscription" will be used. (default: false)
-Q, --queryFunction <name> The name of the function that posts the query. (default: "get")
-M, --mutateFunction <name> The name of the function that posts the mutation. (default: "do")
-S, --subscribeFunction <name> The name of the function that posts the subscription. (default: "subscribe")
-d, --defineScalar <scalars...> Define custom scalars and their TS type. Use this if you don't want (some) scalars to be typed as string by default. (eg. --defineScalar Date=number Time=any)
-h, --help display help for command
yarn graphton generate -o ./src/lib/graphton.js https://example.com/graphql
As you are creating a complete custom command to generate your SDK, it might become cumbersome to remember all the options you added to the command. It's a good idea to add it to your scripts object in your package.json
so you and your team always use the same generator command.
//package.json
{
"scripts": {
"generate": "graphton generate -o ./src/lib/graphton.js https://example.com/graphql"
}
}
Then generate the files using:
yarn generate
# Or
npm run generate
When the ts/js file is generated you can import the Query
and Mutation
factory instances from it.
import { Query, Mutation } from "./graphton.generated.js";
Note: the names
Query
andMutation
are configurable! See CLI Usage & options
Note: Since graphton does not handle subscriptions (yet?) since implementations can differ per-situation, subscription queries are optional. See CLI Usage & options to how to generate them. You can use
getQuery
to output the query and use your own implementation.
For the following examples, the example graphql schema from example/schema.graphql will be used.
Starting a new query is fairly simple. Import Query
from the generated file and call the method with the same name as the query you want to execute.
import { Query } from "./example/graphton.generated.js";
const usersQuery = Query.users();
const getFirstUserQuery = Query.user({ id: 1 });
In GraphQL you have to define which fields you want to get in return. You define which ones by using select
import { Query } from "./example/graphton.generated.js";
const usersQuery = Query.users().returnFields((r) => r.all());
const getFirstUserQuery = Query.user({ id: 1 }).select({
id: {},
name: {},
posts: {
text: {},
},
});
After building the query, you can directly execute it with the get()
method.
import { Query } from "./example/graphton.generated.js";
const firstUser = await Query.user({ id: 1 })
.select({
id: {},
name: {},
posts: {
text: {},
},
})
.get();
// firstUser = {
// data: {
// id: 1,
// name: 'User One',
// posts: [
// {
// text: 'A sample post!'
// },
// {
// text: 'Enother sample post'
// }
// ]
// },
// axiosResponse: /*AxiosReponse*/,
// }
Running mutations is about the same as running a query. The only diferences are:
- You import
Mutation
instead ofQuery
. - Instead of
.get()
you run.do()
import { Mutation } from "./example/graphton.generated.js";
const newUser = await Mutation.createUser({ name: "User Infinite" })
.select({ _all: {} }) // _all will expand automatically to return all shallow fields
.do();
const updatedUser = await Mutation.updateUser({ id: 1, name: "User NotOne", age: 12 })
.select({ _all: {} }) // Selecting all shallow fields again
.deselect({ name: {} }) // But removing name
.do();
Return fields can be dynamically changed.
import { Query } from "./example/graphton.generated.js";
const firstUserQuery = Query.user({ id: 1 }).select({
id: {},
name: {}
});
if (getUserPosts) {
// Adds text of all posts to the query - does NOT replace the already selected id and name
firstUserQuery.select({
posts: {
text: {},
},
});
}
You can change some of the settings, used for making the call to the server, like URL or headers.
import { GraphtonSettings } from "./example/graphton.generated.js";
GraphtonSettings.headers = { Authentication: "Bearer XXX" };
Most of the reference underneath here is very abstract, since we do not know how the external GraphQL schema looks like which you are going to use this on. Because of this, whenever you see a $
next to something in this guide means that it's a value or type that is replaced to accomodate your GraphQL schema.
That said, if you use the .ts variant of the generated output, tsc or your IDE can tell you exactly where something goes wrong.
This is why we recommend TypeScript. The JavaScrips variant works just fine, but without any error correction or autocomplete it might be a little harder to do the thing you want to do.
GraphtonBaseQuery is abstract, the classes you will call are generated and extended from this base class.
Only available if the query has arguments
Signature: setArgs(queryArgs: $ArgumentType): this
Set the arguments for this query to queryArgs
(overwriting previous arguments).
Only available if the return type is an OBJECT
select(fields: Partial<$FieldSelectorType>): this
Add new fields to the return object (merges recursively).
Only available if the return type is an OBJECT
Signature: deselect(fields: Partial<$FieldSelectorType>): this
Removes selected fields from the query.
Note: _all will also remove objects instead of just the shallow fields.
Signature: toQuery(): string
Transform to graphql Query string.
Only available on Query type requests
Signature: get(requestConfig: AxiosRequestConfig = {}): Promise<QueryResponse & { [p:string]: any; axiosResponse: AxiosResponse; }>
Execute the query and get the results.
Only available on Mutation type requests
Signature: do(requestConfig: AxiosRequestConfig = {}): Promise<QueryResponse & { [p:string]: any; axiosResponse: AxiosResponse; }>
Do the mutation on the server.
Intermediary class used to correctly parse Enums in arguments. Your Enum values can be gotten with how you would normally call an enum.
Query.usersOrdered({
orderBy: [
{
column: UserSortColumn.age,
order: SortOrder.ASC,
},
],
});
public static parse(value: keyof typeof $GraphtonEnum.possibleValues): $GraphtonEnum
Transforms a string into an enum.
Alias:
$GraphtonEnum.VALUE;
// Is the same as
$GraphtonEnum.parse("VALUE");
public static list(): $GraphtonEnum[]
Gives you a list of possible enums
Graphton uses the static properties of this class, so you can customize these.
public static headers = { };
Set the headers for each following request to the server.
GraphtonSettings.headers = { Authorization: "Bearer 123abc" };
public static graphqlEndpoint: string = '';
Set the URL to the GraphQL endpoint, defaults to the URL used when generating the file.
GraphtonSettings.graphqlEndpoint = "https://mycoolexample.app/graphql";
- π‘ GitHub Issues: feature requests and bug reports
- ππΌ Patreon: Consider supporting this project if it helped you π
- Robbin "Roboroads" Schepers - Creator / First contributor.
- Other github projects:
- laravel/laravel - This plugin is inspired by the way Laravel Eloquent builds queries.
- Contributors to this project
- I will name you seperately if your amount of contributions is exceptional
Any tips or feedback is welcome in the Issues Tab - or better, make me a PR!
Want to be part of Graphton? Every positive contribution is welcome!