[Solved] - Flowrouter: generic vs variable route

Hey guys!
I want to display posts for different cities, defined by cityId:

FlowRouter.route("/:cityId", {
 name: 'postList',
 action: function() {
    console.log(FlowRouter.getParam("cityId"));
    return BlazeLayout.render('mainLayout', {
      top: 'header',
      body: 'postList'
    });
  }
});

Alongside with that I of course have generic routes like ‘admin’, ‘signup’ and so on.
But when I go to /signup, the postList route gets activated, treating ‘signup’ word as a city id, and ‘signup’ is logged in console.

Defining route like FlowRouter.route("/postList/:cityId") is not an option.

1 Like

Great question, one I’ve been meaning to ask for quite some time.

In Rails, you can apply conditionals to routes and the higher a route is in a file the higher it’s preference.

So you can have URLs like:

/sergiotapia (user_id)
/meteor-js (service slug)
/settings (static route for settings page)

And the routes are matched automatically. Hopefully someone answers.

1 Like

I’m pretty sure that’s not possible - you’re essentially saying (with /:id) that any route except for the root should navigate to this route, as the rest of your URL is essentially an id

I don’t have an answer for you, but I would agree that the easiest thing to do would just be to define the route as /city/:cityId or similar. Better for organization purposes as well.

I just had a look at the docs and they don’t mention an order of precedence. Plus, since you can define routes anywhere I dare say that it will be hard to make predictions as to which comes first.

Probably an oversight.

He explicitly said that’s not an option. For our website StackShare, we have this same schema for URLs:


http://stackshare.io/create-stack

And in our routes, for the user route for example:

get '/:id', to: "users#show", as: 'show_user', constraints: lambda { |request|
  User.where('slug ilike ?', request.path_parameters[:id]).size > 0
}

So we can match only if a user exists with that slug.

There has to be a way to do this with Meteor, or there should be. :frowning:

Apologies, I completely skipped that last line of the original post…woops.

However, I am curious as to why top level organization routes would be out of the question? I’m unable to think of a case where you couldn’t re-organize routes for clearer data context?

For example, with StackShare, what is preventing you from doing the following:

I get that there is a cool feeling in having a URL just being someone’s username…if that is the main requirement, then use top-level domains for users, and move views of other context somewhere else…for example, here is twitter’s URL for moments/notifications/etc:

Like I said…it’s nice to have such simple, all top-level domains, but it’s usually not the best practice in terms of organization and clear context for the user.

Edit: BTW StackShare…looks nice! Great work over there, love the idea.

In other languages, I’ve tackled the same issue by writing my own custom resolver. Something akin to:

  1. Check for hard-coded strings
    You don’t want anything else to possibly get resolved. This happens first, no-matter what, also, by far the fastest of the following cases.
This also means you should probably prevent people from signing up with the username `login`.

_Examples:_  
  • mysite.com/signup

  • mysite.com/login

  1. check for slugs a la blog posts

    You might be able to get better preformance here by having a quick test to see if the queryString contains a - (assuming -'s are forbidden in usernames). Then you know it must be a slug.

_Examples:_  

* >> mysite.com/hottest-gadgets-at-CES-2016

* >> mysite.com/north-korea-tests-nuke
  1. check for user profiles / and possibly other content you don’t control, like forum posts, wikis, etc
although you could lump the latter in with 

Examples:


I don’t know how well this translates into the FR api… That’s a question for @arunoda. My sneaking suspicion is that you’d have to write something that wraps around FR’s resolver and then translates those into the more traditional FR routes. My post here is a little outside of the scope you seem to describe, but should give you (and others) a good idea of where to go from here.

A few extra pointers… if you’re looking up users or blog posts, you’re going to have to do a DB query which does cost something.

  • extract as much information, make the best guess as to what kinda url it is before you query

    • as I mentioned earlie, if you’re looking up slugs, do a regex for -'s
    • consider doing something like mysite.com/@rozzzly, medium.com does this, I think its pretty rad. In this case you would check for the first char being a @
      • another option is mysite.com/u/rozzzly
    • what about strings after the unknown? e.g. mysite.com/rozzzly/profile you can infer from there being a /profile that the first param is probably a username
  • Cache. cache that caching cache. Trying creating an map from incoming urls to fully qualified routes, this can save you a TON of processing power.

3 Likes

Different companies have different requirements. In our case we wanted something similar to what Github does.


https://github.com/pulls
https://github.com/meteor

Agreed, different companies obviously have different “requirements”. What I’m asking is why is a top-level route a requirement?

Can we agree that they are more confusing to the user looking at them?

Can we agree that they are obviously harder to implement? (at least with Meteor?)

Can we agree that, unless there is truly a requirement for such, there is a better solution (as I have presented above)?

In the Github example, I think it makes my point clear. When I click on github.com/pulls, I don’t know if that page is taking me to a page that lists ALL pulls occuring on github, or just pulls for me.

In fact, that page is actually just querying ALL of the pull requests occuring on Github…your username is just added afterwards to the filter bar…remove it and…

Voila, you see every single pull request. There is no server-side filtering happening due to the URL. They are probably just checking if a user is signed in, and just adding the filter client-side if so.

I don’t really feel like discussing the merits of either approach. I just need to know how to do it this way. :thumbsup:

2 Likes

Ok just remember you are loading your server with all of those queries just to check if a URL is legit before the user even gets to the page. Also means as you grow, your URL checks grow too :wink:

For the time being this is simply the most preferred option for my app.

Let’s wait and see if @arunoda has some advice.

this is simply the most preferred option

Hi there @avalanche1, why do you see this as your preferred option? Is it the look of the URL or is there a reason due to the data of your app?

I’m genuinely trying to understand why you would prefer this configuration if it is not meeting your needs and you have no attachments to said configuration beyond “preference”?

I still struggle to find the problem here. Maybe I missed something.

@rhywden as OP goes with many routes, his application has at least middle-level complexity, so he should be using package-oriented architecture for that. With package-oriented architecture, the order of files is never a problem, because you specify it explicitly.

@avalanche1 inside of a file (or in multiple files in package-oriented architecture), first route to be defined gets checked in the first order. So if you specify all your /myAwesomeRoute before /:myAwesomeParameter, you achieve exactly what you wanted.

I guarantee it works because I use that in my project.

Just curious, so what happens when someone wants to sign up with the username “settings” or “new”, or another route you already have defined? Will you continue to update your prohibited username/post-title/whatever list as you add more routes to your app, and just throw the error? I’m honestly asking as I’m trying to work out all the edge-cases here.

Interestingly enough, there is a twitter.com/new but not as twitter.com/settings…so maybe it is just a few disallowed strings.

I keep one, common list of prohibited strings that I check against each new title/name for multiple collections. So even when I want to change my routes structure for an already working application, I can still afford it, because the list will still be valid and titles/names will still be available. The list is longer and contains synonyms and alternative route names that I was thinking about to use now or in the future.

By any mean this list covers 100% cases, but gives me enough of a confidence.

1 Like

Gotcha, so you pre-plan with synonyms and alt names. Smart :thumbsup:

I’m guessing that list probably doesn’t change too much between diff’t apps? Besides like collection-specific names…

You’re right, most of that is common among apps, like create/add/delete/edit/remove/patch/post/change etc, the rest are collection specific names, depending on what application is for.

I keep both singular and plural form on the list, f.e. user/users, story/stories.

1 Like

Simple. You need to define the /signup route before the generic one: /:cityId