GraphQL to TypeScript type, no code gen with ith Prisma-like query syntax, fully type-safe.
Written purely in TypeScript type.
Brought to you by ElysiaJS
Mobius can parse GraphQL schema to TypeScript to create End-to-end type safe GraphQL client in TypeScript.
Made possible by Template Literal and various dark magic.
- Comment must not have "{}" (bracket) otherwise type will not be resolved
- Nested fragment is not supported
- TypeScript has limited total stack, pass around ~8-9 locs / 14k generated around ~900 types (compacted, only types)
This is a proof that you can run GraphQL with end-to-end type safety.
This is a bare minimum utility library, not intent to replace GraphQL client like URQL and GraphQL Apollo.
Mobius acts as a companion library or internal engine to add Type Safety layer over a new or an existing one.
Mobius does 2 things:
- Infers GraphQL to TypeScript types
- A bare minimum client that use Prisma-like syntax to query GraphQL
You can use Mobius in your library / framework, just please keep the LICENSE mentioned that you are using GraphQL Mobius (It's MIT License, feels free to fork or improve on it)
- TypeScript > 5.0
- Set
strict
to true in tsconfig.json
- Define a GraphQL Schema in string (must be const)
- Cast schema to type using
typeof
(or pass it as literal params in constructor)
import { Mobius } from 'graphql-mobius'
const typeDefs = `
type A {
A: String!
B: String!
}
type Query {
Hello(word: String!): A!
}
`
const mobius = new Mobius<typeof typeDefs>({
// Using Mobius default fetch client
url: 'https://api.saltyaom.com/graphql'
})
// This is also fine, if you don't care about TypeDefs being available on client-side
const mobius2 = new Mobius({
url: 'https://api.saltyaom.com/graphql'
typeDefs
})
// Call query to execute query
const result = await mobius.query({
Hello: {
where: {
word: 'Hi'
},
select: {
A: true
}
}
})
result
.then(x => x?.Hello.A)
.then(console.log)
Mobius client provided the following method:
- $: Query all types of query at once
- query: Query GraphQL
- mutate: Mutate GraphQL
- subscription: Subscribe GraphQL
Mobius client provided the following properties:
- mobius: For type declaration only
- fragments: Type-safe GraphQL fragments (type is always provided, literal code is available if
typeDefs
is passed)
Mobius type extends Record<string, unknown>
with the base of following:
- Query:
Record<string, unknown>
- Mutation:
Record<string, unknown>
- Subscription:
Record<string, unknown>
- Fragment:
Record<string, unknown>
- Rest of the types declarations infers from GraphQL Schema
@see Utility Types for an example usage and more detailed explaination
You can add custom scalars type by passing types as second generic.
import { Mobius } from 'graphql-mobius'
const typeDefs = `
type A {
A: String!
B: Date!
}
`
type Scalars = {
Data: Date
}
const client = new Mobius<typeof typeDefs>()
client.klein
/**
* A: {
* A: string
* B: Date
* }
*/
If scalars isn't provided but is defined in GraphQL anyway, it should resolved as unknown
You can use Mobius to strictly type Resolvers
function for GraphQL Apollo and GraphQL Yoga.
import { Mobius } from 'graphql-mobius'
const typeDefs = `
type A {
A: String!
B: String!
}
type Query {
Hello(word: String!): A!
}
`
const mobius = new Mobius({
typeDefs
})
const resolvers = {
Query: {
Hello(_, { word }) {
return {
A: "Hello",
B: "Hello"
}
}
}
} satisfies typeof mobius.resolvers
import type { CreateMobius, Resolvers } from 'graphql-mobius'
const typeDefs = `
type A {
A: String!
B: String!
}
type Query {
Hello(word: String!): A!
}
`
type Resolver = Resolvers<CreateMobius<typeof typeDefs>>
const resolvers = {
Query: {
Hello(_, { word }) {
return {
A: "Hello",
B: "Hello"
}
}
}
} satisfies Resolver
You use use mobius.fragment
if you provided typeDefs as literal code
Fragment syntax works like rest parameters which looks like GraphQL fragment syntax.
const typeDefs = `
interface A {
A: String!
B: String!
C: String!
D: String!
}
fragment APart on A {
A
B
}
type Query {
GetA: A!
}
`
const mobius = new Mobius({
typeDefs
})
const { APart } = mobius.fragments!
mobius.query({
GetA: {
...APart,
C: true
}
})
For framework, and library author.
You can use exported utilities types from graphql-mobius to achieve End-to-end type safety while not increasing any byte att all for your project's bundle.
import type { CreateMobius } from 'graphql-mobius'
const typeDefs = `
# Hello World
type A {
A: String!
B: String!
}
# Hello World
type Query {
Hello(word: String!): A!
}
`
// This is an equivalent to calling new Mobius().klein
type Engine = CreateMobius<typeof typeDefs>
CreateMobius
will returned type structured as extends Record<string, unknown>
the base of following:
- Query:
Record<string, unknown>
- Mutation:
Record<string, unknown>
- Subscription:
Record<string, unknown>
- Fragment:
Record<string, unknown>
- Rest of the types declarations infers from GraphQL Schema
CreateMobius (Type)
- Infers GraphQL types to TypeScriptResolver (Type)
- Infers GraphQL types to TypeScriptRemoveComment (Type)
- Remove GraphQL comment in type-levelCreateQuery (Type)
- Create Prisma-like argument syntax for ClientMakeExecutable (Type)
- Create Prisma-like function for GraphQLmobiusToGraphQL
- Map Prisma-like JSON to GraphQL query (string)createFragment
- Create fragments for usage in Prisma-like client
As mentioned that Mobius is not intent to replace existing GraphQL client, but designed to create an abstraction over.
Allowing you to integrate with existing library by providing a custom fetcher with the following type:
type Fetcher = (query: string) => Promise<unknown>
Fetch function is a callback function that executed when new request is calls, and will accept a stringified GraphQL query and expected to return a GraphQL response.
It's intent to be use like the following:
// Using URQL
new Mobius({
fetch: urql.query
})
// Using native fetch (default)
new Mobius({
fetch: (query) => fetch(this.config.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables: {}
}),
})
.then((res) => res.json())
})
The library that you want to query GraphQL to use with Mobius is your choice, it's designed to be that way.
GraphQL Mobius is a library to convert GraphQL to TypeScript type without code generation, by using purely TypeScript type.
It's not intent to replace existing GraphQL client, but to create an abstraction over.
You can freely use Mobius in your source code / library / framework, just please keep the original LICENSE (MIT License)
Brought to you by ElysiaJS