Search code examples
restfeathersjsfeathers-sequelize

RESTful CRUD to just one or multiple services


If I have a simple datastructure that would just require one service (formular) to also handle its related data in other tables (formgroups, fields).

  1. How to distinguish, if it's an update operation or a create operation with new inserted data in the structure? (by something like a id that is null or not?)
  2. Using one or multiple services?

The datastructure:

{
    "id": 7
    "name": "Formular name",
    "formgroups": [
        {
            "id": 28,
            "formularId": 7
            "name": "General",
            "fields": [
                { "name": "Firstname", "id":107, "formgroupId":28 },
                { "name": "Lastname", "id":108, "formgroupId":28 },
                { "name": "Birthdate", "id":111, "formgroupId":28 }
            ]
        },
        {
            "name": "Address; new group with new fields",
            "fields": [
                { "name": "Street"},
                { "name": "Zip"},
                { "name": "Country"}
            ]
        },
        {
            "name": "Additions",
            "fields": [
                { "name": "First Line"},
                { "name": "Second Line"}
            ]
        }
    ]
}

If with just one service (/formular), I would send the whole datastructure to it and need to distinguish between an element that has an id, so it would make an update query, or if it has none, create the record with its related "parent"-id.

Doing so in one service would hurt single responsibility principle. Because I have logic from formgroup and field in my formular service.

Also I'm not sure if before and after hooks work with feathers-sequelize associations anymore. They maybe bypassed.

If working with multiple services (/formular, /formgroup, /field), doing the same check for id but the client app dispatches to each service with either PUT/PATCH or POST.

PATCH /formular/7

PATCH /formgroup/28

PATCH /field/108

POST /formgroup (insert with formular id 7 and get back the formgroup id 29 for the next field insert)

POST /field (insert 'Street' with formgroup id 29)

POST /field (insert 'Zip' with formgroup id 29)

...

But this doesn't look right. So many requests made. Maybe doing so immediately for each newly created formular/formgroup/field is an solution, and not thinking of the whole datastructure to be saved at once.

A third way might be having all the services, but never expose all of them. Sending the hole datastructure to /formular service, where this one calls internally the other services /formgroup and /field for CRUD operations. But I don't know if this still works with the hooks part and feathers-sequelize functionality.

Entity relationship model:

entity relationship model

Sourceode of my testproject can be found here: https://github.com/Orbitkorbi/feathers-sequelize-test

Additional info about the application: My application is split into two parts.

  1. The server app running feathersjs and providing the REST API with its services.
  2. A clientside SPA running in the browser making calls to the API

Question: How to do this in respect of feathersJS services, hooks, sequelize relations and RESTful API?


Solution

  • You are using "services" where most people would use "resource", "endpoint", or (more generally) "module", depending on context. Answers about "services" aren't likely to help you.

    A third way might be having all the services, but never expose all of them. Sending the hole datastructure to /formular service, where this one calls internally the other services /formgroup and /field for CRUD operations.

    This approach is most consistent with REST.

    The central idea being that your "REST API" is a facade to make your application look like a generic document store (aka a website). Remote clients that want to send information to you do so by proposing edits to the resources on your web site.

    Each document on your website is independent of the others, and edits to that document can be proposed using self describing messages that are common to all documents.

    Therefore

    PUT /formular
    

    Is a perfectly reasonable way to propose to the server that it makes its copy of /formular look like the client's copy.

    The server is then responsible for (a) determining how it wants to change its copy of the resource in response to the request and (b) figuring out how to make those changes.

    So if /formular is a resource built from multiple rows in multiple tables, the server will need to figure out for itself which database statements will be needed to update those tables appropriately.

    That complexity is deliberately hidden behind the REST facade - as far as a client is concerned, you are just writing the new representation of the resource into a file somewhere.


    As far as single responsibility principle is concerned, the request handler has one responsibility -- to handle the request. You are probably going to want to implement that by having the request handler delegate some or all of the work to simpler things.

    (For example, parsing the HTTP request, generating the HTTP response, etc)