update nodejs example con node.js mongoose graphql

node.js - nodejs - graphql mongoose



¿Cómo verificar permisos y otras condiciones en la consulta GraphQL? (2)

¿Cómo podría verificar si el usuario tiene permiso para ver o consultar algo? No tengo ni idea de cómo hacer esto.

  • En args ? Cómo sería posible que eso funcione?
  • En resolve() ? Ver si el usuario tiene permiso y de alguna manera eliminar / cambiar algunos de los argumentos?

Ejemplo:

Si el usuario es "visitante", solo puede ver publicaciones públicas, "admin" puede ver todo.

const userRole = ''admin''; // Let''s say this could be "admin" or "visitor" const Query = new GraphQLObjectType({ name: ''Query'', fields: () => { return { posts: { type: new GraphQLList(Post), args: { id: { type: GraphQLString }, title: { type: GraphQLString }, content: { type: GraphQLString }, status: { type: GraphQLInt // 0 means "private", 1 means "public" }, }, // MongoDB / Mongoose magic happens here resolve(root, args) { return PostModel.find(args).exec() } } } } })

Actualización : el modelo Mongoose se ve así:

import mongoose from ''mongoose'' const postSchema = new mongoose.Schema({ title: { type: String }, content: { type: String }, author: { type: mongoose.Schema.Types.ObjectId, // From user model/collection ref: ''User'' }, date: { type: Date, default: Date.now }, status: { type: Number, default: 0 // 0 -> "private", 1 -> "public" }, }) export default mongoose.model(''Post'', postSchema)


Puede verificar el permiso de un usuario en la función de resolución o en la capa del modelo. Estos son los pasos que debe seguir:

  1. Autentica al usuario antes de ejecutar la consulta. Esto depende de su servidor y generalmente ocurre fuera de graphql, por ejemplo, mirando la cookie que se envió junto con la solicitud. Consulte esta publicación de nivel medio para obtener más detalles sobre cómo hacer esto usando Passport.js.
  2. Agregue el objeto de usuario autenticado o el ID de usuario al contexto. En express-graphql puedes hacerlo a través del argumento de contexto:

    app.use(''/graphql'', (req, res) => { graphqlHTTP({ schema: Schema, context: { user: req.user } })(req, res); }

  3. Usa el contexto dentro de la función de resolución así:

    resolve(parent, args, context){ if(!context.user.isAdmin){ args.isPublic = true; } return PostModel.find(args).exec(); }

Puede hacer verificaciones de autorización directamente en las funciones de resolución, pero si tiene una capa de modelo, le recomiendo implementarla pasando el objeto de usuario a la capa de modelo. De esa forma, su código será más modular, más fácil de reutilizar y no tendrá que preocuparse por olvidar algunas verificaciones en algún lugar de resolución.

Para obtener más información sobre la autorización, consulte esta publicación (también escrita por mí mismo): Auth en GraphQL - parte 2


Un enfoque que nos ha ayudado a resolver la autorización en nuestra compañía es pensar en los resolvedores como una composición de middleware. El ejemplo anterior es excelente, pero se volverá ingobernable a gran escala, especialmente a medida que los mecanismos de autorización se hagan más avanzados.

Un ejemplo de un resolver como una composición de middleware podría verse más o menos así:

type ResolverMiddlewareFn = (fn: GraphQLFieldResolver) => GraphQLFieldResolver;

Un ResolverMiddlewareFn es una función que toma GraphQLFieldResolver y devuelve GraphQLFieldResolver .

Para componer nuestras funciones de middleware de resolución utilizaremos (lo adivinó) la función de composición. Aquí hay un ejemplo de compose implementado en javascript, pero también puede encontrar funciones de redacción en ramda y otras bibliotecas funcionales. Compose nos permite combinar funciones simples para hacer funciones más complicadas.

Volviendo al problema de permisos de GraphQL, veamos un ejemplo simple. Digamos que queremos registrar el resolver, autorizar al usuario y luego ejecutar la carne y las papas. Compose nos permite combinar estas tres piezas para que podamos probarlas y reutilizarlas fácilmente en nuestra aplicación.

const traceResolve = (fn: GraphQLFieldResolver) => async (obj: any, args: any, context: any, info: any) => { const start = new Date().getTime(); const result = await fn(obj, args, context, info); const end = new Date().getTime(); console.log(`Resolver took ${end - start} ms`); return result; }; const isAdminAuthorized = (fn: GraphQLFieldResolver) => async (obj: any, args: any, context: any, info: any) => { if (!context.user.isAdmin) { throw new Error(''User lacks admin authorization.''); } return await fn(obj, args, context, info); } const getPost = (obj: any, args: any, context: any, info: any) => { return PostModel.find(args).exec(); } const getUser = (obj: any, args: any, context: any, info: any) => { return UserModel.find(args).exec(); } // You can then define field resolve functions like this: postResolver: compose(traceResolve, isAdminAuthorized)(getPost) // And then others like this: userResolver: compose(traceResolve, isAdminAuthorized)(getUser)