Use a GraphQL Schema Directive to define Javascript logic, have that logic executed as a resolver.
Its a directive you can use on Fields:
directive @code(source: String!) on FIELD_DEFINITIONYou define Javascript logic in the source argument. The source is wrapped in an IFFE & passed into a https://nodejs.org/api/vm.html. Supports Promises & you can use dependency injection via the context.
$ npm install graphql-code-directiveconst { ApolloServer } = require("apollo-server");
const codeDirective = require("graphql-code-directive");
const fetch = require("node-fetch");
const typeDefs = `
type Todo {
id: ID!
userId: ID!
title: String!
completed: Boolean
}
type Query {
todos: [Todo]
@code(
source: """
const response = await context.fetch('https://jsonplaceholder.typicode.com/todos');
return response.json()
"""
)
}
`;
const server = new ApolloServer({
typeDefs: [codeDirective.typeDefs, typeDefs],
schemaDirectives: {
code: codeDirective.CodeDirective,
},
context: () => {
return {
fetch,
};
},
});
server.listen(4000).then(() => console.log("http://localhost:4000"));Inside the code source you have access to the four global variables:
rootValueargscontextresolveInfo
So for example if you were to write a 'Normal' Javascript resolver the variables would map to each argument:
function myResolver(rootValue, args, context, resolveInfo) {}Make sure you disable introspection & yes it is safe. You are in control of what the VM has access to, via context, and the Node.js team has done a good job at isolating the VM.
Unit testing could become cumbersome, this is because you would have to parse the definitions into an AST in order to access the source. You can however write solid integration tests, why not checkout https://www.apollographql.com/docs/apollo-server/testing/testing/ ?
Yes! There are 3 exports:
typeDefs- A string of the directives type definitionsCodeDirective- A legacySchemaDirectiveVisitorthat you are most likely to be familiar withcodeDirective- A functional approach to Schema Directives that returns a transformer
const { codeDirective } = require("graphql-code-directive");
const { makeExecutableSchema } = require("@graphql-tools/schema");
const { codeDirectiveTypeDefs, codeDirectiveTransformer } = codeDirective();
let schema = makeExecutableSchema({
typeDefs: [codeDirectiveTypeDefs, typeDefs],
resolvers: {},
});
schema = codeDirectiveTransformer(schema);MIT licence.