@jdrydn/graphql-keyvalue
TypeScript icon, indicating that this package has built-in type declarations

1.1.0 • Public • Published

graphql-keyvalue

NPM CI

Standalone GraphQL Scalar type for Key-Value hashes in JavaScript.

type User {
  id: ID!
  name: String!
  state: KeyValue!
}

extend type Query {
  user: User!
}

extend type Mutation {
  updateUserState(id: ID!, state: KeyValue!): KeyValue!
}

Install

$ npm install --save graphql @jdrydn/graphql-keyvalue

From your codebase, you can either use predefined items (type definition & resolver) directly in your project or define the scalar yourself & include the scalar instance in your resolvers.

The following example uses graphql-tools & the predefined items:

const assert = require('assert');
const { typeDefs: keyValueTypeDefs, resolvers: keyValueResolvers } = require('@jdrydn/graphql-keyvalue');

const typeDefs = /* GraphQL */`
  type User {
    id: ID!
    name: String!
    email: String!
    state: KeyValue!
  }

  type Query {
    user(id: ID!): User!
  }

  type Mutation {
    updateUserState(id: ID!, state: KeyValue!): KeyValue!
  }
`;

const resolvers = {
  Query: {
    async user(_, { id }) {
      const user = await getUser(id);
      return user && user.id ? user : null;
    },
  },
  Mutation: {
    async updateUserState(_, { id, state }) {
      const user = await getUser(id);
      assert(user && user.id, 'User not found');

      // Merge in the user state
      user.state = { ...user.state, ...state };

      await setUser(id, user);
      return user.state;
    },
  },
};

const schema = makeExecutableSchema({
  typeDefs: [ typeDefs, keyValueTypeDefs ],
  resolvers: [ resolvers, keyValueResolvers ],
});

Whereas this example uses apollo-server & includes the scalar instance KeyValue in its resolvers:

const { ApolloServer } = require('apollo-server');
const { KeyValue } = require('@jdrydn/graphql-keyvalue');

const typeDefs = /* GraphQL */`
  type User {
    id: ID!
    name: String!
    email: String!
    state: KeyValue!
  }

  type Query {
    user(id: ID!): User!
  }

  type Mutation {
    updateUserState(id: ID!, state: KeyValue!): KeyValue!
  }

  # @NOTE You must define the scalar yourself
  scalar KeyValue
`;

const resolvers = {
  Query: {
    async user(_, { id }) {
      const user = await getUser(id);
      return user && user.id ? user : null;
    },
  },
  Mutation: {
    async updateUserState(_, { id, state }) {
      const user = await getUser(id);
      assert(user && user.id, 'User not found');

      // Merge in the user state
      user.state = { ...user.state, ...state };

      await setUser(id, user);
      return user.state;
    },
  },
  KeyValue,
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

Usage

Once the type definition & resolver is configured, you can send & receive simple key-value objects in GraphQL in both queries & mutations.

Query

query GetUser {
  user(id: "1") {
    id
    name
    state
  }
}
{
  "data": {
    "user": {
      "id": "1",
      "name": "jdrydn",
      "state": {
        "signedInWith": "APPLE",
        "finishedOnboarding": true
      }
    }
  }
}

Mutation

mutation UpdateUser {
  updateUserState(id: "1", state: { "finishedOnboarding": true })
}
{
  "data": {
    "updateUserState": {
      "signedInWith": "APPLE",
      "finishedOnboarding": true
    }
  }
}

Notes

  • The scalar will pass an object for input values & expects an object to be passed for output values.
  • Trying to send/receive a JSON object/array will throw an error.
  • Why only one-level deep? One of the benefits of GraphQL is the strictly typed schema that is produced. This isn't trying to defy the GraphQL schema, merely extend it to cover more use-cases.
  • After more than one level? Check out graphql-type-json.
  • Any questions or suggestions please open an issue.

Readme

Keywords

Package Sidebar

Install

npm i @jdrydn/graphql-keyvalue

Weekly Downloads

0

Version

1.1.0

License

MIT

Unpacked Size

4.88 kB

Total Files

2

Last publish

Collaborators

  • jdrydn