Scalable and typesafe GraphQL client for JS, React and React Native.
- 🦺Simple codegeneration with full type safety at runtime and in typescript
- 🚀~50% reduction in RAM usage and up to x20 performance in persistence (comparing to Apollo)
- 🏎Does not block main thread
- 🍰Layered design that allows replace transport, socket or graphql core implementations
yarn add @openland/spacex @openland/spacex-web
yarn add -D @openland/spacex-cli get-graphql-schema
- API Endpoint too download schema from (ex: https://api.example.com/graphql)
- .graphql files with operations (ex: ./src/api/definitions/*.graphql)
SpaceX Compiler generates three files:
spacex.definitions.json
: Descriptors of all fragments and operations for GraphQL Enginesspacex.types.ts
: Typescript fragments and operations filesspacex.ts
: Client itself
get-graphql-schema https://api.example.com/graphql --json > schema.json
yarn spacex-cli compile \
--path "./src/api/definitions/*.graphql" \
--schema ./schema.json \
--output ./src/api/ \
--name ExampleClient
import { WebEngine, createCommonTransport } from '@openland/spacex-web'
import { ExampleClient } from './spacex'
// Transport
const transport = createCommonTransport({
url: 'wss://api.example.com/graphql',
mode: 'transport-ws',
connectionParams: {
token: 'some-fancy-token'
}
})
// Engine
const engine = new WebEngine({
definitions: require('./spacex.descriptor.json'),
transport
})
// Client
const client = new ExampleClient(engine)
For each query in definitions there are generated functions on client:
// Simple Promise-based api
const me = await client.queryUser({ username: 'steve' }) // returns: Promise<User>
// Provide fetchPolicy - bypass cache and fetch from network
const me = await client.queryUser(
{ username: 'steve' },
{ fetchPolicy: 'network-only' }
)
// Refetch method - sugar for fetchPolicy: 'network-only'
const me = await client.refetchUser({ username: 'steve' })
// Hook with suspence
const me = client.useUser({ username: 'steve' }) // returns: User
// Hook with fetchPolicy
const me = client.useUser(
{ username: 'steve' },
{ fetchPolicy: 'network-only' }
) // returns: User
// Hook without suspence
const me = client.useUser({ username: 'steve' }, { suspence: false }) // returns: User | null
For each defined mutation there this generated function:
// Simple Promise-based api
const result = await client.mutateSendMessage({
chat: 'steve',
message: 'Hello, SpaceX!'
})
For each defined mutation there this generated function:
// Subscription
const subscription = client.subscribeNewMessages({ chat: 'steve', from: Date.now() }, handler: (e) => {
if (e.type === 'stopped') {
// Subscription stopped
// No more updates in this handler
} else if (e.type === 'message') {
// Received message
const message = e.message;
}
});
// Destroy subscription
subscription.destroy();
If you want to edit store you are able to update queries in the store via update*
functions:
await client.updateUser({username: 'stever'}, updated: (src) => {
return {
...src,
friends: src.friends + 1
};
});
Since there are multiple ways to deploy web workers SpaceX
provides API for integration and leaves developer a freedom to deploy any way they wish.
This is a code to run web worker host - the code that executes queries itself and answers for requests from main process.
import {
WebEngine,
createCommonTransport,
WorkerInterface,
WorkerHost
} from '@openland/spacex-web'
var host
//
// Handler for first message: It fetches connectionParams and creates an engine
//
const initHandler = (ev: MessageEvent) => {
// Read init package
let msg = ev.data
if (msg.type !== 'init') {
throw Error('Worker need to be inited first!')
}
self.removeEventListener('message', initHandler)
// Define Worker Interface
let workerInterface: WorkerInterface = {
post: src => self.postMessage(src),
setHandler: handler =>
self.addEventListener('message', (src: any) => handler(src.data))
}
// Create transport
const transport = createCommonTransport({
url: 'wss://api.example.com/graphql',
mode: 'transport-ws',
connectionParams: {
token: msg.token
}
})
// Create Engine
const engine = new WebEngine({
definitions: require('./spacex.descriptor.json'),
transport
})
// Create Host
host = new WorkerHost({
engine,
worker: workerInterface
})
}
self.addEventListener('message', initHandler)
// Our connection token
const token: string = 'some-fancy-worker'
// Create WebWorker
const W = require('./spacex.worker') // NOTE: We using WebPack plugin to generate web workers
let thread: Worker = new W()
thread.onerror = e => {
console.error(e) // There are no way to recover easily
}
// Send init package to worker
thread.postMessage({ type: 'init', token })
// Create Engine
let threadInterface: WorkerInterface = {
post: src => thread.postMessage(src),
setHandler: handler =>
(thread.onmessage = src => {
handler(src.data)
})
}
const engine = new WorkerEngine({ worker: threadInterface });
// Client
const client = new ExampleClient(engine)
MIT