https://github.com/thuoe/gql-util-directives
A simple utility package of GraphQL schema directives
https://github.com/thuoe/gql-util-directives
apollo-graphql apollo-server directives graphql typescript
Last synced: 6 months ago
JSON representation
A simple utility package of GraphQL schema directives
- Host: GitHub
- URL: https://github.com/thuoe/gql-util-directives
- Owner: thuoe
- License: mit
- Created: 2024-01-21T17:05:37.000Z (over 2 years ago)
- Default Branch: next
- Last Pushed: 2024-05-02T22:27:58.000Z (over 2 years ago)
- Last Synced: 2025-08-17T09:39:51.779Z (12 months ago)
- Topics: apollo-graphql, apollo-server, directives, graphql, typescript
- Language: TypeScript
- Homepage:
- Size: 926 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
gql-util-directives
Simple utility library for custom GraphQL schema directives
- [Get started](#get-started)
- [Local Development](#local-development)
- [Directives](#directives)
- [@encode](#encode)
- [@regex](#regex)
- [@cache](#cache)
- [Overriding in-memory cache](#overriding-in-memory-cache)
- [@currency](#currency)
- [@log](#log)
- [Logging to file](#logging-to-file)
# Get started
Install package:
```sh
npm install --save @thuoe/gql-util-directives
```
Example of importing the `@regex` directive & instantiating with Apollo Server:
```typescript
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
import { makeExecutableSchema } from "@graphql-tools/schema";
import directives from "@thuoe/gql-util-directives";
const typeDefs = String.raw`#graphql
type User {
firstName: String
lastName: String @regex(pattern: "\\b[A-Z]\\w+\\b")
age: Int
}
type Query {
user: User
}
`;
const resolvers = {
Query: {
user: () => ({
firstName: "Michael",
lastName: "Jordan",
age: 61,
}),
},
};
const { regexDirective } = directives;
const { regexDirectiveTypeDefs, regexDirectiveTransformer } =
regexDirective("regex");
const transformers = [regexDirectiveTransformer];
let schema = makeExecutableSchema({
typeDefs: [regexDirectiveTypeDefs, typeDefs],
resolvers,
});
schema = transformers.reduce(
(curSchema, transformer) => transformer(curSchema),
schema,
);
const server = new ApolloServer({
schema,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`🚀 Server ready at: ${url}`);
});
```
Here are the possible directive functions that are exposed as part of this util package:
`regexDirective | encodingDirective | cacheDirective`
# Local Development
Install local dependencies:
```sh
npm install
```
Run local environment (Apollo Studio):
```sh
npm run dev
```
Link to Apollo Studio can be found on http://localhost:4000 to perform mutations and queries.
# Directives
## @encode
`encodingDirective(directiveName?: string)`
You can use the `@encode` directive on fields defined using the `String` scalar type.
Following encoding methods:
`ascii | utf8 | utf16le | ucs2 | base64 | base64url | latin1 | binary | hex`
```graphql
type User {
firstName: String @encode(method: "hex")
lastName: String @encode(method: "base64")
}
```
## @regex
`regexDirective(directiveName?: string)`
You can use the `@regex` directive to validate fields using the `String` scalar type. It will throw an
`ValidationError` in the event that the pattern defined has a syntax if no matches are found against the field value.
```graphql
type User {
firstName: String @regex(pattern: "(John|Micheal)")
lastName: String @regex(pattern: "\\b[A-Z]\\w+\\b")
}
```
⚠️ Escaping characters
If you are defining a regex pattern using backslashes must escape them (`//`) **and** pattern invoke the function `String.raw()` to the schema so that the escape characters are not ignored:
```typescript
const typeDefs = String.raw`
type User {
firstName: String @regex(pattern: "(Eddie|Sam)")
lastName: String @regex(pattern: "\\b[A-Z]\\w+\\b")
age: Int
}
type Query {
user: User
}
`;
```
## @cache
`cacheDirective({ directiveName, cache }?: { directiveName?: string, cache?: CachingImpl })`
You can use `@cache` directive to take advantage of a in-memory cache for a field value
```graphql
type Book {
name: String
price: String @cache(key: "book_price", ttl: 3000)
}
```
`key` - represents the unique key for field value you wish to cache
`ttl` - time-to-live argument for how long the field value should exist within the cache before expiring (in milliseconds)
### Overriding in-memory cache
If you wish to take leverage something more powerful (for example [Redis](https://redis.io/)), you can override the in-memory solution with your own implementation.
Example:
```typescript
import Redis from 'ioredis'
const redis = new Redis()
....
const cache = {
has: (key: string) => redis.exists(key),
get: (key: string) => redis.get(key),
delete:(key: string) => redis.delete(key),
set: async (key: string, value: string) => {
await redis.set(key, value)
},
}
...
const { cacheDirectiveTypeDefs, cacheDirectiveTransformer } = cacheDirective({ cache: callback })
```
You must confirm to this set of function signatures to make this work:
- `has: (key: string) => Promise` Checks if a key exists in the cache.
- `get: (key: string) => Promise` Retrieves the value associated with a key from the cache.
- `set: (key: string, value: string) => Promise` Sets a key-value pair in the cache.
- `delete: (key: string) => Promise` Deletes a key and its associated value from the cache.
## @currency
`currencyDirective(directiveName?: string)`
You can use the `@currency` directive to fetch the latest exchange rate of a given amount
```graphql
type Car {
make: String
model: String
price: String @currency(from: GBP, to: USD)
}
```
The field can either be resolved with scalar types `String` or `Float`
The valid currency codes to use as part of the directive's arguments can be found [here](./src/types.ts).
## @log
`logDirective({ directiveName, filePath }?: { directiveName?: string, filePath?: string })`
Use the `@log` directive to log fields, queries and mutations once they are resolved.
For example, this graphql schema with the directive on the query:
```graphql
type User {
firstName: String
lastName: String
age: Int
amount: String
}
type Query {
user(firstName: String!): User @log(level: INFO)
}
```
Will log to the console in the following format:
`[] [INFO] @log - Operation Type: query, Arguments: [{"firstName":"Eddie"}], Return Type: User`
The following log levels are valid:
- `INFO`
- `DEBUG`
- `WARN`
- `ERROR`
### Logging to file
In order to migrate logs to a custom log file, you can define a filepath with the appropriate file name:
```typescript
const { logDirectiveTypeDefs, logDirectiveTransformer } = logDirective({
filePath: path.join(__dirname, 'logs', 'application.log')
})
```