Search code examples
angularjsrestsingle-page-applicationpermalinks

How should single page applications provide permalinks?


What are the conventions for providing publicly accessible URLs for resources that are managed via a single page application? I think this is an architectural design question, but I anticipate developing an SPA in AngularJS, in case that matters. I'm new to SPAs.

The user will create, view, and modify resources (e.g. server-based objects) of various sorts via an SPA. These same resources will also be accessible to the general public via permalink URLs. I'm fine with the SPA displaying a resource for visitors upon visiting the resource's permalink URL.

I can only think of these two approaches:

  1. Place all resources at http://example.com/resourcetype/resourceID, implementing RESTful APIs here (varying the HTTP method).
  2. Place all permalinks at http://example.com/resourcetype/resourceID and have the SPA hit http://example.com/api/resourcetype/resourceID.

(It doesn't seem reasonable to have permalinks under /api. By "permalink", I just mean the public not-logged-in URL for a resource.)

I would prefer that a user who navigates to a resource via an SPA arrive at a shareable URL, because a user wanting to share that page is going to first think to share its URL, not to first find a link to the permalink page. This suggests employing the first approach, but the second approach is better for versioning APIs via URLs such as /api/v1, /api/v2, etc.

It would be ideal to avoid hashes in the URLs. I understand that I can use HTML5 mode in AngularJS to hide them in browsers that support the mode. This would also require server-side support, and I have seen the solution that rewrites deep links as links to SPA access URLs.

I'd like to know what people are actually doing and whether people find themselves limiting the use of SPAs in practice. Thanks for your help!


Solution

  • This is a well understood, but massively under-communicated weakness of all SPAs. To support permalinks (and dynamic URLs) in our Vue application, we use web server URL rewriting. We create an Apache rewrite rule that listens for specific known/reserved permalinks or dynamic URL patterns and rewrites the request to the ONLY viable endpoint for an SPA (i.e./index.hml) with a custom query string parameter referencing the desired resource.

    For example:

    RewriteEngine On
    RewriteRule ^somemodule/(.*?)$ /?somemodule=$1 [R=301,L]
    

    The rule above listens for a request URL like mywebsite.com/somemodule/32 and rewrites it to mywebsite.com/{index.html}?somemodule=32

    Our Vue app uses a "middleware" component that inspects ALL requests, looking for our reserved query string parameter matching ?somemodule=x. When it encounters this, it INTERNALLY routes the request to the proper component/module.

    Dealing with permalinks and dynamic URLs in SPA's is a serious hassle. Hopefully the approach above provides some additional food for thought.