Developing GraphQL API with Netflix DGS Framework(Part-IV)

This is the fourth post of the series. For the first three posts of this series you can follow the given links part-I, part-II, part-III

In this post, I will explain about the Named queries and how to do File uploading in GraphQL using Netflix DGS Framework

Named Queries and Parameter placeholders

In our previous posts, we have used GraphQL queries and mutations, like below.

query {
  employee(id :1){
    first_name
    last_name
  }
}Code language: Java (java)
https://gist.github.com/sureshgadupu/de065ea1b55520592a6c9b581852ac88
https://gist.github.com/sureshgadupu/9320824ea46ef1afa98f21a8eb6481ae
mutation {
 createEmployee (employee :{first_name: "Alex" ,last_name : "Peak" ,gender : M ,deptId:1}) {
  id
  first_name
}
  
}Code language: Java (java)

Above style has 2 problems.

Suppose we want to have 2 queries, one with Employee and Department information included and another with only Employee information.

Since query or mutation has no name, we need to look at the response fields to understand the query.

Second problem is parameters are hard coded, if we want to use the queries in GraphQL client , passing params based on the user input will be tedious.We have

Gra[hQLaddresses these problems with Named Queries and Paramter place holders.

For every query, we can write name like below. Observe that after query and mutation key word, I have written some meaningful string to identify the query and mutations.

Named queries also helps in debugging and server-side logging reasons, it’s useful to give your queries meaningful names. That way, when you see something wrong going on either in your network logs or your GraphQL server , you can easily find that query in your codebase by name instead of trying to decipher the contents. Think of it like a function name in your favourite programming language

query GetEmployee{
  employee(id :1){
    first_name
    last_name
  }
}

query GetEmployeeWithDept{
  employee(id :1){
    first_name
    last_name
    department {
      dept_name
    }
  }
}

mutation createEmployee{
 createEmployee (employee :{first_name: "Alex" ,last_name : "Peak" ,gender : M ,deptId:1}) {
  id
  first_name
}
  
}Code language: JSON / JSON with Comments (json)
https://gist.github.com/sureshgadupu/7ba26d379c2680e22d864880147eb2dd

Parameter placeholders

Second problem is parameters are hard coded in above queries, if we want to use the queries in GraphQL client , passing parameters based on the user input will be tedious.We have to build entire query string based on the user input.

GraphQL addresses this problem with parameter placeholders.

Below, you can observe that placeholders are defined with $ sign.

After query name we write placeholder parameter and its type

GetEmployee ($id : Int)

,then pass the placeholder to actual query

employee(id :$id)

query GetEmployee ($id : Int){
  employee(id :$id){
    first_name
    last_name
  }
}

mutation createEmployee ($employee : SubmittedEmployee){
 createEmployee (employee : $employee) {
  id
  first_name
}
  
}Code language: JSON / JSON with Comments (json)
https://gist.github.com/sureshgadupu/917d0ea31d48784ed7bfdcfd1e394e54

Let’s test the above queries with Altair GraphQL client. You can observe that variables are passed from variables section

mutation placeholder

In this blog post , let’s see how to upload a file to GraphQL server.

File Uploading with GraphQL

In GraphQL, file upload operation treated as mutation request from a client to GraphQL server.

The Netflix DGS framework supports the Upload scalar with which you can specify files in your mutation query as a MultipartFile. When you send a multipart request for file upload, the framework processes each part and assembles the final GraphQL query that it hands to your data fetcher for further processing.

Let’s add the file uploading feature in our api

First we need to declare Upload scalar and then define mutation operation.

scalar Upload

type Mutation {
   uploadEmployeePhoto(emp_id: Int! ,inputFile : Upload!): String
}Code language: JSON / JSON with Comments (json)
https://gist.github.com/sureshgadupu/aa8282204ced4f6ad4ac3a6aeaa748b2

Now we need to implement the uploadEmployeePhoto method

@DgsComponent
public class EmployeePhotoUploadDataFetcher {
	
	@DgsMutation
	public String uploadEmployeePhoto(@InputArgument(name="emp_id") Integer empId, @InputArgument MultipartFile inputFile) throws IOException {
		
		 System.out.println("empId :"+empId);
		 
		Path uploadDir = Paths.get("uploaded-images");
		
        if (!Files.exists(uploadDir)) {
            Files.createDirectories(uploadDir);
        }
        
        String fileName = empId+"-"+inputFile.getOriginalFilename();
        
        Path newFile = uploadDir.resolve(fileName);
       
        try (OutputStream outputStream = Files.newOutputStream(newFile)) {
            outputStream.write(inputFile.getBytes());
        }
       
		return newFile.toUri().toString();
	}

}Code language: Java (java)
https://gist.github.com/sureshgadupu/3117450f245c5b9760ed947de72a06c4

Now let’s test the function with Altair GraphQL client. Since the method is expecting file input, select the Add files option from the bottom of the screen, set inputFile as the variable name ( you need to set variable name as per method definition) and use select file link to select the file to upload.

Enter empId value in the variables section

GraphQL File upload

Unit testing GraphQL file upload

We use the MockMultipartFile class to simulate the MultipartFile object

@SpringBootTest(classes = { DgsAutoConfiguration.class, DateScalar.class, EmployeePhotoUploadDataFetcher.class })
public class EmployeePhotoUploadDataFetcherTest {

	@Autowired
	DgsQueryExecutor dgsQueryExecutor;

	@Autowired
	EmployeePhotoUploadDataFetcher empPhotoUploader;

	@Test
	public void test_uploadEmployeePhoto() throws IOException {

		MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt", "text/plain",
				"Spring Framework".getBytes());
		String url = empPhotoUploader.uploadEmployeePhoto(1, multipartFile);
		
		assertThat(url).contains("test");
		
	}

}Code language: Java (java)
https://gist.github.com/sureshgadupu/ad8131ec3178c6b521a164c5ff7effc3

You can download source code for this blog post from GitHub

Other posts in this series

Similar Posts