It would be nice to have JSON API support clients that want to operate on entities in specific workspaces (using Workspace module which is planned for core).
JSON API is planned for 8.3, potentially 8.4.
The same for Workspace module, planned for 8.3, potentially 8.4.
If JSON API makes it into core before Workspace, it would be nice not needing to break APIs once workspaces get into core.
Simply, we should coordinate a little bit :)
Aspect A: URL structure
Alternative 1
Consistently embedding workspace ID in the paths, e.g. /api/<workspace ID>/node/1
If JSON API gets into core before Workspace module, we can simple hard-code live as the only workspace ID. That way, when Workspace module gets into core it will continue working.
Alternative 2
/api/node/1 falls back at the "live" workspace, if Workspaces are used.
/api/<workspace ID>/node/1 targets the entity in a specific workspace ID.
The problem here might be path matching, and potential conflicts between workspace ID and entity type ID.
Comments
Comment #2
dixon_Unfortunately negotiating workspace based on headers isn't an option because of #2364011: [meta] External caches mix up response formats on URLs where content negotiation is in use
Comment #3
dixon_Comment #4
e0ipsoHello Dick.
I'm open to the idea. For the negotiation, I think we may be on the same page since you mentioned the headers. Since that is not an option (womp womp) maybe we could take that lead and add a
_workspacequery string parameter, a la_format.I'm not in love with that idea, since we'd be implying that
/api/node/article/36f5f02a-a7ba-4caa-87e1-3c1c8fd5100c?_format=api_jsonis the same resource entity as/api/node/article/36f5f02a-a7ba-4caa-87e1-3c1c8fd5100c?_format=api_json&_workspace=workspaceId. Which is not necessarily true. Or maybe it is? I'm not sure.The part that I like the least about the path pattern is that we are shutting down the door to other data realms. Would it even be possible to have a use case that would not be covered by workspaces? Maybe decoupled preview systems?
I'm sorry I come back with more questions than answers, but I agree with you on the need of collaboration.
Comment #5
wim leersPerhaps
/api/<realm>:<realm ID>/<entity type>/<entity ID>can work? Then you'd end up with/api/workflow:staging/node/1.Comment #6
e0ipsoThat name pattern on the path is good for me.
Comment #7
Grayside commentedThe downside of that pattern is the first-time DX of interacting with the API immediately requires understanding the concept of a "realm" and what a workspace is. An implicit default helps people onboard at a more measured pace to the API and minimizes verbosity for use cases where maybe this does not matter.
I am arguing for the querystring parameter or for making sure an alias or redirect system can cover the simpler case of not designating the workspace.
Comment #8
e0ipso@Grayside my assumption was always that the URL pattern would remain the same if no
realmis needed. Someone without the need of Workspaces integration would use the same old URL/api/node/article, but if they needed to interact with a particular workspace then they'd need to use/api/workspace:the-wsp-id/node/article.Does this make sense to everyone?
Comment #9
dixon_I like that idea. It makes sense to me!
Comment #10
wim leersYep, that's exactly what I meant.
Comment #11
Grayside commentedMaybe I'm not fully understanding workspaces, but does that mean
/api/node/articleand/api/workspace:live/node/articlecould point to the same resource? If so, which one is canonical? I generally like to use the most specific URL for a resource as the "most canonical", so might we want to encourage developers to find their preferred URL by adding a canonical LINK header from one to the other?Comment #12
wim leersComment #13
wim leersWhat hasn't been mentioned yet, is the addressability of revisions. JSON API uses UUIDs as the identifier for an entity. But with the Workflow initiative, it will become necessary to be able to access specific revisions (and list revisions of an entity). How will JSON API support that?
It's perhaps out of scope of the originally intended scope, but it seems even more important to discuss?
Comment #14
dixon_Yes, supporting revisions will indeed be very important. Core is moving rapidly towards revisions everywhere, which creates the foundation for workspaces.
My thoughts below should probably be captured in a new JSON API issues, specifically about revisions. Anyway, I'll post it here for now:
I think identifying entities with the UUID is good. Fetching specific revisions can be done with a query parameter. And we should not use the "local" revision ID for this, we should use the revision UUID: #1812202: Add UUID support for entity revisions
Furthermore, the revision API in Drupal 8 will continue to evolve over the coming few releases. One of the things we'll introduce are parent revisions, effectively creating a revision hierarchy to handle revision conflicts etc. There's a few things the API needs to consider here:
These are some of the reasons why we borrowed the API specification from CouchDB when implementing the Relaxed API module. I look at Realxed as an API specifically useful for replication, content staging and revision management. The API is not equally useful for custom client implementations, decoupled apps etc...
However, there's nothing stopping us from adopting some similar concepts for JSON API to support clients wanting to deal with revisions.
1. Open revisions
The CouchDB spec (hence also Relaxed module) allows you to fetch all open revisions with
?open_revs=allwhich will return amultipart/mixedresponse with each revision as a separate response part. Very restful, very clean.Example with 2 open revisions:
GET /api/node/abc123?open_revs=all HTTP/1.1 Accept: multipart/mixed HTTP/1.1 200 OK Content-Type: multipart/mixed; boundary="xyz789" Transfer-Encoding: chunked --xyz789 Content-Type: application/json { "uuid": "abc123", "revision_uuid": "def456", "foo": "bar" } --xyz789 Content-Type: application/json { "uuid": "abc123", "revision_uuid": "fgh789", "foo": "guz" } --xyz789--2. Avoid introducing conflicts
The CouchDB spec stops clients from introducing conflicts by requiring clients to always indicate what parent revision they intend to write on-top of, by essentially including the current revision UUID in the payload. If there exists a newer revision UUID the API should return
HTTP 409to indicate the conflict.Comment #15
dixon_There's lots more to discuss here. But in terms of fetching and listing revisions the CouchDB's approach is pretty simple. With a few simple query parameters you can include some vital meta data in the response. See this for some CouchDB examples:
I'm not saying that we need to copy these concepts straight off, Relaxed module already does this specifically for replication and content staging :)
But some inspiration can certainly be taken from this :)
Comment #16
wim leersComment #17
e0ipsoThere is a documented issue with the domain module. I suspect that it's on the domain side, but it may affect any potential solution here. #2810307: Interaction with the domain module produces stale caches
#2795279: [PP-2] [META] Revisions support is the issue to track revision support.
I like the idea of the
multipart/mixedresponse. Would it be reasonable to only allow outputting (GET) revisions for single resource entities (as opposed to collections, relationship endpoint and related endpoint).Comment #18
gabesulliceRelevant RFC for future reference: https://tools.ietf.org/html/rfc5829
Comment #19
wim leersOhhh!
Comment #20
Snugug commentedOne thing to consider, given we're also talking about revisions, is grouping language translations (or available translations) in with the revision discussion, too, as it's not entirely obvious now how to get translations of an entity vis JSON:API
Comment #21
wim leersYep, translation support is still missing from JSON API. It also is missing from core's
restmodule.Comment #22
wim leers#20 + #21: For translations, see #2794431: [META] Formalize translations support. A simple search would've found it: https://www.drupal.org/project/issues/jsonapi?text=translation&version=8....
Also, this is a key new feature, belongs in the next major version.
Comment #23
wim leers#2784921: Add Workspaces experimental module added the Workspace module as experimental to Drupal core 2 months ago. 🎉
I'd like to get advice/wishes/pointers from those actively working on it. How would you like to see this work? We got some feedback from @dixon_ 1.5 years ago, but I'm sure your thoughts on it will have crystalized by now.
Thanks! 🙏
Comment #24
effulgentsia commentedCurrently, the Workspace module has 2 negotiators for determining the active workspace (the one that the current request applies to):
SessionWorkspaceNegotiatorandQueryParameterWorkspaceNegotiator. Should JsonApi just use those (which I think it currently does already, since there's nothing disabling those negotiators for jsonapi requests)?But that means that it's already the case that a jsonapi request could come in for a session whose active workspace isn't the default one. Which I think might require catching exceptions like the ones being added in #2975334: Prevent changes that would leak into the Live workspace as well as making sure that workspace-supported entity saves are happening correctly (in a pending revision associated with the requested workspace). I wonder if temporarily it makes sense for jsonapi to explicitly return a friendly 4xx for any request that comes in for the non-default workspace.
Comment #26
amateescu commentedFix component following module rename.
Comment #36
capita commentedCan we use JSON:API to retrieve a node from the 'stage' workspace instead of the usual 'live' one? If so, how would we go about doing that? Thanks a bunch!
Comment #37
amateescu commented@capita that's not possible currently because there's no integration between JSON:API and Workspaces. That's what we're trying to figure out in this issue :)