Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
graphql-middleware
Advanced tools
The graphql-middleware package allows you to apply middleware functions to your GraphQL resolvers, enabling you to add custom logic before or after the execution of a resolver. This can be useful for tasks such as authentication, logging, error handling, and more.
Authentication Middleware
This example demonstrates how to use graphql-middleware to add an authentication check to a GraphQL resolver. If the user is not authenticated, an error is thrown.
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { applyMiddleware } = require('graphql-middleware');
const typeDefs = `
type Query {
secret: String
}
`;
const resolvers = {
Query: {
secret: () => 'This is a secret message'
}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const isAuthenticated = async (resolve, parent, args, context, info) => {
if (!context.user) {
throw new Error('Not authenticated');
}
return resolve(parent, args, context, info);
};
const schemaWithMiddleware = applyMiddleware(schema, isAuthenticated);
Logging Middleware
This example shows how to use graphql-middleware to log messages before and after a resolver is executed. This can be useful for debugging and monitoring purposes.
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { applyMiddleware } = require('graphql-middleware');
const typeDefs = `
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => 'Hello world'
}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const logger = async (resolve, parent, args, context, info) => {
console.log('Request started');
const result = await resolve(parent, args, context, info);
console.log('Request finished');
return result;
};
const schemaWithMiddleware = applyMiddleware(schema, logger);
Error Handling Middleware
This example demonstrates how to use graphql-middleware to catch and handle errors that occur during the execution of a resolver. The error is logged, and a generic error message is returned to the client.
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { applyMiddleware } = require('graphql-middleware');
const typeDefs = `
type Query {
throwError: String
}
`;
const resolvers = {
Query: {
throwError: () => { throw new Error('An error occurred'); }
}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const errorHandler = async (resolve, parent, args, context, info) => {
try {
return await resolve(parent, args, context, info);
} catch (error) {
console.error('Error:', error);
throw new Error('Internal server error');
}
};
const schemaWithMiddleware = applyMiddleware(schema, errorHandler);
graphql-shield is a package that allows you to create a permission layer for your GraphQL API. It provides a way to define rules and apply them to your schema, ensuring that only authorized users can access certain parts of your API. Compared to graphql-middleware, graphql-shield is more focused on authorization and permissions, while graphql-middleware is more general-purpose.
apollo-server is a popular GraphQL server implementation that includes built-in support for middleware through its plugin system. While it is not as flexible as graphql-middleware in terms of applying middleware to individual resolvers, it provides a comprehensive set of features for building and deploying GraphQL APIs, including schema stitching, subscriptions, and more.
graphql-yoga is a fully-featured GraphQL server that comes with built-in support for middleware. It is built on top of apollo-server and express, and provides a simple and easy-to-use API for setting up a GraphQL server. Like apollo-server, it is not as flexible as graphql-middleware for applying middleware to individual resolvers, but it offers a lot of functionality out of the box.
Split up your GraphQL resolvers in middleware functions.
GraphQL Middleware is a schema wrapper which allows you to manage additional functionality across multiple resolvers efficiently.
NOTE: As of 3.0.0
graphql-middleware
no longer wraps introspection queries.
NOTE: As of 5.0.0
graphql-middleware
no longer supports GraphQL Yoga out of the box. We might bring back the support if the library becomes maintained again. We are keeping the docs as the reference for older versions.
yarn add graphql-middleware
GraphQL Middleware lets you run arbitrary code before or after a resolver is invoked. It improves your code structure by enabling code reuse and a clear separation of concerns.
const { ApolloServer } = require('apollo-server')
const { makeExecutableSchema } = require('@graphql-tools/schema')
const typeDefs = `
type Query {
hello(name: String): String
bye(name: String): String
}
`
const resolvers = {
Query: {
hello: (root, args, context, info) => {
console.log(`3. resolver: hello`)
return `Hello ${args.name ? args.name : 'world'}!`
},
bye: (root, args, context, info) => {
console.log(`3. resolver: bye`)
return `Bye ${args.name ? args.name : 'world'}!`
},
},
}
const logInput = async (resolve, root, args, context, info) => {
console.log(`1. logInput: ${JSON.stringify(args)}`)
const result = await resolve(root, args, context, info)
console.log(`5. logInput`)
return result
}
const logResult = async (resolve, root, args, context, info) => {
console.log(`2. logResult`)
const result = await resolve(root, args, context, info)
console.log(`4. logResult: ${JSON.stringify(result)}`)
return result
}
const schema = makeExecutableSchema({ typeDefs, resolvers })
const schemaWithMiddleware = applyMiddleware(schema, logInput, logResult)
const server = new ApolloServer({
schema: schemaWithMiddleware,
})
await server.listen({ port: 8008 })
Execution of the middleware and resolver functions follow the "onion"-principle, meaning each middleware function adds a layer before and after the actual resolver invocation.
The order of the middleware functions in the middlewares array is important. The first resolver is the "most-outer" layer, so it gets executed first and last. The second resolver is the "second-outer" layer, so it gets executed second and second to last... And so forth.
You can read more about GraphQL Middleware in this fantastic article.
const { ApolloServer } = require('apollo-server')
const { makeExecutableSchema } = require('@graphql-tools/schema')
// Minimal example middleware (before & after)
const beepMiddleware = {
Query: {
hello: async (resolve, parent, args, context, info) => {
// You can use middleware to override arguments
const argsWithDefault = { name: 'Bob', ...args }
const result = await resolve(parent, argsWithDefault, context, info)
// Or change the returned values of resolvers
return result.replace(/Trump/g, 'beep')
},
},
}
const typeDefs = `
type Query {
hello(name: String): String
}
`
const resolvers = {
Query: {
hello: (parent, { name }, context) => `Hello ${name ? name : 'world'}!`,
},
}
const schema = makeExecutableSchema({ typeDefs, resolvers })
const schemaWithMiddleware = applyMiddleware(
schema,
metricsMiddleware,
authMiddleware,
beepMiddleware,
)
const server = new ApolloServer({
schema: schemaWithMiddleware,
})
await server.listen({ port: 8008 })
graphql-yoga
graphql-yoga
has built-in support forgraphql-middleware
!
import { GraphQLServer } from 'graphql-yoga'
import { authMiddleware, metricsMiddleware } from './middleware'
const typeDefs = `
type Query {
hello(name: String): String
}
`
const resolvers = {
Query: {
hello: (parent, { name }, context) => `Hello ${name ? name : 'world'}!`,
},
}
const server = new GraphQLServer({
typeDefs,
resolvers,
middlewares: [authMiddleware, metricsMiddleware],
documentMiddleware: [],
})
server.start(() => console.log('Server is running on localhost:4000'))
lightstep
tracesA middleware is a resolver function that wraps another resolver function.
export declare type IMiddlewareResolver<
TSource = any,
TContext = any,
TArgs = any
> = (
resolve: Function,
parent: TSource,
args: TArgs,
context: TContext,
info: GraphQLResolveInfo,
) => Promise<any>
export interface IMiddlewareWithOptions<
TSource = any,
TContext = any,
TArgs = any
> {
fragment?: IMiddlewareFragment
fragments?: IMiddlewareFragment[]
resolve?: IMiddlewareResolver<TSource, TContext, TArgs>
}
export type IMiddlewareFunction<TSource = any, TContext = any, TArgs = any> =
| IMiddlewareWithOptions<TSource, TContext, TArgs>
| IMiddlewareResolver<TSource, TContext, TArgs>
interface IMiddlewareTypeMap {
[key: string]: IMiddlewareFunction | IMiddlewareFieldMap
}
interface IMiddlewareFieldMap {
[key: string]: IMiddlewareFunction
}
type IMiddleware = IMiddlewareFunction | IMiddlewareTypeMap
function middleware(
generator: (schema: GraphQLSchema) => IMiddleware,
): MiddlewareGenerator
function applyMiddleware(
schema: GraphQLSchema,
...middleware: (IMiddleware | MiddlewareGenerator)[]
): GraphQLSchema & {
schema?: GraphQLSchema
fragmentReplacements?: FragmentReplacement[]
}
/**
* Applies middleware to a schema like `applyMiddleware` but only applies the
* middleware to fields that have non-default resolvers. This method can be
* useful if you want to report performance of only non-trivial methods.
*/
function applyMiddlewareToDeclaredResolvers(
schema: GraphQLSchema,
...middleware: (IMiddleware | MiddlewareGenerator)[]
): GraphQLSchema & {
schema?: GraphQLSchema
fragmentReplacements?: FragmentReplacement[]
}
In some cases, your middleware could depend on how your schema looks. In such situations, you can turn your middleware into a middleware generator. Middleware generators are denoted with function middleware
and receive schema
as the first argument.
const schemaDependentMiddleware = middleware((schema) => {
return generateMiddlewareFromSchema(schema)
})
const schemaWithMiddleware = applyMiddleware(
schema,
schemaDependentMiddleware,
someOtherOptionalMiddleware,
etc,
)
Fragments are a way of expressing what information your resolver requires to make sure it can execute correctly. They are primarily used in schema forwarding when the client might not always request all the fields your resolver demands. Because of that, we need to provide a way of telling what other information we need from a remote schema and that's why we use fragments.
You can read more about fragments in the graphql-binding
repository and on graphql-tools
documentation website.
GraphQL Middleware provides a convenient way to quickly and easily add fragments to your middleware. This might turn out particularly useful when your middleware depends on resolver data.
We've made fragments extremely flexible by using the general API which, if you have ever run over fragments, you probably already know.
// Schema wide - gets applied to every field.
const middlewareWithFragments = {
fragment: `fragment NodeID on Node { id }`,
resolve: (resolve, { id }, args, ctx, info) => {
const foo = doSomethingWithID(id)
return resolve(foo)
},
}
// Type wide - gets applied to every field of certain type.
const middlewareWithFragments = {
Query: {
fragment: `fragment NodeID on Node { id }`,
resolve: (resolve, { id }, args, ctx, info) => {
const foo = doSomethingWithID(id)
return resolve(foo)
},
},
Mutation: {
fragments: [
`fragment NodeID on Node { id }`,
`fragment NodeSecret on Node { secret }`,
],
resolve: (resolve, parent, args, ctx, info) => {
return resolve(parent, customArgs)
},
},
}
// Field scoped - gets applied to particular field.
const middlewareWithFragments = {
Query: {
node: {
fragment: `fragment NodeID on Node { id }`,
resolve: (resolve, { id }, args, ctx, info) => {
const foo = doSomethingWithID(id)
return resolve(foo)
},
},
books: (resolve, parent, args, ctx, info) => {
return resolve(parent, customArgs)
},
},
}
const { schema, fragmentReplacements } = applyMiddleware(
schema,
middlewareWithFragments,
someOtherMiddleware,
)
graphql-middleware
automatically merges fragments from multiple middlewares if possible. Otherwise, validation function throws an error.
Yes. Nevertheless, we encourage you to use it in combination with Yoga. Combining the power of middlewares
that GraphQL Middleware offers, with documentMiddleware
which Yoga exposes, gives you unparalleled control over the execution of your queries.
directives
?GraphQL Middleware and directives
tackle the same problem in a completely different way. GraphQL Middleware allows you to implement all your middleware logic in your code, whereas directives encourage you to mix schema with your functionality.
GraphQL Middleware allows you to modify the context of your resolvers, but we encourage you to use GraphQL Yoga's documentMiddleware
for this functionality instead.
Thanks to everyone who supported the development of this project. It's an honor to lead a project that helps so many people.
Thank you! :heart:
FAQs
GraphQL Middleware done right!
We found that graphql-middleware demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.