Graphql rest controller - java

I am new to graphql. I want to implement backend in spring boot using graphql. I am still confused about endpints. So if i have 2 entities user and product. so do i have to implement 2 endpoints
#RequestMapping(value="/graphql/user", method=RequestMethod.POST, produces = "application/json")
public GraphQLResponse<Object> getService(#RequestHeader HttpHeaders headers, #RequestBody GraphQLRequest graphql) {
GraphQLResponse<Object> execute = graphQLService.execute(headers, graphql);
return execute;
}
like this one for user and another one for product. or just one.

No you need only one Endpoint. I suggest you to use this library. With this library it is very easy to start into the world of GraphQL:
https://github.com/leangen/GraphQL-SPQR
And a good example with this library and Springboot you find here, just clone the repo and run it local:
https://github.com/leangen/graphql-spqr-samples
Install this Plugin in Chrome to get read and query your schema easely:
https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij
I hoped this answer helped you.

With spring-boot-grpahql, there's no need to write controller at all. You just need to write ORM layer rest will be taken care by it. Here's an example implementation of GraphQL in java using spring-boot, gradle, spring-jpa and mongo.

Graphql exposes only one endpoint and depending on your setup it may be accessible like this: http://localhost:8080/graphql. Usually you never access graphql directly but rather via libraries that perform actions that allow you to communicate with graphql (like type-checking, converting between graphql types and your types and more).
Java GraphQL Kickstart is one of them and all you do is define schema and write resolvers for your queries, mutations and subscriptions:
schema {
query: Query
}
type Query {
allUsers: User
}
type User {
id: ID!
firstName: String
lastName: String!
}
And your resolver:
package your.project.resolvers;
import graphql.kickstart.tools.GraphQLQueryResolver;
import org.springframework.stereotype.Service;
import your.project.domain.persistence.User;
import your.project.repository.UserRepository;
import java.util.List;
#Service
public class UserResolver implements GraphQLQueryResolver {
private final UserRepository userRepository;
// be carefull about the name of this method
public List<User> allUsers() {
// perform actions based on your needs
return userRepository.findAll();
}
}
Notice that I'm using service annotation because this is basically performing some business logic on data retrieved from persistence layer. Also the name of the method allUsers() in the UserResolver is important. GraphQL will look for it based on your schema like this:
trying exact match with allUsers
prepending get: getAllUsers

Related

How to avoid services in DtoMappers layer

Good day, I have a Spring Boot based backend , we are using own library to convert JPA entities to Dto's (library works based on reflection).
The problem is , we inject service layer directly to some mappers. Let's say I have a UserEntity and UserDto.
UserDto has a field called avatar and avatars are stored in S3.
So in order to build a UserDto we are using the code like this.
#Component
class UserMapper {
#Inject
S3Service s3Service;
public UserDto toDto(UserEntity entity){
UserDto dto = new UserDto();
BeanUtils.copy(entity,dto);
dto.setAvatar(s3Service.getAvatarByUser(entity));
}
}
I don't like this approach because Mapper mustn't know anything about Service layer . However this mapper is used by other mappers as well. In case I want to return an OrderDto, it has a nested UserDto so OrderDto calls UserMapper internally.
Are there any best practices for Mappers to be service free ?
So far I tried the following.
Store avatar in ThreadLocal cache. When controller calls a service to get a user, service will store user's avatar in the ThreadLocal, and then Mapper will get it from ThreadLocal cache. Disadvantage - it's hard to test it and requires me to make Mocks
Create a separate POJO called UserWithAvatar that stores UserEntity entity;String avatar and create a mapper for UserWithAvatar instead of UserEntity. Disadvantage - as I said this mapper will be used by OrderMapper and order mapper takes OrderEntity with nested UserEntity instead of UserWithAvatar
I think mapper should be inside of service but I will try working with ur requirements
u have 2 choices:
You inject both service and mapper to controller, get entity back to the controller and map it using mapper before returning response
Use event publishing to publish an event which mapper then catches and produces the mapping. After that you could either directly return the dto to controller or produce another event. (event publishing is by default synchroneous so you dont have to worry about concurrency issues)
Event publishing is done via spring and results in very uncoupled code where publisher doesnt know anything about event subscribers and hence these 2 can be in 2 seperate layers that wont know anything about each other
Easy to follow guide: https://www.baeldung.com/spring-events

How to create a java client for a Spring service?

I have a service definition using Spring annotations. Example (source):
#RequestMapping(value = "/ex/foos/{id}", method = GET)
#ResponseBody
public String getFoosBySimplePathWithPathVariable(
#PathVariable("id") long id) {
return "Get a specific Foo with id=" + id;
}
The question is whether spring (or another library) can auto-create a remote implementation (client) of the same API without the need to manually type paths, method type, param names, etc. (like needed when using RestTemplate)?
Example of an such a client usage:
FooClient fooClient = new FooClient("http://localhost:8080");
String foo = fooClient.getFoosBySimplePathWithPathVariable(3l);
How can I get to such a client "generated" implementation"?
You are probably looking for Feign Client. It does everything you need: calling one service via HTTP is similar to calling method of Java interface. But to make it work you need Spring Cloud, standard Spring framework doesn't have this feature yet.
You can generate it using Swagger Editor. You shoud just define the path of the resources and then it'll generate for you the client for almost any language of your choice

How to structure controllers that communicate with databases

I'm pretty new to the concept of patterns. I am practising my dependency injection skills as well as using DAO principles. The code I have written works but I feel that it can be written in a more elegant fashion. I've tried restructuring it a pattern I saw but that complicated things so not sure if I implemented it correctly. As a general rule when a web application communicates with a database and throws out result, how should one structure their project?
I've heard of the MVC principle but that doesn't necessarily add database to the mix.
This is what I have so far:
A class containing a controller in a Controller package:
#RestController
public class ResponseController {
#Autowired
MongoBase dbConnection;
#RequestMapping(value = "/jsonresult", method = RequestMethod.GET)
#ResponseBody
public String jsonresult(#RequestParam(value = "id", required = true) String id){
return dbConnection.documentToJSON(id, Constants.database,Constants.collection);
}
#RequestMapping(value = "/alljsonresult", method = RequestMethod.GET)
#ResponseBody
public String alljsonresult(){
return dbConnection.allDocumentToJSON(Constants.database,Constants.collection);
}}
A class containing CRUD methods to the database in a Database package:
#Component
public class MongoBase {
#Autowired
MongoClient mongoClient;
public MongoBase() {
try {
mongoClient = new MongoClient("localhost", 27017);
} catch (Exception e) {
e.printStackTrace();
}
}
public void printAllCollection(String databaseName, String collectionName) {
...
}
So is there a better way/more efficient way of writing thi? Also I feel I haven't fully implemented DI in the Monogbase class since it contains the new keyword..
If you are using springboot, then you don't need this old style
also don't need to create mongoClient bean your self, spring boot help you in it
You just need to add following properties in application.properties file
#mongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=app1
Also declares a spring-boot-starter-data-mongodb in your pom or gradle
it's a cool and super awesome dependency for accessing Data with MongoDB
you can read about it from here[https://spring.io/guides/gs/accessing-data-mongodb/]
suppose you have a domain
#Document(collection = "domain")
public class User {
#Id
private long id;
#Indexed(unique = true)
private String domain;
private boolean displayAds;
//getters and setters
}
Now if we need to perform curd operation on this domain, extends MongoRepository, you have CRUD function automatically. Spring data come with many magic findBy queries, review the official Spring data MongoDB – Query methods for detail.
public interface UserRepository extends MongoRepository<User, Long> {
Domain findFirstByDomain(String domain);
Domain findByDomainAndDisplayAds(String domain, boolean displayAds);
//Supports native JSON query string
#Query("{domain:'?0'}")
Domain findCustomByDomain(String domain);
#Query("{domain: { $regex: ?0 } })")
List<Domain> findCustomByRegExDomain(String domain);
}
UserRepository extends the MongoRepository interface and plugs in the type of values and id it works with: User and Long. Out-of-the-box, this interface comes with many operations, including standard CRUD operations (create-read-update-delete).
now you can easly use it in your controller
#RestController
public class ResponseController {
#Autowired
UserRepository userRepository;
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
User create(#RequestBody #Valid User user) {
return userRepository.create(user);
}
}
also you can do with it lot of things. you just need to go throw with it doc.
Also you you can use mongoTemplate for execute the query
#Autowired
MongoTemplate mongoTemplate;
When I build web applications I typically define the full chain as follows:
Client Side:
View - This is the V in MVC where you control visuals & user action derived workflow.
Controller - This is the C in MVC where workflow is managed. Most Client processing will go here and multiple Client calls can be made to get/send data or perform lookups.
Client - This is where you make a call to a REST web service and parse/deserialize the results/handle exceptions.
Server Side:
RestController (Sometimes Called Resource) - This is your REST API endpoint. Here you extract & validate a request.
Service - This is where most of your server logic will go. Sometimes you might have to make multiple data access calls or call other service functions.
DataAccessObject (Sometimes Called Provider) - This is your database interaction to pull data from your database into a model. CRUD operations (Create Read Update Delete)
Example Scenario:
Lets say we want to submit data & permissions for a given user
UserView.jsp - User types in user & permission data and hits submit.
UserController.java - validates User & permission data, does any necessary lookups, then calls UserClient.
UserClient.java - Builds the REST request and calls the /user/create REST endpoint.
UserRestController.java - Unpackages/Validates the request, then calls UserManagementService
UserManagementService.java - Server Logic happens here! Lets say I have two tables in my database. A User table and a Permissions table. I want to store the user information in the User table and the permission information in the permission table so I will call the UserDAO for the user data and the PermissionDAO for the permission data.
UserDAO & PermissionDAO - Saves The passed models to their respective tables.
Return to Service, Return to RestController, Return to Client (Parse Response), Return to Controller (Move the Workflow forward with a redirect or a success message).
Conclusion:
This may seem like a lot of in-between steps but this design provides a ton of flexibility, especially if your building large, complex web services. Each component has a specific purpose, follows an easy naming convention, and splits complex logic into smaller, simpler steps.
I'd like to recommend some improvements to your solution:
You're using Spring. You should not create any injected beans using new. Let Spring instantiate and manage those beans for you.
Use the #Repository annotation to mark your persistence class.
Make your repository class interface based.
Don't embed "Mongo" into the class name. You happen to be using Mongo as your persistence provider now, but you may change your mind later. Don't reveal implementation information in your class names - hide it.
The Controller is part of the UI. It uses repositories and/or services to fulfill use cases. It's perfectly correct to use Spring to inject the repository into the Controller.
I'd recommend that you use Spring Boot, an opinionated version of Spring. Have a look at their guides to learn how to use it properly.

Flow of data between Angular 2 and Spring

I am about to start my first real project for work (new grad), and I was tasked with creating an internal address book for the company (displaying name, phone extension number, email etc).
My mentor told me that I need to pull the address data from Active Directory.
He also told me that I need to use Angular 2 for the front end, and Spring for the backend. I still need to learn these frameworks, but he realizes this which is precisely why he gave me this task.
However, I am struggling to understand the flow of data between the frameworks.
This is what I am thinking so far http://imgur.com/a/xiH6m.
If someone could please explain what is right/wrong with the diagram and perhaps explain how the data would flow in such a project. I would prefer to bother my mentor with more specific questions.
Just create a REST service with Spring that returns the data as JSON. You can use a simple POJO on the server side, and the converter for Spring should convert it to JSON. Maybe something like
#RestController
public class EmployeesController {
#Autowired
private LdapService service;
#RequestMapping(value = "/employees/{empId}")
public Employee getEmployee(#PathVariable("empId") Long empId) {
Employee emp = ldapService.getEmployee(empId);
return emp;
}
}
With Spring, it should convert the Employee object to JSON on the outbound response (given you have the JSON converter configured).
In Angular, just make a simple Http request to the endpoint, and you will get back JSON, for which you can convert it to an Employee object on the client side. Maybe something like
class Employee {
// employee properties
}
#Injectable()
class EmployeeService {
constructor(private http: Http) {}
getEmployee(empId: number): Observable<Employee> {
return this.http.get(`${empBaseUrl}/${empId}`)
.map(res => res.json() as Employee)
}
}
Here, in the service, you make the Http request to the employee endpoint on the server, and get the result back as JSON, for which you convert it to an object with res.json() and cast it to Employee
That's pretty much it.
Your "Converts to useful format" will not happen on its own. You need a Controller layer there. REST Controller to be precise.
AngularJS 2 is built to work easily with REST. You can use Spring MVC to create REST Controllers which can generate JSON Response.
for Example you can have an Endpoint
GET /contacts/data
which will return
[
{"name":"ABC",
"email":"someone#abc.com",
"telephone":"0101010101"
},
...
]
The following Spring documentation will be a good starting point eventhough it talks about Angularjs 1.

Java Spring REST + MongoDB (custom URI? using MongoDB Template instead of respository class? what about the xml config files?)

I currently have been reading and trying out MongoDB with Spring REST framework.
I have the following code for the controller:
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserRepository userRepo;
#RequestMapping(value = "/info2", method = RequestMethod.GET)
public List<User> getAllUser(){
List<User> users = userRepo.findByEmail("test#test.com");
return users;
}
#RequestMapping(value = "/save", method=RequestMethod.POST)
public User createUser(#RequestBody User user){
return userRepo.save(user);
}
}
However, when I call
localhost:8080/users, it returns back
{
_links: {
self: {
href: "http://localhost:8080/users{?page,size,sort}"
templated: true
}-
search: {
href: "http://localhost:8080/users/search"
}-
}-
page: {
size: 20
totalElements: 0
totalPages: 0
number: 0
}-
}
I want the URI /users/info2 to return back the list of users by that particular email. How do I do that?
Also, here are the outstanding questions I have:
Can I just use MongoDBTemplate instead of the MongoRepository? I want more customized queries, and I couldn't find any in depth examples on the web.
How does Spring framework map the xml config file, for example, in cases like when I setup multiple MongoDB connections?
So without your configs, but looking at your existing code and service response, one can make some general assumptions. Your Repository is annotated with #RepositoryRestController which not only provides Spring Data access but also exposes a common ReST endpoint for your repository.
This can be determined from two aspects of your post. First you don't appear to be using HATEOAS in your Controller class, yet your service exposes JSON + HAL. Furthermore your response exposes the paging and sorting feature of Mongo Repository as well as the search behavior of Spring Data Repositories when annotated with #RepositoryRestController.
If you don't want this behavior, change the annotation on your Repository to #Repository instead.
Also to answer your other questions:
1. you can annotate methods if you want with queries on Spring Data Repositories, you can also use any class in the stack managed by spring you want, but why?
I suggest reading about the bean lifecycle to understand the dependency management within spring, there are plenty of presentations (including one in my github repo). If you have multiple connections, you can define your repository beans to utilize anything you wire it. There is a lot more manual operations at that point, and it will take some understanding and through to make it work. There is no way to give you a simple "do xyz" type answer without a lot more information.

Categories

Resources