Walkthrough of Using GraphQL Shield Posted in Security Vyom Srivastava August 4, 2022 GraphQL Shield is a library that provides a simple way to add authorization to your GraphQL server. It uses the Apollo server and the graphql-shield middleware to provide a declarative way to define per-field or per-type access control rules for your GraphQL schema. You can use it to add authorization to your existing GraphQL server, or you can use it to build a new server from scratch. Importance of Using GraphQL-Shield GraphQL Shield is a great tool for protecting your GraphQL server from malicious queries. It allows you to whitelist the fields and arguments you want to expose and block all other requests. This ensures that your data is safe from unauthorized access and that your server can’t be overloaded with unwanted requests. Another important benefit of using GraphQL Shield is that it can help improve the performance of your application. By declaratively specifying the data requirements of your application, you can optimize the underlying query execution to minimize the amount of data fetched and minimize round-trips to the server. This can result in a significant performance improvement for data-heavy applications. GraphQLShield is also very easy to use and integrates well with existing GraphQL servers. It’s well-documented, and a community of users can help you if you face any problems. Overall, it’s a great tool for protecting your GraphQL API. Create a GraphQL Server To use GraphQL Shield in your project’s server, first, you need to create a new GraphQL instance with an Express framework and the GraphQL plugin. Don’t miss our workshop with Apollo GraphQL: What If All Your Data Was Accessible in One Place Step 1: Initializing the Project Here we’re going to create a project directory and initiate a Node.js project. Once in that directory, we’ll set up a package.json file so that we have a place to stash all of our dependencies! npm init Step 2: Installing Dependencies Next, we create an empty script file called server.js and install some dependencies. npm install --save graphql npm install express npm install --save express-graphql npm install @graphql-tools/schema Step 3: Creating Schema With Server Now, let’s create a simple schema with some dummy data and resolvers that map to those dummy data entries. const express = require('express'); const {graphqlHTTP } = require('express-graphql'); const {makeExecutableSchema} = require('@graphql-tools/schema'); // GraphQL Schema const typeDefs = ` type Query { me: User users: [User!]! } type Mutation { createUser(input: CreateUserInput): User } type User { id: ID! name: String! email: String! role: Role } input CreateUserInput { name: String! email: String! } enum Role { USER ADMIN } `; // Root resolver const users = [ { id: "1", name: "Vyom", role: "USER", email: "vyom@geekyhumans.com" }, { id: "2", name: "Thor", role: "USER", email: "thor@graphql.com" }, { id: "3", name: "Captain America", role: "USER", email: "captain@graphql.com" }, ]; const resolvers = { Query: { users: () => users, me: (_, __, ctx) => users.find(({ id }) => id === ctx.headers["user=id"]), }, Mutation: { createUser: (_, {input}) => ({ id: "4", role: "USER", ...input, }), }, } const schema = makeExecutableSchema({ typeDefs, resolvers }); const app = express(); // Create an express server and a GraphQL endpoint app.use('/graphql', graphqlHTTP ({ schema: schema, graphiql: { headerEditorEnabled: true, }, }), ); app.listen(4000, () => { console.log('Express GraphQL Server Now Running On localhost:4000/graphql'); }); After successfully installing the Server, run the following command to check your server to see if it returns the correct output. node server.js Output: Step 4: Adding graphql-shield to the Server Before we start, you must install graphql-shield from npm. Run the following command in your terminal: yarn add graphql-middleware yarn add graphql-shield Before we can design the schema for our project, we first need to apply middleware to our schema and pass in that GraphQL Shield library. After that, we will import an instance of Shield from the GraphQL Shield package. Note: As we move forward with the Shield Library, other package imports will automatically be available. const express = require("express"); const { graphqlHTTP } = require("express-graphql"); const { makeExecutableSchema } = require("@graphql-tools/schema"); const { applyMiddleware } = require("graphql-middleware"); const { shield, rule, and, inputRule } = require("graphql-shield"); Adding GraphQL Schema and Root resolver as before: const typeDefs = ` type Query { me: User users: [User!]! } type Mutation { createUser(input: CreateUserInput): User } type User { id: ID! name: String! email: String! role: Role } input CreateUserInput { name: String! email: String! } enum Role { USER ADMIN } `; // Root resolver const users = [ { id: "1", name: "neeraj", role: "USER", email: "neeraj@graphql.com" }, { id: "2", name: "rahul", role: "USER", email: "rahul@graphql.com" }, { id: "3", name: "rohan", role: "USER", email: "rohan@graphql.com" }, ]; const resolvers = { Query: { users: () => users, me: (_, __, ctx) => users.find(({ id }) => id === ctx.headers["user=id"]), }, Mutation: { createUser: (_, {input}) => ({ id: "4", role: "USER", ...input, }), }, } const schema = makeExecutableSchema({ typeDefs, resolvers }); Define the isAuthenticated rule, then define the signature of the rule, and then pass the User-Id as a header to the GraphQL server on request. const isAuthenticated = rule()(async (parent, args, ctx, info) => { return !!ctx.headers["user-id"]; }); Now let’s create another rule that states if a user wants to run the query, they must be admin and authenticated. const isAdmin = rule()(async (parent, args, ctx, info) => { const user = users.find(({ id }) => id === ctx.headers["user-id"]); return user && user.role === "ADMIN"; }); Here we are creating another rule called inputRule: const isNotAlreadyRegistered = inputRule()((yup) => yup.object({ input: yup.object({ name: yup.string().required(), email: yup .string() .email() .required() .notOneOf( users.map(({ email }) => email), "A user exists. Choose another." ), }), }) ); Now, we are declaring the permission we’ll need to invoke shield and pass to the constructor and object that will map to our query and root types. const permissions = shield({ Query: { // "*": deny, // "*": allow, users: and(isAuthenticated, isAdmin), me: isAuthenticated, }, Mutation: { // "*": deny, createUser: isNotAlreadyRegistered, }, }); Then we’ll declare schema with permissions, and use applymiddleware to pass schema and permission. const schemaWithPermissions = applyMiddleware(schema, permissions); Now, we have to update our application and then update the graphqlhttp package to point to the new schema with permissions instead of the old schema. const app = express(); Create an e=Express server and a GraphQL endpoint. app.use( "/graphql", graphqlHTTP({ schema: schemaWithPermissions, graphiql: { headerEditorEnabled: true, }, }) ); app.listen(4000, () => { console.log(`Server listening on http://localhost:4000`); }); Output: Now, if you run the server by going to this link — https://localhost:4000/graphql — you will get an error because you are not authorized. But if we give it permission bypassing the user-id in the request headers like below, we can get the info: And you can also reverse-check the authenticated user. Output: Here we are checking the new input rule, and for that, we are creating a new user with the same email address. It returns an error, so the rule is working correctly. Now let’s try it with a different email address: Final Words GraphQL Shield is an original library that provides a way to protect your GraphQL API from unauthenticated users. This is particularly handy for public APIs where you don’t necessarily want to hardcode a username and password. Instead, you can use role-based permissions to protect your API. Keep in mind of a new development on the graphql-shield repository since we wrote this piece: “graphql-shield is currently unmaintained.” The latest API insights straight to your inbox