Announcing DeSolver for GraphQL
DeSolver is an open source, lightweight, minimalist and un-opinionated Node.js - GraphQL framework providing a powerful yet approachable API for modularizing resolver business logic. The DeSolver class provides a simple way to organize functionality within the resolver business logic layer through abstraction and built-in caching with Redis ultimately providing a concise, multi-tier architecture approach to resolver functionality and structure. DeSolver is available as an npm package here.
The DeSolver instance methods allow a pipeline to be loaded with mini-resolver functions, and a wrapper forms around the resolver map object, permitting a series of functions in a chain or “pre-hook” functions to execute prior to a query or mutation.
DeSolver provides a developer-friendly framework for modularizing and adding to the functionality to your pre-existing resolvers and increasing optimization through in-memory data persistence.
What is GraphQL and what are resolvers?
GraphQL is a query language for API’s and a runtime for fulfilling those queries. By providing an intuitive and flexible syntax and system for describing data requirements and interactions, GraphQL returns exactly the data the client needs in one request. This allows GraphQL applications to be more efficient in client-server data queries because data is returned exactly as it is requested, thereby avoiding over and under fetching problems common to REST APIs.
The GraphQL resolver is a function that returns a response for a specific field in the GraphQL query. A resolver essentially acts as a GraphQL query handler and communicates directly with your data source.
Every resolver function in a GraphQL schema accepts four parameters: root, arguments, context, and info. DeSolver builds off this format by adding three custom parameters.
Why DeSolver (what problem in resolvers does this solve/address?)
Resolver functions can quickly become cumbersome and bloated as complex logic is added. As resolvers are unique to individual fields in your schema, adding common business logic across many resolvers may mean copying and pasting this new functionality into many individual resolvers (for example if you were adding an authentication layer, across all of your mutations).
This is where DeSolver can help! DeSolver reduces code redundancy for common functionality that needs to be invoked/run before resolvers, so individual resolvers need not be refactored to accommodate these changes. DeSolver provides a clean and concise API to modularize this resolver business logic so it can be organized into a separate part of the file, or imported from a new file, in the form of a DesolverFragment. The DeSolver class also provides a method that can be applied to the resolver map object.
Overall, DeSolver provides abstraction and modularity for easier refactoring, unit testing, and scaling with resolver functions.
Features of DeSolver
DeSolver is written in TypeScript to ensure proper type safety during development. TypeScript’s strict enforcement of type declarations minimized many type errors and bugs common to vanilla JavaScript development. Its object oriented syntax makes it friendly for adopting and utilizing object oriented design patterns in DeSolver’s architecture.
While DeSolver can be implemented just as readily with JavaScript, we recommend building your GraphQL API/server with TypeScript. Out of the box type annotations enable your IDE’s IntelliSense to provide responsive feedback and code autocompletion to ensure proper implementation of DeSolver’s methods.
For example, modeling the DesolverFragment type off of the standard resolver syntax allows users to create custom functionality that fits with the GraphQL syntax developers already know and love, while providing optimization and modularity under the hood.
DeSolver offers three additional arguments to pass: next, ds and escapeHatch. The ‘next’ argument can be used to call the next middleware in the chain. The ‘ds’ argument is a context object that can be passed between Desolver Fragment middleware functions similarly to res.locals within the Express framework.
The DeSolver escapeHatch argument can be used for additional configuration to stop the DeSolver pipeline and therefore the resolvers from completing their execution if a condition is met. The escapeHatch offers greater control to the developer, allowing for conditional logic to deny the execution of subsequent functions in the middleware pipeline. For example, developers may want to confirm the session is still valid for the client prior to completing a query.
How to Use DeSolver:
To use DeSolver in your application, create a new instance of the DeSolver class, and optionally pass in a configuration object with two properties: cacheDesolver and applyResolverType. These two properties allow the developer to declare whether or not the returned resolved values should be cached, and which type of resolver your prehook functionality should be applied to.
Utilize the desolver.use() method on the DeSolver instance to generate your pipeline of prehook functions. Any number of function arguments (in the form of a DeSolver Fragment) passed to desolver.use() will be pushed to the function pipeline. Multiple successive invocations of desolver.use() will also add additional functions to the pipeline.
DeSolver is then applied to your resolver map object (or individual resolvers) via the apply() method and essentially functions as a wrapper around the resolver map object and all resolver types therein.
Conclusion
We invite you to contribute, provide feedback, and help guide the future development of this new framework. Thank you for reading, and we look forward to the community’s response to our product!
The team for DeSolver (operating with tech accelerator, OS Labs) can be found individually via: Michael Chan, Mia Kang, Alexander Gurfinkel, and Julia Hickey or via Github.