snek-query
v0.0.114
Published
Powerful queryless GraphQL API client
Downloads
110
Readme
Snek-query
Snek-query is a simple and easy-to-use library for interacting with any GraphQL API. It abstracts away the complexities of manually constructing and sending GraphQL queries and mutations, providing a convenient and simplified interface for making these requests.
With Snek-query, you can quickly and easily fetch data from your GraphQL API and perform mutations such as creating or updating data. The library also handles authentication, error handling, and data parsing, allowing you to focus on writing the queries and mutations you need for your application.
Table of Contents
Installation
To install Snek-query, you can use npm:
npm install snek-query
or yarn
yarn add snek-query
Please note that this library requires Node.js version >=18
Usage
First, you need to generate the Query
and Mutation
object from your GraphQL schema by running the following command
yarn snek-query generate https://your-graphql-endpoint.com/graphql
To see all available options, you can run the command with the --help
flag:
yarn snek-query generate --help
This command will generate a folder (by default named snek-query
) that contains the generated file schema.generated.ts
and an index.ts
file.
The schema.generated.ts
file contains the Query
and Mutation
objects that are generated based on the GraphQL schema of the endpoint provided to the command. The index.ts
file contains an instance sq
that is already configured with the schema.generated.ts
and the options passed to the makeSnekQuery
function when the yarn snek-query generate
command was run.
Interact with the API
You can use this instance directly in your application by importing it from the generated snek-query folder. The sq object takes care of the boilerplate for you and provides convenient methods for making GraphQL queries and mutations.
To import the sq
object:
import {sq} from './snek-query'
You can then use the sq object to make queries/mutations like this:
// Queries
const [bookTitles, bookTitlesError] = await sq.query(q => q.books.title)
if (bookTitlesError) {
console.error(bookTitlesError)
} else {
console.log(bookTitles)
}
const [shipIds, shipIdsError] = await sq.query(q => q.ships({limit: 10}).id)
if (shipIdsError) {
console.error(shipIdsError)
} else {
console.log(shipIds)
}
const [ages, agesError] = await sq.query(
q => q.ship({id: 1}).members({first: 10}).age
)
if (agesError) {
console.error(agesError)
} else {
console.log(ages)
}
// Mutations
const [createdBook, createBookError] = await sq.mutate(q =>
q.createBook({title: 'Book Title', author: 'Book Author'})
)
if (createBookError) {
console.error(createBookError)
} else {
console.log(createdBook)
}
Each function call to sq.query
or sq.mutate
returns an array of two values. The first value is the result of the query or mutation, and the second value is an error. If the query or mutation is successful, the second value will be undefined
, otherwise it will be an array of GraphQLError
objects, representing the errors that occurred.
Customizing the Snek-query instance
The sq
object that is generated by the snek-query generate
command has a default configuration. However, you can customize the behavior of the sq
object by passing other options to the makeSnekQuery
function.
import { makeSnekQuery } from 'snek-query'
const sq = makeSnekQuery({ Query, Mutation }, {
apiURL: "https://api.spacex.land/graphql/",
middlewares: [
async ({context}) => {
const token = await storage.get(AUTH_TOKEN_KEY)
if (!token) return
context.headers = {
...context.headers,
Authorization: `JWT ${token}`
}
}
],
onError: async ({graphQLErrors, networkError, operation, forward}) => {
... do something
// forward request
}
});
Snek-query storage
Snek-query also comes with a built-in, cross-platform key-value storage that can be used to store data in a way that is similar to using the localStorage
API in the web, but works on Node.js and React Native environments as well.
You can use the storage
object from snek-query
to store and retrieve data in a way that is similar to using the localStorage
API:
import {storage} from 'snek-query'
// set a value
await storage.set('key', 'value')
// get a value
const value = await storage.get('key')
console.log(value) // 'value'
// remove a value
await storage.remove('key')
The storage object has the following methods:
set(key: string, value: any)
: sets a value for the given keyget(key: string)
: retrieves the value for the given keyremove(key: string)
: removes the value for the given keyclear()
: removes all stored values
It is important to note that the storage is asynchronous, so all the methods returns promises.
You can use this feature to store data such as authentication tokens or user preferences that need to be accessed across different parts of your application, without having to pass them around as props or using a global state management solution.
Network Status Utilities
Snek-query includes network status utilities that can be helpful when working with GraphQL APIs in various environments. These network status utilities are used internally by snek-query
to handle network errors, but they can also be used in your own code to check the device's online status and subscribe to online/offline events.
Import Statements
Before using these utilities, you'll need to import them into your project:
import {isOnline, onOnline} from 'snek-query'
isOnline
Function
The isOnline function can be used to check if the device is currently online. It provides a simple way to determine whether the device has internet connectivity.
async function checkOnlineStatus() {
const online = await isOnline()
if (online) {
console.log('The device is online.')
} else {
console.log('The device is offline.')
}
}
checkOnlineStatus()
onOnline
Function
The onOnline
function allows you to subscribe to online/offline events, making it easy to react to changes in network status. You can provide a callback function that will be called whenever the online status changes.
Example:
// Subscribe to online/offline events
const unsubscribe = onOnline(isOnline => {
if (isOnline) {
console.log('The device is online.')
} else {
console.log('The device is offline.')
}
})
// Later, when you no longer need the subscription, unsubscribe
unsubscribe()
Please note that the onOnline
function is designed to trigger events when the network status changes, so it won't provide the current network state on its initial call. It's best suited for reacting to network changes in your application.
Current limitations and workarounds
While snek-query
is a powerful tool for interacting with APIs, there are a few limitations that you should be aware of. In this section, we will discuss one such limitation and its workaround.
Using enums
It is important to note that enums defined in schema.generated.ts
are not directly compatible with the sq.query
and sq.mutate
functions. Instead, you should use the asEnumKey
function to convert the enum to a compatible format.
Here is an example of what not to do:
import {sq} from './snek-query'
const [bookTitles, bookTitlesError] = await sq.query(
q => q.books({orderBy: BookOrderByField.TITLE}).title
)
In the above example, BookOrderByField.TITLE
will not work with sq.query
as is. The correct way to use enums is to use the asEnumKey
function to convert the enum to a compatible format, as shown in the example below:
import {asEnumKey} from 'snek-query'
import {sq} from './snek-query'
const [bookTitles, bookTitlesError] = await sq.query(
q => q.books({orderBy: asEnumKey(null, 'TITLE')}).title
)
// or
const [bookTitles, bookTitlesError] = await sq.query(
q => q.books({orderBy: asEnumKey(BookOrderByField, 'TITLE')}).title
)
Note that the asEnumKey
function only works if the value passed to it is the actual string value of the key, and not the value itself. In other words, you should use the string value of the enum key as the argument for asEnumKey. If you use the value of the enum instead of the string value, the asEnumKey function may not work as expected.
Maintainers
Snek-query is actively maintained by:
- Nico Schett ([email protected])
Feel free to reach out to us with any questions or concerns.
Roadmap
- Implement Pagination Hook
- Optimize Query Performance (QueryOptimizer/Cache)
- Enhance Query and Mutate Functionality with Built-in Array Support (e.g. .map, .sort, .find, .filter, .slice)
- Handle Null Types with Ignoring or Placeholder Modes
- Introduce Helper Functions:
all/resolve
to retrieve all fields of a node (e.g.sq.query(q => all(q.userMe))
)interface
to access fields by interface (e.g.interface(q.userMe, "InterfaceName")
orq.userMe.INTERFACE
)alias
to create a path alias (e.g.alias(q.userMe, "thisIsMe")
)