GraphQXL is an extension of the GraphQL language with some additional features that help creating big and scalable server-side schemas.
When following a schema-first approach, there are some challenges that are left unaddressed compared to a code-first approach.
-
With a code-first approach, you have all the tools the programming language you are using provides, like functions for automatically generating repetitive types, inheritance for reusing code pieces, and so on.
-
With a schema-first approach you need to write by hand all the repetitive entities in your schema, and if you want to split your schema across different files for a better maintainability you are bound to language-specific tools for merging them.
GraphQXL provides additional syntax to the original GraphQL language for solving this challenges and making defining server-side GraphQL schemas a nicer experience, without being bound to language-specific tools. At the end of the day, GraphQXL is just a binary executable that will compile your GraphQXL schemas into GraphQL, independently of the programming language that you are using for the backend.
Installation
You can just download the precompiled binary from GitHub releases or use a language-specific wrapper for a better integration with your build tools.
Precompiled binary
There are precompiled binaries that you can download from GitHub releases:
Mac M1
wget https://github.com/gabotechs/graphqxl/releases/latest/download/graphqxl-aarch64-apple-darwin.tar.gz
tar -xvf graphqxl-aarch64-apple-darwin.tar.gz
Mac Intel
wget https://github.com/gabotechs/graphqxl/releases/latest/download/graphqxl-x86_64-apple-darwin.tar.gz
tar -xvf graphqxl-x86_64-apple-darwin.tar.gz
Linux x86_64
wget https://github.com/gabotechs/graphqxl/releases/latest/download/graphqxl-x86_64-unknown-linux-gnu.tar.gz
tar -xvf graphqxl-x86_64-unknown-linux-gnu.tar.gz
Linux aarch64
wget https://github.com/gabotechs/graphqxl/releases/latest/download/graphqxl-aarch64-unknown-linux-gnu.tar.gz
tar -xvf graphqxl-aarch64-unknown-linux-gnu.tar.gz
Language-specific wrappers
Node
yarn add -D node-graphqxl
Or
npm install --save-dev node-graphqxl
Usage
GraphQXL is just a compiler that will receive .graphqxl
files as an input and will
compile them down to a common .graphql
file.
For example, given this foo.graphqxl
file:
# foo.graphqxl
type MyType {
foo: String
}
You can do
graphqxl foo.graphqxl
And the file will get compiled to foo.graphql
:
# foo.graphql
type MyType {
foo: String
}
Features
So, the example above was not very useful, as the compilation result is exactly the same as the input. Here is a list of more useful things you can do with GraphQXL:
- Field inheritance
- Generic types and inputs
- Type and input modifiers
- Import statements
- Description templates
Inheritance
There will be times that a lot of types
or inputs
have some fields in common. In GraphQXL you
can inherit fields between types
and inputs
using spread operators.
Spread operator
types
and inputs
can inherit fields from other types
and inputs
using
spread operators, for example:
Source GraphQXL | Compiled GraphQL |
|
|
Private fields
It is very common that you do not want to expose the Common
type in the public API,
so you can make it private by prefixing the name with a _
character (or the
prefix that you provide in the CLI argument):
Source GraphQXL | Compiled GraphQL |
|
|
Inheriting interfaces
A common pattern is to declare a GraphQL interface
and to implement it in a type
, but
you need to rewrite all the fields in the type
that belong to the interface
as they
are not implicit. You can use the spread operator also with interfaces
:
Source GraphQXL | Compiled GraphQL |
|
|
Generics
Generics can be used to reuse types
or inputs
that have some
small subset of the fields slightly different from each other:
Source GraphQXL | Compiled GraphQL |
|
|
Notice how the generic type definition is omitted from the generated GraphQL. If
a type
or an input
is declared with generic type parameters, it will not be
present in the generated GraphQL.
It can even be combined with inheritance:
Source GraphQXL | Compiled GraphQL |
|
|
Modifiers
Modifiers are like built-in generic types that will modify the provided type based on some rules. Users cannot define their own modifiers, only use the built-in ones.
These are the currently available modifiers:
Optional
The Optional
modifier takes a type
or an input
as a parameter and outputs a
similar object with all its fields marked as "nullable". If the field in the original
object was declared required with a !
the output will contain that field without
the !
, otherwise the field is left untouched.
Source GraphQXL | Compiled GraphQL |
|
|
Required
The Required
modifier takes a type
or an input
as a parameter and outputs a
similar object with all its fields marked as "non-nullable". If a field in the original
object did not have a !
, it will have it in the new object, otherwise it will be
left untouched
Source GraphQXL | Compiled GraphQL |
|
|
Imports
Import statements allow you to split your schema across different files without relying on any opinionated merging tool or some language-specific merging script. This is useful for keeping your schema's code base clean and for improving its maintainability.
An import
statement will resolve all the content of the imported
file in the current one, for example:
Source GraphQXL | Compiled GraphQL |
|
|
A good pattern for keeping your schema clean could look something like this:
import "products"
import "users"
# import other entities
type Query {
products: [Product!]!
product(id: ID!): Product
users: [User!]!
user(id: ID!): User
# queries for the other entities
}
type Mutation {
createProduct(input: CreateProductInput!): Product!
deleteProduct(id: ID!): Product!
createUser(input: CreateUserInput!): User!
deleteUser(id: ID!): User!
# mutations for the other entities
}
schema {
query: Query
mutation: Mutation
}
Description templates
Let's say that you have a type
that you want to reuse in other types:
Source GraphQXL | Compiled GraphQL |
|
|
There, foo
's description says that the field foo
belongs to the type ToBeReused
,
but once that is compiled it is not true, it should say something like:
input Bar {
"Field foo from input 'Bar'"
foo: String!
}
Description string interpolation
You can use template variables in the description strings to refer to some contextual values:
Source GraphQXL | Compiled GraphQL |
|
|
These are the available values for the templates:
- block.name: The parent's block name
- block.type: The parent's block type (
type
orinput
) - variables.YOUR_GENERIC_VARIABLE: The value of the generic variable once instanced