@thuoe/gql-util-directives v1.3.0
Get started
Install package:
npm install --save @thuoe/gql-util-directivesExample of importing the @regex directive & instantiating with Apollo Server:
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:
npm installRun local environment (Apollo Studio):
npm run devLink 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
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.
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:
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
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), you can override the in-memory solution with your own implementation.
Example:
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<boolean>Checks if a key exists in the cache.get: (key: string) => Promise<string>Retrieves the value associated with a key from the cache.set: (key: string, value: string) => Promise<void>Sets a key-value pair in the cache.delete: (key: string) => Promise<boolean>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
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.
@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:
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:
[<TIMESTAMP>] [INFO] @log - Operation Type: query, Arguments: [{"firstName":"Eddie"}], Return Type: User
The following log levels are valid:
INFODEBUGWARNERROR
Logging to file
In order to migrate logs to a custom log file, you can define a filepath with the appropriate file name:
const { logDirectiveTypeDefs, logDirectiveTransformer } = logDirective({
filePath: path.join(__dirname, 'logs', 'application.log')
})