Spring MVC - how to return different JSON responses based on query parameters? - rest

I am implementing a REST api using Spring MVC. The requirement is:
/transactions returns an array of transactions.
/transactions?groupByCategory returns transactions grouped by categories.
For example request #1 returns:
[
{ "id": 1, "category": "auto", "amount": 100 },
{ "id": 2, "category": "misc", "amount": 200 },
{ "id": 3, "category": "auto", "amount": 300 }
]
whereas request #2 groups this result by category and returns the sum of each category:
[
{ "category": "auto", "amount": 400 },
{ "category": "misc", "amount": 200 }
]
Obviously the return value from these requests is of different types. How do I do this in Spring MVC? So far I have:
#RequestMapping("/transactions")
public List<Transaction> getTransactions(
#RequestParam(value="groupByCategory", required=false) String groupByCategory
) {
...
}
But this fixes the return type to List<Transaction>. How do I accommodate the second type: List<TransactionSummaryByCategory>?
Edit: I found one solution, i.e. to specify the return type as Object. That seems to work and the response is serialized correctly based on the object type. However, I wonder if this is a best practice!
public Object getTransactions(...) {
...
}

You can simply provide two methods in your controller. First one returning List<Transaction> mapped using
#RequestMapping("/transactions")
and the second one returning List<TransactionSummaryByCategory> and mapped using
#RequestMapping(value = "/transactions", params = "groupByCategory")

You can return a generic List.
public List<?> getTransactions()
{
}
Based on the request params you can populate the list with the appropriate objects

Related

Should a RESTful GET collection API return content of elements

I have an entity stored in the backend which translates to the json format as,
{
"id": 123,
"name": "test",
"key": "test1",
"description": "",
"models": [
{
content
},
{
content
}
]
}
So when i want to retrieve the list of elements using the api
GET /elements
Should i return the elements metadata only(id, name, descriptions) or should also include the content(models[])?
Normally
GET /elements
would return a list of complete entities.
You could add another endpoint:
GET /elements/meta or GET /elements?mode=meta
which would return a List of the metadata only.

GraphQL query result for object that does not exist

I have a GraphQL query that calls a REST service to get the return object. The query contains an Id parameter that is then passed to the service. However, the REST service can respond with http status 404 Not Found if an object with that Id does not exist. That seems like the right response.
How do you model a Not Found response in GraphQL?
Is there a way to inform the GQL caller that something does not exist?
Update
Some options I am considering:
Return null
Change the GrqlhQL Query to return a list of objects and return empty list of nothing is found
Return some kind of error object with an error code
but it is unclear if there is a recommended practice in GQL API design.
You might treat it as an error and handle it accordingly.
I recommend you to check the GraphQL spec, the paragraph about error handling.
I hope it contains exactly what you are looking for.
Basically, you should return whatever you could, and inform a client about potential problems in the "errors" field.
The example from the documentation:
Request:
{
hero(episode: $episode) {
name
heroFriends: friends {
id
name
}
}
}
Response:
{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [ { "line": 6, "column": 7 } ],
"path": [ "hero", "heroFriends", 1, "name" ]
}
],
"data": {
"hero": {
"name": "R2-D2",
"heroFriends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
{
"id": "1002",
"name": null
},
{
"id": "1003",
"name": "Leia Organa"
}
]
}
}
}

REST API filter options in response body

I'm trying to display the remaining possible filter options, for a REST endpoint, based on the already set filters.
Is there some kind of best practice, on how to design a REST API, to let the client know about the remaining options?
Something like this came to mind.
{
"count": 131,
"next": "2",
"previous": null,
"filters": {
"status": [
1,
2,
99
],
"...": [
"..."
]
},
"results": [
{
"id": 1,
"status": 1,
"...": ".."
},
{
"id": 1,
"status": 2,
"...": "...."
}
]
}
The best way to let the user know which filters can be added to an endpoint is to create an API Documentation with Swagger or Redoc.
To let the clients know what filters they can add for an endpoint, you send them the documentation where they can see exactly how it can be used.
Do you have another particular use case for which you want to send them on the response?
If you must necessarily send the filters in the body, you can create a FilterMixin and put it on all the ViewSets where you want to have this functionality and overwrite the list function. An example would be somthing like this:
class FiltersMixin:
def list(self, request, *args, **kwargs):
data = super().list(request, *args, **kwargs)
remaining_filters = list(set(self.filter_fields) - set(self.request.query_params.keys())
return {"filters": remaining_filters, **data}
This works if the filters on the viewset are added on the ViewSet using the filter_fields property.

Does Sprint Data Rest support out of the box column queries?

I have a very simple example using Spring Data Rest and JPA which exposes a person resources. When launching the application it works as expected and I can POST and GET instances of the resource.
When sending a GET against /person I get the following response:
"_embedded": {
"person": [
{
"firstName": "FN",
"lastName": "LN",
"_links": {
"self": {
"href": "http://localhost:9090/person/1"
},
"person": {
"href": "http://localhost:9090/person/1"
},
"address": {
"href": "http://localhost:9090/person/1/address"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:9090/person{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:9090/profile/person"
},
"search": {
"href": "http://localhost:9090/person/search"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
As you can see the person resource has a firstName attribute with a value of FN.
My question is should the following GET query work out of the box?
/person?firstName=FN
or is this something that needs to be implemented with a custom search method?
Needless to say it isn't working for me but I'm seeing conflicting information as to if it is supported out of the box.
Thanks in advance,
My question is should the following GET query work out of the box?
No. You will get an error like
{
"cause": {
"cause": null,
"message": "For input string: \"findByName\""
},
"message": "Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'findByName'; nested exception is java.lang.NumberFormatException: For input string: \"findByName\""
}
Spring Data Rest has the following URL structure.
/{pluralEntityName}/{primaryKey} e.g when the entity name is Person and the primary key is Long it will be /persons/1
We see this error message because Spring Date Rest tries to convert the second argument after the entity name to the primary key of that entity. In my Person entity, the primary key is Long, so It tries to convert findByName to Long and fails.
or is this something that needs to be implemented with a custom search method?
Yes, if you want to do a search on the repository you need to write a method in the repository following Spring Data JPA's method name conventions then Spring Data JPA will automatically convert this method to an SQL query and Spring Data Rest will automatically expose this method as an endpoint.
e.g If you write a method like:
List findByNameContains(String name);
This method will be exposed with Spring Data Rest and you will be able to access it from the following endpoint:
http://localhost:8080/persons/search/findByNameContains?name=Mahsum
Btw, you can see all available search method by visiting http://localhost:8080/persons/search

DTO (Data Transfer Object) implementation

I have two questions about DTO implementation.
I have two apis return slightly different nested objects, which responses like:
{
"shop": {
"id": 1,
"name": "name",
"logo_url": "logo_url"
}
...
}
and
{
"shop": {
"id": 1,
"name": "name",
"logo_url": "logo_url",
"address": "...",
"phone": "..."
}
...
}
should I define shop as a inner class of each parent dto, or just define two different dto classes.
Some apis accept same dto object for both create and update, should I define just one class like EditShopDTO for both create and update, or define another two classes like CreateShopDTO and UpdateShopDTO.
Thank you for your help.