5

Quick Summary

I'm building a search micro-service that will allow searching across different types of resources in a particular system (e.g. blog posts, users etc.). The API that will be exposed will be consumed by both web applications and native mobile applications.

My Solution

What I'm considering is returning an array of generic "SearchResult" objects. For example:

"results": [
    {
        "title": "My Blog Post",
        "description": "My blog post description",
        "image": "https://img.com/icon.jpg"
        "link": "https://link.com"
        "type": "blog-post"
    },
    {
        "title": "Henry Jacobson",
        "description": "Henry was born in a small village.",
        "image": "https://img.com/henry.jpg"
        "link": "https://link.com/henry",
        "type": "user"
    }
]

I think this is pretty flexible because it means that regardless of the type of resource that has been found, it'll be possible to parse it as a generic SearchResult object on the client side. This means we can return new resources in the future without having to worry about how any particular client will go about parsing them.

Sometimes, additional data might need to be provided in order to allow clients to do some custom rendering. I think this might be a good way of achieving that:

"results": [
    {
        "title": "My Blog Post",
        "description": "My blog post description",
        "image": "https://img.com/icon.jpg",
        "link": "https://link.com",
        "type": "blog-post",
        "extra": {
            "category": "Finance",
            "commentCount": "501"
        }
    },
    {
        "title": "Henry Jacobson",
        "description": "Henry was born in a small village.",
        "image": "https://img.com/henry.jpg",
        "link": "https://link.com/henry",
        "type": "user",
        "extra": {
            "status": "active",
            "topFriends": ["Jerry", "Jamie"]
        }
    }
]

Is there anything seriously flawed with this approach and if there is, what might be a better alternative?

4 Answers 4

2

You can solve this relatively easy using GraphQL as it can represent union types. (Of course you can redo the same approach also without using GraphQL libraries)

In essence, the server will return a collection of search result objects that may have some common fields and - depending on the specific type - have extra fields.

The client can then query the server and ask for details about the types that the client knows. If more types are added, the old client will just get the common fields for that type.

The main benefits of GraphQL in my eyes are:

  • Client tailored response (web app & mobile app may need different detaisl or images)
  • The schema allows for some change control and you can usually generate the clients in a type safe way

As you can see, this closely resembles your approach with the extra-object already, just that GraphQL adds some standardization around it all and thus you can access a large toolset to produce and consume it.

1

The problem is that the client wont know what fields to expect for each result.

You will need to add a "_type" property to indicate the type of each result and then have documentation somewhere indicating the fields returned for each type.

The client will then have to query the _type property and be programmed with the documentation to know what to look for.

eg. Search for kittens and put all the images in one list, showing the "imageUrl" and ordered by "cutenessFactor" and all the blog posts in another list, ordered by "date" and showing the first 20 characters of "content"

3
  • 2
    Thanks for the feedback, I've already included a type property in my sample responses which should address the issues you've mentioned :)
    – Commit
    Commented May 1, 2018 at 11:44
  • Why do u need the type? The type is SearchResult. SearchResult is not meant to be an input to create new resources of one or another type. It's only output.
    – Laiv
    Commented Sep 25, 2020 at 12:45
  • in order to know what sort of "conditional rendering" to do
    – Ewan
    Commented Sep 25, 2020 at 12:55
1

You've told that this API should return different kind of resources. Let's see some issues

  1. The representation of some searchable resource change: should you also change the representation from the search API perspective?

  2. What if you add a new kind of resource?

  3. If the client needs to separate different type of resources in different sections (eg. tabs, checkout facebook search) you need to specify which resrouce type you are retrieving and how many resources types you can have

In case 1. has no trivial solution (eg. the search microservice is different from posts microservice, so models have to be shared) don't return resources, return just links. This could introduce one more round trip from you clients, but they can cache responses (from client side), you can cache responses (from backend side) and this improves performance. Furthermore, you could add a backend aggregaton layer (or backend for frontend) to "join" data and make the search api return post instead of links back. But in this way you don't have to share knowledge of view models

To solve 2. and 3. You could just add a resourceType field in every item of you response. Take into consideration to retrieve the list of types with a GET /searchabletypes or whatever fits for you

The thing to remember is that REST is all about decoupling and navigation. Server side has its own evolution path, clients can chase them without any jump

1

You can make a design decision that the search results are not responsible for delivering extended properties. I'm facing a similar challenge right now. I have an endpoint delivering similar functionality to your search. I'm considering adding a property named "uri" that would point the client to another endpoint that could provide a domain-specific object.

Replacing the "extra" object, you provide a URI reference to the domain-specific endpoint where the client can get the remaining properties. Note that this approach requires exposing a key structure that can be used to direct the client to the domain-specific resource.

Your search results:

"results": [
    {
        "title": "My Blog Post",
        "id": 194328,
        "description": "My blog post description",
        "image": "https://img.com/icon.jpg",
        "link": "https://link.com",
        "type": "blog-post",
        "uri": "https://link.com/api/blog-post/194328"
    },
    {
        "title": "Henry Jacobson",
        "id": 89312,
        "description": "Henry was born in a small village.",
        "image": "https://img.com/henry.jpg",
        "link": "https://link.com/henry",
        "type": "user",
        "uri": "https://link.com/api/user/89312
    }
]

When the client navigates to the Henry Jacobson uri:

"user": [
    {
        "id": 89312,
        "name": "Henry Jacobson",
        "description": "Henry was born in a small village.",
        "image": "https://img.com/henry.jpg",
        "link": "https://link.com/henry",
        "status": "active",
        "topFriends": ["Jerry", "Jamie"]
        etc.....
    }
]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.