Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 832
Joe Lapp Avatar asked May 06 '16 22:05

Joe Lapp


2 Answers

At our office, we use both resourceIds as well as slugs. Any shareable resource will have a slug property that is generated by a property of the resource model. For example, anyone who writes an Article for our SPA must give the Article a title. That title is validated by the server on save to ensure that there are no other articles containing the same title. If the title is unique, a slug is generated for that resource and saved to our DB, otherwise the server responds with an error message stating that the title is not unique.

Then in our state declarations when a user visits http://example/articles/article-slug, we query our API for an article matching the article-slug and return 404 if no article is found.

        .state('app.articles.article', {
            url: '/:slug',
            views: {
                articles: {
                    templateUrl: tpl + 'views/frontend/articles.article.html',
                    controller: 'ArticleCtrl',
                    resolve: {
                      article: ['$stateParams', function ($stateParams) {
                            var slug = $stateParams.slug;
                            // query API for slug
                      }]
                    }
                }
            }
        })
like image 141
SuperVeetz Avatar answered Sep 25 '22 15:09

SuperVeetz


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.

like image 22
rmirabelle Avatar answered Sep 23 '22 15:09

rmirabelle