6

I am building a REST service. This services enables the user to create an entity that has an Address. For simplification, let say this other entity is 'House', so 'A House has an Address'.

The system has to geocode every House so along with the Address, a House must have latitude/longitude at the moment of being created, but those are not requested to the user.

The user will be asked to enter an Address. From this address the system will try to retrieve latitude/longitude using a third party service. This service will return the best match for the address entered by the user, so sometimes it will return a different address (e.g could happen that there is two different streets with the same name in different cities and the services could return information regarding the wrong one).

I have to make sure that the address I am storing with the House is the one the user wanted, so the workflow for the user would be something like this:

  • The user wants to create a new House.
  • The user Enter an address.
  • The user sees the best match for that address in a map.
  • The user confirms that the address is OK.
  • The system saves the House with the address and the lat/long retrieved from the service.

Approach 1

The entities model would be:

  • House has an Address
  • Address has a Coordinates (lat/long)

and I would have an endpoint for POST addresses and another for POST houses. The workflow would be:

  • The user wants to create a new House.
  • The user Enter an address and make a POST to the addresses endpoint.
  • The system retrieves the lat/long for the best match.
  • The system saves the address retrieved by the geocoding service in the DB. Note that it can be different from the address the user entered.
  • As a response to the POST request, the system returns the address that have saved and show it to the user.
  • The user confirms that the address is OK.
  • The system makes a POST to the houses endpoint with the ID for the address that has been created before, so in DB the house will be linked to the address that was created before.

With this approach, if the user doesn't accept the address provided, I would end up with some 'orphan' addresses (addresses that are not linked to any House). I would need to make a periodic clean on the DB for those addresses.

Approach 2

In this approach, I would have an endpoint /validate-address, so the user would POST an address, and the system would returns the best match. Nothing would be created in the DB at this point. The user then would accept the returned address and would make a POST to /houses with the information of the house and the address.

With this approach, I cannot assure that all the Address in the DB are correctly geocoded as some user could make a POST to /houses without a previous call to /validate-address.

Also, it doesn't seem quite right from a REST perspective to have a POST /validate-address request that doesn't create anything on the DB.

What would be the best approach to this situation? Can you see an alternative approach to this two? Any feedback would be appreciated.

  • 2
    Why do you want to keep addresses and houses separate endpoints? Do you expect an address to be bound to multiple houses or the other way round? Can't you make the first request a GET, save nothing to the database, keep all of the options retrieved from the geoservice in the response (as hypertext) and allow the user to issue a final POST request carrying all of the data (long/lat, address, other house data) to a single endpoint (be it houses or addresses)? The possible addresses would be in the response and the user could just select the one to actually use to create a resource. – toniedzwiedz Nov 18 '14 at 20:35
  • It is Ok/possible to send data in a GET request? The way to say would allow a user to put data into the DB without validate the address before, how would you avoid that? – mario595 Nov 19 '14 at 14:48
  • You have to validate the data in the course of the same request that actually creates a resource. There's no other way. In case of a GET request, it's fine to pass some arguments that are used to find and retrieve the data. This data can be returned and displayed to a user. After the user makes a choice, the data has to be sent in a POST request and validated. At this point, you can create something in the database or display some kind of an error message. An additional validation call could be used before that but only as a means of improving the user experience. – toniedzwiedz Nov 19 '14 at 14:59
  • Ok, thank you for your clarification. I will try the approach 2, which is similar to what you propose, making some kind of validation of the second request (the POST) to make sure the user is creating the entity with valid data. – mario595 Nov 19 '14 at 16:36
1

When you say, a user has a house; and each house has adress data, that means in reverse: no address without a house - which makes by the way no sense. From that the procedure should be clear:

Usecase: Creation of house

1) User enters all relevant data

2) A GET-Request is sent to a geocode-Service, which returns possible addresses

3) Only after the user entered valid data / accepted one data-set from the geocode-Service a POST-Request with the complete data (house-data and address) is made to /houses: since you are creating a new house-ressource. POST ing to /user/{id}/houses is also a possibility; the stress is then (semantically) on the user. I prefer the first variant. The relation belongs to user {id} is an attribute of the house.

4) When the house-ressource is created the new ressource is returned (you get the idea):

{
    "name": "Casa di falcone",
    "id": "123",
    "links": [
        {
            "rel": "self",
            "href": "http://www.example.com/houses/123"
        },
        {
            "rel": "users",
            "href": "http://www.example.com/users/1"
        },
        {
            "rel": "addresses",
            "href": "http://www.example.com/addresses/27/"
        }
    ],
    "address": {
        "id": "27",
        "links": [
            {
                "rel": "self",
                "href": "http://www.example.com/addresses/27"
            },
            {
                "rel": "houses",
                "href": "http://www.example.com/houses?address=27"
            },
            {
                "rel": "users",
                "href": "http://www.example.com/users?address=27"
            }
        ]
    },
        "user": {
        "id": "1",
        "links": [
            {
                "rel": "self",
                "href": "http://www.example.com/users/1"
            },
            {
                "rel": "houses",
                "href": "http://www.example.com/houses?user=1"
            },
            {
                "rel": "addresses",
                "href": "http://www.example.com/addresses?user=1"
            }
        ]
    }
}

Implicitely there was an address generated and its id is embedded in the result. Of course adress and house are in your model two different things/ressources, but it makes no sense to handle them separately.

0

This answer expands on toniedzwiedz comments, so anyone else read them as well.

So reading through the question it becomes clear that you do not trust the user's description of the address, but you DO trust the user's description of the geo-code id of the address.

That is to say the user is not the authority of the address format for the house, the geo-code database is the authority. But the is the authority of which geo-code address ID is mapped to the house.

So when the user tells the system "This is the address of the house" what they are really saying is "This is the geo-code address ID of the house".

In that case the flow is

User creates a house by POSTing to /users/123/houses

User creates address of house by PUTting address geo-code ID to /users/123/houses/1/address

How does the user get the geo-code ID? As toniedzwiedz says they can query that independently to the flow. You can have an end point that simply returns geo-code ids for addresses

GET /geo-codes?address=1600 Pennsylvania Ave NW

{
    "matches": [
    {
        "geo-id":122,
        "address": "1600 Pennsylvania Ave NW, Washington, D.C., United States"
    },
    {
        "geo-id":9837,
        "address": "1600 Pennsylvania Ave, Sweden,"
    }]
}

The user then PUTs the geo-id to the URI /users/123/houses/1/address.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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