Giter Club home page Giter Club logo

Comments (6)

sikanhe avatar sikanhe commented on May 21, 2024 2

I looked through Nexus' plugin, most of them should not need extensions. Just plain, reuse-able higher order functions.

Take your authentication for example, we just create a function that takes a regular resolver, and return a new resolver, and can be used on any field that requires auth

const adminOnly = (resolver) => (parent, args, ctx) => {
   if (Permissions.isAdmin(ctx.currentUser)) return resolver(ctx, parent, args);
   return null // or return error
}

fields: [
   t.field('allUsers', {
      type: ListOfUsers,
      resolver: adminOnly(() => { return Users.getAll() })
   })
]

The same pattern can be applied to pretty everything on that plugin list.

For the relay ID example. You create a new GraphQL RelayId Scalar type, which is used for parsing and serializing a leaf node. Roughly

const globalId = t.scalarType('globalId', {
   serialize: toGlobalId,
   parse: fromGlobalId
})

fields: [
  t.defaultField("id", globalId)
]

from gqtx.

sikanhe avatar sikanhe commented on May 21, 2024

Could be a great idea - any rough example what would the API look like?

from gqtx.

n1ru4l avatar n1ru4l commented on May 21, 2024

A quick sketch:

type VisibilityExtension = {
  visibilityAccess?: "public" | "hidden";
}

type LiveQueryExtensions = {
  liveQuery?: {
    buildResourceIdentifier: (source: unknown) => string;
  },
}

type TExtensions = VisibilityExtension & LiveQueryExtensions

const t = createTypesFactory<TContext, TExtensions>();

const Character = t.objectType({
  name: "Character",
  extensions: {
    visibilityAccess: "public"
  },
  fields: () => [
    t.field("name", {
      type: t.NonNull(t.String),
      resolve: (character) => "HI",
    }),
  ],
});

Note extensions are just there for adding metadata to the schema. Actually doing sth with the metadata must be done in user-land. We could however provide some way of at least providing typings for the available extensions.

See graphql/graphql-js#2097

I guess we will hit limits on what we can do. E.g. buildResourceIdentifier would receive the source object of the object type (in the user-land implementation), however, I am not aware of how that could be typed properly in a general way.

from gqtx.

n1ru4l avatar n1ru4l commented on May 21, 2024

I created #36 for a basic implementation. It does however not use any typing magic, but I guess stuff like that could still be introduced later on.

from gqtx.

Ericnr avatar Ericnr commented on May 21, 2024

I was asked for some examples of use cases, here are some.

interface TExtensions {
  authorize?: (ctx: TContext) => boolean;
  authenticate?: boolean;
}

const t = createTypesFactory<TContext, TExtensions>({
  extensions: {
    authorize: { // first arg is whatever value passed to authorize, in this case a fn 
      extendObjectTypeResolver: (checkAuth) => async (parent, args, ctx, info) => {
        const isAuthorized = await checkAuth(ctx);
        if (!isAuthorized) throw new AuthorizationError()
      }
    },
    authenticate: {
      extendObjectTypeResolver: (needsAuthentication) => async (parent, args, ctx, info) => {
         const isAuthenticated = await ctx.checkDbForSession(ctx.user.id);
          if (!isAuthenticated) throw new AuthenticationError()
      }
    },
  }
});

const Mutation = t.mutationType({
  fields: [
    t.field('doStuffOnlyAdminShouldDo', {
      type: SomeResponseType,
      args: {
        id: t.arg(t.NonNullInput(t.ID)),
      },
      extensions: {
        authorize: (ctx) => ctx.checkRoles(['ADMIN']),
        authenticate: true,
      },
      resolve: (_, args, ctx) => {
        ...
      },
    }),
  ],
});

Now for a more complicated example, and probably much harder to define with TS

import { fromGlobalId } from 'graphql-relay';
const t = createTypesFactory<TContext, TExtensions>({
  extensions: {
    parse: {
      extendInputType: (parseFn) => async (args, argName) => {
        // mutates the args object before the resolver runs
        // parse function will also validate inputs and throw if invalid
        args[argName] = await parseFn(args[argName]);
      },
    },
  },
});

const MyInputType = t.inputObjectType<{ id: string }>({
  name: 'MyInputType',
  extensions: {
    // arg should have the same type as input
    parse: async (input) => {
      // input.id is a Relay Global Object Identifier, it needs to be parsed into the id that is actually used in the db
      return {
        realId: fromGlobalId(input.id),
      };
    },
  },
  fields: () => [t.defaultField('id', t.ID)],
});

const Mutation = t.mutationType({
  fields: [
    t.field('doStuffOnlyAdminShouldDo', {
      type: SomeResponseType,
      args: {
        input: t.arg(t.NonNullInput(MyInputType)),
      }, 
      // type of `args.input` is now the return type of its parse extension
      // { realId: string } 
      resolve: (_, args, ctx) => {},
    }),
  ],
});

I expect it wouldnt be feasible, but a simpler version where you only validate the input and throw an exception if its bad (without mutating args) might be.

There is a lot of inspiration to be taken from nexus's plugins https://nexusjs.org/docs/api/plugins

from gqtx.

Ericnr avatar Ericnr commented on May 21, 2024

yea, thats a totally fair decision. These are quality of life features only that could make the complexity explode

from gqtx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.