Friday, July 31, 2020

AWS Lambda Security

The security of an application is one of the most important non-functional requirements. Every application and underneath infrastructure has to go through strict security guidelines to secure the whole system. Serverless architecture is getting more attention from the developer community, so do hackers as well and AWS Lambda is a widely used service that hosts serverless architecture applications. 


There are several myths around Lambda and serverless architecture and the most common one is that whole security for these apps relies on AWS. But that is not correct. AWS follows the shared responsibility model where AWS manages the infrastructure, foundation services, and the operating system. And the customer is responsible for the security of the code, data being used by Lambda, IAM policies to access the Lambda service.




By developing applications using a serverless architecture, you relieve yourself of the daunting task of constantly applying security patches for the underlying OS and application servers. And concentrate more on the data protection for the application.


In this article, we are going to discuss many different aspects of the security of the Lambda function.

Data Protection in AWS Lambda

As part of data protection in AWS Lambda, we first need to protect account credentials and set up the individual user accounts with IAM policies enabled. We need to ensure that each user is given the least privileges to fulfill their jobs.


Following are different ways, we can secure the data in Lambda:


  • Use multi-factor authentication (MFA) for authentication to each user account.


  • Use SSL/TLS to have communication between Lambda and other AWS resources.


  • Set up CloudTrail service with API and user activity logging.


  • Use the AWS server-side and in-transit encryption solutions, along with all default security controls within AWS services.


  • Never put sensitive identifying information such as account numbers, credentials of the services in the code.


Encryption in Transit

Lambda API endpoints are accessed through secure connections over HTTPS. When we manage Lambda resources with the AWS Management Console, AWS SDK, or the Lambda API, all communication is encrypted with Transport Layer Security (TLS).


When a Lambda function connects to a file system, It uses encryption in transit for all connections.

Encryption at rest

Lambda uses environment variables to store secrets. These environment variables are encrypted at rest.

There are two features available in Lambda while encrypting the environment variables:


AWS KMS keys -

For each Lambda function, we can define a KMS key to encrypt the environment variable. These keys can be either AWS managed CMKs or customer-managed CMKs.


Encryption helpers -

By enabling this feature, environment variables are encrypted at the client-side even before sending it to Lambda. This ensures secrets are not displayed unencrypted on AWS Lambda console or in CLI or through API.


Lambda always encrypts files that are uploaded to Lambda, including deployment packages and layer archives.

Amazon CloudWatch Logs and AWS X-Ray used for logging, tracing, and monitoring logs also encrypt data by default and can be configured to use a CMK.


IAM Management for AWS Lambda

IAM management in AWS typically handles users, groups, roles, and policies. For a new account by default, IAM users and roles don't have permission for Lambda resources. An IAM administrator must first create IAM policies that grant users and roles permission to perform specific API operations on the Lambda and other AWS services. The administrator must then attach those policies to the IAM users or groups that require those permissions. 


There are a few best practices to handle the IAM policies:

  • AWS has already created many managed policies for the Lambda function. So to start quickly attach these policies to the users.

  • Start with the least privileges rather than being too lenient initially and trying to tighten them later.

  • For sensitive operations, enable multi-factor authentication (MFA). 

  • Use Policy conditions to enhance security. For example - allow a request to come only from a range of IP addresses or allow a request to come only within a specified date or time range.


Auto-Generate Least-Privileged IAM Roles

An open-source tool for AWS Lambda security is available that automatically generates AWS IAM roles with the least privileges required by your functions. The tool:


  • Saves time by automatically creating IAM roles for the function

  • Reduces the attack surface of Lambda functions

  • Helps create least-privileged roles with the minimum required permissions

  • Supports Node.js and Python runtimes for now.

  • Supports Lambda, Kinesis, KMS, S3, SES, SNS, DynamoDB, and Step Functions services for now.

  • Works with the serverless framework


Logging and Monitoring for AWS Lambda

In AWS, we have two logging tools relevant to watch for security incidents in AWS Lambda: Amazon CloudWatch and AWS CloudTrail.


For Lambda security, CloudWatch should be used to: 

  • Monitor “concurrent executions” metrics for a function. Investigate the spikes in AWS Lambda concurrent executions on a regular basis.

  • Monitor Lambda throttling metrics 

  • Monitor AWS Lambda error metrics. If you observe a spike in timeouts, it may indicate a DDoS attack 

When we enable data event logging, CloudTrail logs function invocations and we can view the identities invoking the functions and their frequency. Each invocation of the function is logged in CloudTrail with a timestamp. This helps to verify the source caller.


One of the most significant benefits of enabling CloudWatch and CloudTrail for your AWS Lambda serverless functions comes from the built-in automation. Notifications, messages, and alerts can be set up that are triggered by events in your AWS ecosystem. These alerts enable you to react to potential security risks as soon as they are introduced. 

Securing APIs with API Gateway

AWS API Gateway along with AWS Lambda enables us to build secure APIs with a serverless architecture. With this, we can run a fully managed REST API that integrates with AWS Lambda functions to execute business logic.


Following are controls that can be used to control access to APIs:


  • Generate API keys and use it with usage plans with usage quota limiting

  • Use AWS IAM roles and policies to grant access to user

  • Use Cognito user pools to enable authentication. It has features to authenticate using third party providers like Facebook, Twitter, GitHub, etc..

  • Use Lambda authorizer functions for controlling access at API methods levels. It can be done using token authentication as well as header data, query string parameters, URL paths, or stage variables.


Summary

Serverless architecture takes away a lot of pain in operation management. This also offloads the onus of patching OS and other infrastructure levels of security concerns. However, it opens new vectors for attacking like events injection and many others which are not known yet. But security basics remain the same and application and data-level security have to be enabled and monitored regularly to avoid any security attacks. 


 



Monday, June 29, 2020



An in-depth overview of the major topics around debugging serverless

Serverless computing has changed the way enterprises design and build their production applications. It is no longer a hype but a mainstream application development technology. It has become a game-changer, enabling developers to build and ship faster. Now, a developer can concentrate on writing business logic rather than worrying about server provisioning, maintenance, idle capacity management, auto-scaling, and more. 

AWS Lambda function is now a well-known service that builds serverless applications. Since its inception in 2014, it has been adopted very well by developers and architects.

AWS serverless application comprises several components. However, Function is the backbone of it. Other AWS services interact with each other through Function, completing the serverless workflow.

In this article, we are going to do a deep-dive and understand the testing and debugging of a typical Lambda serverless application. Before going further, let us first understand all the components involved in building a serverless application.

1. Event Source: - Lambda Function doesn’t have a REST API endpoint. Hence, it cannot be called directly from outside clients. It needs event triggering to get invoked. An event can be:
    Data record change of a DynamoDB table
    Request to a REST endpoint through API Gateway
    Change in the state of resource e.g. file uploaded to the S3 Bucket

It can also be triggered through CloudWatch Events. As Lambda has been evolving, it’s constantly adding many other types of events. 

2. Function: - All the functional logic goes here. It does support many languages, including but not limited to Java, Go, Node, Python. The event source triggers it and then connects to all other AWS Services to create a serverless pattern.

3.Services: - It can be either an AWS service or an external service. These services would be required either to persist or retrieve the data from other systems. Some of the services like SQS, SNS complements serverless flow with event-driven architecture.

So, we need to keep these components in mind while understanding the debugging of an AWS serverless application.

Debugging monolithic apps vs serverless

With all of the talk these days about microservices and serverless applications, monolithic applications have become the scourge of cloud systems design. However, it is known that there are certain aspects of application design where monolith still does better than microservices and serverless. And, debugging is one of them. Microservice and serverless didn’t come to address any of the existing debugging challenges of a monolith application. In contrast, it has brought new challenges though.

Different Aspects of Debugging a Monolith

1. Debugging a monolith application conjures specific images in the developer’s mind. The most common image would be the local environment using the favorite IDE ( Eclipse, VSC, IntelliJ, etc..). It is as simple as creating a breakpoint at the line of code that needs to be inspected and run the application in debug mode.


2. In a monolith, most of the issues are debugged in local as it uses to have only a few failures points UI/Backend or the Database behavior and in most cases, these can be set up in the local environment. 

3. In a monolith, eclipse/IntelliJ is used to do remote debugging as well. It could be done from Eclipse, VSC, or STS IDE. It would certainly need privileged access to connect to the remote servers. It is being used in exceptional cases where simulating the same scenario was not at all possible in local.

4. Debugging a monolith application is simple and easy since all tasks run on a single process, it makes things much easier to test.

5. Though debugging a monolith is easier but it takes a lot of effort. A large application may take minutes instead of seconds to startup which slows down debugging effort. Also, reaching to a debugging breakpoint takes time if the monolith app flow is huge.

This was all about monolith debugging. Serverless has changed the way we do design and build the applications and that changes the debugging as well.

Different Aspects of Debugging a Serverless

1. With serverless applications, logging has become the most common way of debugging issues. Disk space has become cheaper and logs can be rolled after 30 days or so. It has become important to log everything that the service does and that helps developers understand the real behavior.

2. Serverless flow generally has more components involved, hence, failure points have increased. All these components may not be able to set up in the local environment. 

3. Serverless applications by nature don’t give an option for remote debugging.

4. Serverless needs hopping across process, machine, and networking boundaries and that introduce many hundreds of new variables and opportunities for things to go wrong – many of which are out of the developer’s control but he has to debug when things go wrong.

5. Serverless brings loose dependency between components and as such it is harder to determine when compatibility or interface contracts are broken. It won’t know something has gone wrong until well into the runtime.

So, we can see that there are many differences in the way debugging works in monolith and serverless. Let’s talk a little more about the challenges serverless brings in debugging applications.

 The Challenges of Debugging a Serverless

A serverless application in AWS has event sources like API Gateway, DynamoDB, SQS, Kinesis invoking a Lambda Function. Then, the function makes calls to DBs, SNS/SQS, the workload on ECS/EKS, or even services running outside AWS.

So, it is comprised of so many components and any issue may require to debug these so many components. Here are a few common challenges:

1. Distributed Components - serverless application components are highly distributed. It can be within AWS or outside. Simulating these local may not be always possible and doing debugging on a live environment is neither cheap nor easy.

2. Lack of remote debugging - serverless application by nature doesn’t give access to server and OS level so remote debugging is not an option. For rare cases, where the developer is not able to understand why the issue is happening and cannot do live debugging makes him handicapped.

3. Ephemeral - servers are ephemeral by design in serverless computing and that gives very limited opportunity to the developer to debug the issue.

4. High Cost - as most of the debugging are done in a live environment, it adds to the monthly bill. An application running on a live environment and being debugged for bugs/defects really hit the ROI to the business.

5. Lack of frameworks for local setup - there are very few frameworks available to do setup Lambda in locally and those also come with limitations. 

Debugging Serverless Offline

Running and Debugging an application in local is a critical part of software development. Understanding that, AWS and even open source community came up with several tools that enable Lambda Function and serverless workflow to run and debug in local.

AWS Serverless Application Model (SAM) 

It is an open-source framework majorly contributed by AWS that we can use to build serverless applications on AWS. SAM has two major components:

SAM template specification - We use this specification to define serverless application. We can describe the functions, APIs, permissions, configurations, and events in the specification.

SAM templates are an extension of AWS CloudFormation templates, with some additional components that make them easier to work with. Below is the YAML template for SAM specification:


SAM command-line interface (SAM CLI) - We use this tool to build serverless applications defined by AWS SAM templates. The CLI provides commands that enable us to verify that AWS SAM template files are written according to the specification, invoke Lambda functions locally, step-through debugging Lambda functions, package and deploy serverless applications to the AWS Cloud.
The process to Set Up the Debugger Run the below commands to start and debug Lambda in local:
sam init – It generates a preconfigured AWS SAM template and example application code in the language that we choose.

sam local invoke and sam local start-API – We use this command to test your application code locally. By adding a parameter -d we can enable a debug port as well.

e.g. sam local invoke -d 9999 -e event.json HelloWorldFunction

Once the function is up and running, the debugger is listening to localhost:9999. The event.json file has the event details configuration that is used to trigger the Lambda Function. Now, VSC or any other IDE can be used to debug the code. Let us take a walkthrough with VSC here. 

When we hit the F5 button, VSC opens up the launch configuration. This configuration needs to be updated so that it enables the debugger UI to debug at a breakpoint. Below is a sample for the .vscode/launch.json configuration:
 
Once we run the debugger UI with the above launch.json, VSCode will attach itself with SAM CLI process and we should be seeing the breakpoint code is highlighted. We can also use the inspect option to see the detailed values of an object being debugged.

Alternatively, we can install AWS Toolkit in VSCode. This tool enables the links for running the function in locally. It also enables the debug option. To supply the event information, the Configure indicator from the CodeLens line is used. See the below example:

Serverless Framework - 

It is also an open-source framework to provide a powerful, unified experience to develop, deploy, test, secure, and monitor your serverless applications. The benefit of this framework from AWS SAM is that it is not tightly coupled with AWS and can be used for other cloud functions like Azure Function.
The serverless framework comes with an open-source CLI which provides the support with commands to create and build serverless application using template yml file.

The process to Set Up the Debugger

Install the Serverless CLI using below command:
npm install -g serverless
Create a new Serverless Service/Project:
serverless create --template aws-nodejs --path my-service
It will generate the scaffold for a nodejs function code having a handler.js file along with a serverless.yml file.
handler.js file has the handler method which will be invoked through events. Business logic will reside in this file. 
serverless.yml file has a configuration for all the Functions, Events, and resources required for a serverless application flow.
Now, as our application is ready to run, we need to install a plugin called Serverless Offline. This plugin emulates AWS Lambda and API Gateway on your local machine to speed up your development cycles. 
Add serverless-offline in your project using below command:
npm install serverless-offline --save-dev
Then, inside your project’s serverless.yml file add plugin entry. If there is no existing plugin in the yml file, then add a new entry:
plugins:
  - serverless-offline
To test if the plugin got installed successfully run this command:
serverless offline
It should print all the options for the plugin to run:

 

Now, run a nodejs application in debug mode.

Update the script section of the package.json file residing in the root folder:


"scripts": {
"start": "./node_modules/.bin/serverless offline -s dev",
"debug": "export SLS_DEBUG=* && node --debug ./node_modules/.bin/serverless offline -s dev",
"test": "mocha"
}

Note: This script is for the Linux environment. For Windows, we need to replace the “debug” parameter with:

 "debug": "SET SLS_DEBUG=* && node --debug %USERPROFILE%\\AppData\\Roaming\\npm\\node_modules\\serverless\\bin\\serverless offline -s dev"

In the above code, we need to pay attention to the only debug section. The SLS_DEBUG environment variable is set to let the serverless framework know that this application needs to be running in debug mode. And once node executes node --debug command, it will use the serverless offline plugin to run the function in locally. We can also define the port in serverless.yml file which we want to map with this nodejs code.

Now, we can tell VSCode to run this code and attach a debugger to it by pressing F5. It will prompt you to set up a launch configuration. We need to use :

.vscode/launch.json needs to have below configuration:

Now we simply hit F5 or click debug > chose Debug and click play, our lambda functions will run locally with a debugger attached. This is what it would look like:

How to Debug Serverless

So far, we have seen how to debug a serverless application locally before we deploy the application in the AWS environment. However, once the application runs in a live environment, new issues may be discovered as it will now connect to real AWS services. It will talk to some of the services which we would not even test locally. Now, we will see the steps on how to debug application while running on AWS live environment.

Debugging with API Testing Tools -

To debug a serverless application on AWS, we may have to start with API Gateway testing. There are various clients available that can be used to test an end-point. The most common tools are Postman and SoapUI. Curl can also be used for simple testing. 

Postman provides options for setting the request type, headers, SSL certificates, body, and tests. This approach works great for functions tied to API endpoints as event triggers, such as API Gateway. But not all Lambda functions are used this way. If a function is triggered asynchronously by other services, such as SQS, Kinesis, or even CloudWatch, we need to have other tools to debug.

Debugging with AWS Console -

AWS Console provides a testing option for the Lambda function directly without the involvement of the event source. It is very useful while deploying the lambda first time on AWS and check real-time issues.

It requires Test events to be created and run those events directly on the function while you are writing and building your application. These test events can emulate what is expected from any AWS services like SQS, Kinesis, SNS or so. Below is an example used to create a thumbnail from an image whenever it gets uploaded in the S3 bucket:
When we hit the Test button, output will be generated as below:
We can further check metrics using the “Monitoring” tab. It has out of the box metrics to check error rate, throttling, and others to help to debug the issues. Detail logs can also be viewed using the “View logs in CloudWatch” button.

Debug using AWS CloudWatch Logs -

CloudWatch Logs is the main source of information on how an AWS application is behaving. The same applies to the Lambda function. Lambda Function by default sends the log data to CloudWatch. CloudWatch creates a LogGroup for each Lambda function. Inside a LogGroup, there will be multiple Log Streams collating the logs that get generated from a particular Lambda instance (Lambda concurrency allows multiple instances created for a Lambda function).


Log Streams will have log events, clicking on each event will provide detailed information for a particular Lambda function invocation.


So, whenever a Lambda function has an issue, we know the steps on how to dig through and see the logs that are happening for that particular invocation. Logs appear in CloudWatch Logs are almost real-time so put logs as much as you need to debug an issue. 
However, there are few caveats using CloudWatch logs:
  • AWS does charge based on the size of the log data being stored in it. So, it is recommended to remove unnecessary log statements once the bug is fixed. It may also increase the size of your Lambda function as well which may cause a delay in loading and starting a function (warm start time).

  • Some of the enterprises which work with multiple clouds and/or on-premise platforms would be using Splunk or ELK for log aggregation and CloudWatch being very tightly coupled with AWS services only, would not be preferred by those organizations. In that case, CloudWatch Logs are streamed to these commercial tools through streaming services like Kinesis. These tools are also good at providing a visualization layer by creating several dashboards as per need. 

  • CloudWatch Logs becomes costly over time as AWS charge more for higher storage requirement. ELK being open-source is preferred by many to save the cost.
Below is a typical flow used to store and view the logs by these enterprises:





Debugging using X-Ray -

Despite having rich features in CloudWatch Logs, it is limited to providing information only for one component and in this case, it would be a Lambda Function. But, for a typical serverless flow, a function would be calling other AWS services like dynamodb, S3, or other services. What if we need to get more details around these integrations - how they are performing, what failures are causing performance issues. 

For these, AWS introduced X-Ray which helps developers analyze and debug distributed applications. X-Ray provides an end-to-end view of requests as they travel through the application, and shows a map of the application’s underlying components. An X-Ray can be used to analyze both applications in development and in production, from simple three-tier applications to complex microservices applications consisting of thousands of services.


So, if we need to debug an application on the AWS environment, we may end up using multiple tools to get all the information. And, that’s why there are many commercial tools evolving and providing a seamless experience to monitor, trace, and debug the distributed applications with end to end flow visualization.

Summary

Serverless has been a big hit in revolutionizing the way we build and deploy cloud applications. While, it solves a lot of problems for developers managing infrastructure but introduces a few like monitoring, debugging, testing, and tracing. That’s the reason the focus is now more around building the tools addressing these problems. Cloud providers and commercial vendors are coming up with tools that can help monitor, debug end-to-end. Currently, they are evolving addressing one issue at a time. Over the period, these tools will mature and make debugging easier.


Thursday, May 28, 2020

Introduction

In the earlier article, we talked about GraphQL getting popularity replacing REST and did some comparison between those two. In this article, we will build a sample app for GraphQL and see how to create APIs using GraphQL server and client. GraphQL servers can be built using NodeJS, Spring, and other supported frameworks. In another article, we already explored the GraphQL with Express-NodeJS. In this article, We are going to do that using the Spring Boot with graphql java tools library. 
Like an earlier article, we are going to build an API for Class and Student resources. We will also define some relationships between class and students to see the power of GraphQL. In this example, we will show how a class can have multiple students and can be queried accordingly.

Pre-requisites

  • Familiarity with Spring boot
  • Any IDE
  • Familiarity with Maven build
  • A MongoDB database - You can use MongoDB atlas which gives a free account and sufficient for this article. 
In this article, we will just explore the GraphQL server only. A client can be built using apollo or relay as per your need. You can see the sample implementation in the earlier article.

GraphQL Server

The server is like an API that will implement the functionality for each graphql query by integrating with DB, third party services, etc... It is a bit different from the express-nodejs setup as spring boot has come up with the graphql-java-tools library to eliminate the configuration of a huge boilerplate code.

Step 1 - Set up a Maven Dependency

  1. spring-boot-starter-web - It is a standard library for a spring boot application to bring all the dependencies together required for a web application.

  2. spring-boot-starter-data-mongodb -This jar is required for spring data implementation with mongodb entity classes.

  3. graphql-spring-boot-starter - It is used for enabling GraphQL servlet, and it becomes available at a path /graphql. This path value can be customized by updating the properties in application.properties. It initializes the GraphQLSchema bean. It will add graphql-java and graphql-java-servlet as dependencies of your project. 

  4. graphql-java allows us to write schema with GraphQL schema language, which is simple to understand.

  5. graphiql-spring-boot-starter - It provides a user interface with which GraphQL queries can be tested and can view query definitions. It exposes /graphiql endpoint.

  6. graphql-java-tools - This is the most important library. This library works magically to parse GraphQL Schema and map the GraphQL objects with your own POJOs (Entity classes)
pom
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>    
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-spring-boot-starter</artifactId>
            <version>5.0.2</version>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-tools</artifactId>
            <version>5.2.4</version>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphiql-spring-boot-starter</artifactId>
            <version>5.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <optional>true</optional>
        </dependency>
</dependencies>

Step 2 - Application Properties

Add properties for the MongoDB connection:
spring.data.mongodb.uri=mongodb+srv://graphql:graphql@graphqldb-tochh.mongodb.net/test?retryWrites=true&w=majority
spring.data.mongodb.database=test

Step 3 - Define GraphQL Schema

GraphQL comes with its own language to write GraphQL Schemas called Schema Definition Language (SDL). The schema definition consists of all the API functionalities available at an endpoint.
Add the required Query and Mutation Schema around Classes and Students Collections. This file has to be kept in the resources folder with graphqls extension.
.graphqls
type StudentClass {
    _id: ID!,
    grade: String,
    sections: String
    students: [Student]
}
type Student{
        _id: ID!,
        name: String,
        dresscode: String
        classid: String
}
type Query {
    classes(count: Int):[StudentClass]
    class(_id: ID):StudentClass
    students(count: Int):[Student]
     
}
type Mutation {
    addClass(grade: String!, sections: String!):StudentClass
    addStudent(name: String!, dresscode: String!, classid: String!):Student
}

Step 4 - Define Document Entities for MongoDB

Define both students and classes Document. the id field is auto-generated.
students
@Data
@Document(collection = "students")
public class Student implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String _id;
    private String name;
    private String dresscode;
    private String classid;
}
classes
@Data
@Document(collection = "classes")
public class StudentClass implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String _id;
    private String grade;
    private String sections;
    private List<Student> students;
         
  }
}

Step 5 -  Define Repositories

Now, define the repositories for both the documents:
There is one extra method findBy_id has been added to search the StudentClass and Students by _id field. As _id field is ObjectId type in MongoDB, it has to be defined this way to get work.
Another method has been added to Student Repository to get the list of students by searching with a classid.
@Repository
public interface StudentClassRepository extends MongoRepository<StudentClass, String > {
    StudentClass findBy_id(String id);
}
@Repository
public interface StudentRepository extends MongoRepository<Student, String > {
   Student findBy_id(String id);
   List<Student> findStudentsByClassid(String id);
}

Step 6 -  Define Service Implementation

We need to implement the Student and Class service implementation with repository calls. Example of StudentService implementation:

Service Implementation Collapse source
@Service
public class StudentService {
    private final StudentRepository studentRepository ;
    public StudentService(final StudentRepository studentRepository) {
        this.studentRepository = studentRepository ;
    }
    @Transactional
    public Student addStudent(final String name,final String dresscode, final String classid) {
        final Student student = new Student();
        student.setName(name);
        student.setDresscode(dresscode);
        student.setClassid(classid);
        
        return this.studentRepository.save(student);
    }
    @Transactional(readOnly = true)
    public List<Student> getAllStudents(StudentClass classObj) {
        return this.studentRepository.findStudentsByClassid(classObj.get_id()).stream().collect(Collectors.toList());
    }
    @Transactional(readOnly = true)
    public List<Student> getAllStudents(final int count) {
        return this.studentRepository.findAll().stream().limit(count).collect(Collectors.toList());
    }
    @Transactional(readOnly = true)
    public Optional<Student> getStudent(final String id) {
        return this.studentRepository.findById(id);
    }
}

Step 7 -  Define Resolvers

Query or Mutation objects are root GraphQL objects. They need resolvers to map the fields defined in root types.  These resolvers have methods that will be called when those root type fields are requested.
For example, In Query root type, we have a field called students so the resolver class which implements GraphQLQueryResolver needs to have a method with name getStudents() or students() with similar signature defined in the schema. If it doesn't find, it should through a runtime error.
Mutation - Mutation is used to insert and update the records. GraphQL Java has specific resolver GraphQLMutationResolver for Mutation handling.
GraphQLMutationResolver
@Component
public class Mutation implements GraphQLMutationResolver {
   
    @Autowired
    private ClassService classService;
    @Autowired
    private StudentService studentService;
    public Student addStudent(final String name, final String dresscode, final String classid) {
        return this.studentService.addStudent(name, dresscode, classid);
    }
    public StudentClass addClass(final String grade, final String sections) {
        return this.classService.addClass(grade, sections);
    }
  
}
Query - GraphQL Java has specific resolver GraphQLQueryResolver to handle all the query schemas
GraphQLQueryResolver
@Component
public class QueryResolver implements GraphQLQueryResolver {
    @Autowired
    private ClassService classService;
    @Autowired
    private StudentService studentService;
    public List<StudentClass> getClasses(final int count) {
        return this.classService.getAllClasses(count);
    }
    public Optional<StudentClass> getClass(final String id) {
        return this.classService.getClass(id);
    
    public List<Student> getStudents(final int count) {
        return this.studentService.getAllStudents(count);
    }
}
There is no resolver required for graphql scalar types like Int, String, etc.. However, for a complex object, a separate resolver will be required. For example, StudentClass which is referred by classes field in Query type has students field. To resolve the students field, it would need GraphQLResolver. 
GraphQLResolver
@Component
public class StudentClassResolver implements GraphQLResolver<StudentClass> {
    @Autowired
    private StudentService studentService;
    public List<Student> getStudents(StudentClass classObj) {
        return this.studentService.getAllStudents(classObj);
    
}

Test the GraphQL Server

We can launch the graphiql UI using http://localhost:8080/graphiql

We can pass the graphql compatible input queries in that and test all the queries.

Summary 

We have built a simple Spring boot app with GraphQL java library. With the help of graphql-java-tools, we avoided the implementation of DataFetchers, RuntimeWiring, and other classes. The implementation is very straightforward and can work with any Database, Entities, POJOs.
As usual, the code can be found on GitHub: graphql-java


Follow by Email

Followers

Total Pageviews

Popular Posts