REST / CRUD. Am I cooking it wrong?

    Introduction


    REST is a very interesting method for working with objects on the server. Implementing a CRUD interface with REST tools is quick and easy! Today I will try to show which of the approaches in REST / CRUD in my opinion are wrong and detrimental to the project.

    Must have for all resources


    To continue the story, you should immediately decide on the minimum set of object properties, addressing and other delights. Let's start:
    • A URL is such a thing, clicking on which you can 100% get information about the current state of an instance of an object. It has the form
      ///
    • URN is such a thing, by clicking on which you can 100% get information about the object. It differs from the URL in that it does not bind to a specific protocol and server. In 99 cases out of 100, it makes sense to use one. It has the form
      //
    • A URI is such a thing, comparing that between objects you can 100% say whether you are working with an instance of the same object. In other words, if their URIs are true for 2 or more objects, then this is the same object! It has the form
      /
    • resource is such a thing, comparing that between objects you can 100% say whether you are working with instances of the same collection of objects . In other words, if their resource is true for 2 or more objects, then these are objects from the same collection!
    • slug is such a thing, comparing which between objects without binding to resource, you can 100% say that you are doing something wrong. In other words, if for 2 or more objects their slug is true, then this DOES NOT MEAN ANYTHING ! Serves exclusively for unambiguous identification of an instance in the collection !

    Important difference between slug and id

    • id - an integer value that means NOTHING to the user
    • slug is a meaningful string value. Typically with underscores (or periods) instead of spaces

    For sql-tankers (no offense) I will explain:
    • id in the table - for data integrity in the relationship. Has restrictions NONULL, UNIQ, INT
    • slug is for API. It has restrictions NONULL, UNIQ, CONST and NOT USED in relationships.

    For example, users.id is id, but users.login is quite a slug.

    And now we take it for the axiom “all objects must have the specified properties”, and in order not to get spread over the object, we shove them into the __link__ property, for example:
    {
        __link__: {
            url: 'http://localhost/api/users/vasya',
            urn: '/api/users/vasya',
            resource: 'users',
            slug: 'vasya'
        }
    }
    

    By the way, this is a valid object. It could return from some url (not " localhost / api / users "). The developer, having received such an object, must request data on url / urn from __link__, but more on that later.
    By the way, “on the right” - do not add. property "__lnk__", and use headers of the X-URI, X-URN, X-URL, etc format for these purposes, but not all web users like to work with headers. Maul took the body and ran.
    Hmmm ... how many REST API implementations have similar properties? Maybe I invented a bicycle?

    CRUD / Read or HTTP / GET


    Everything is simple here. We take the URL, request the object, and ... But what about the “and”? And sometimes we observe this answer:
    Example:
    HTTP / GET http: // localhost / api / users

    {
        status: true,
        count: 100,
        data: [
            {
                login: 'vasya',
                name: 'Vasilii',
                id: 146,
                __link__: {
                    url: 'http://localhost/api/users/vasya',
                    urn: '/api/users/vasya',
                    resource: 'users',
                    slug: 'vasya'
                }
            },
            {
                login: 'petya',
                name: 'Petr',
                id: 145,
                __link__: {
                    url: 'http://localhost/api/users/petya',
                    urn: '/api/users/petya',
                    resource: 'users',
                    slug: 'petya'
                }
            }
        ]
    }
    

    What's wrong here:
    • Why is there a “status"? HTTP / 200 is not enough? Or is your server unable to send error message text? Sending HTTP / 200 with "status == false" - hiding the error. If the backend does not have logging at the point where the error occurred, we will debate for a long time in search of what happens. Whereas the http server log will immediately tell which url the error occurred.
    • This kind of wrapping forces weber to write unwrap functions to display error messages or receive data. Although for a full debug, a problematic URL will be enough.
    • Hmm ... And if there are +100500 elements, and one of the admins has changed the "name" property for the user "petya"? Or, more often, a new user was added to the collection and count is already 101? That's right, you can forget about HTTP / Cache-control - we will pull the entire list in a new way.

    Conclusion: I do not know how correct this is, but so far all the arguments are not in favor of this approach.

    The answer should be like this:
    [
        {
            __link__: {
                url: 'http://localhost/api/users/vasya',
                urn: '/api/users/vasya',
                resource: 'users',
                slug: 'vasya'
            }
        },
        {
            __link__: {
                url: 'http://localhost/api/users/petya',
                urn: '/api/users/petya',
                resource: 'users',
                slug: 'petya'
            }
        }
    ]
    

    At the same time, count must live in HTTP / HEAD, as It is not directly related to the resource, but characterizes the state of the collection. Again, you need to display the number of users. How to be Select everything and count in JavaScript? Write a new url to get the number of elements? What for? Make an HTTP / HEAD collection request. There will be no data, only the headings will return.

    Skeptics will say: to draw all the elements, you will have to make many requests to the backend.
    I will answer: HTTP / 1.1 / Keep-alive will save us all. When using javascript frameworks and caching, most of the data will be requested during initialization, one-time, and after that they will only exchange requests with an HTTP / 304 response (the resource has not changed).

    Do you know why webers don't like this approach? They have to control the receipt of data from the server for normal display.
    For example: If data arrives immediately with properties, you can immediately draw several divs in a loop without worrying about synchronization. If you make 4 requests to display 4-lines with users, you need to somehow monitor the status of all requests. Those. until ALL requests work out - do not draw anything. See Promise and Future for solutions to such problems. They all have code for forced synchronization, although I would draw right away as is ...

    PSAt 2:30 a.m. I really wanted to talk about the nesting of resources, about the massive DELET / POST / PUT ... but I'll probably go to sleep. I will leave this opus to the public. Please speak in the comments - is it worth it to continue or common truths are not in fashion?
    PPS I would be very grateful to people for the different rakes that had to be stepped up when working with the REST API from third-party developers.
    Thanks to all!

    Also popular now: