Express named placeholder routes on root

I'm currently building a MEAN app and I have routing setup as you'd expect (/login, /profile, /logout etc). I've come to the point now in my app where I'm building user profile pages and I'd like user profiles to be accessible from the root of the domain, like: www.domain.com/:username.

I currently have the following route which is defined at the end of my routes.js file and just before my catchall/wildcard routes.

app.get('/:username', function (req, res) {
    var username = req.params.username;
        usersTable.findOne({ 'local.username': username }, function (err, user) {               
            if (err)
                return err;

            if (!user)
                res.redirect('/');

            res.render('public-profile.ejs', {
                user: user
            });
        });
});

app.all('/*', function (req, res, next) {
    if (req.user) {
        res.sendfile('views/dashboard.html'); // for angular routing
    } else {
        res.redirect('/login');
    }
});

As it currently stands there is some interference between Express/Angular. If I visit /matthew which is a username and then try to load /profile from the URL bar, Angular doesn't catch this (/* route) and instead loads the default ('/') angular route.

My concern is that I wouldn't be able to create a /:username route in Angular because how will that page know what user data to show? Will I have to create an API call that reads the given URL which then looks for a user in the database with that name? (seems clunky?)

I'm pretty new to building large-scale webapps so advice on the architecture would be great.

The code in your post only configures the server-side routes, which has in fact nothing to do with Angular routing.

Angular is for single-page applications (SPA). If you want to use AngularJS on the client, you probably don't want to serve pages from the server, but return data, i.e your routes are meant for API calls only.

So, on the client-side, you should have something like :

myApp
.constant('MY_BACKEND_URL', 'http://www.domain.com')
.config(['$stateProvider', function ($stateProvider) { // client-side route config

  $stateProvider
    .state('userProfile',{
    url: '/:username',
    templateUrl: 'views/user-profile.html',
    controller: 'UserProfileCtrl',
    resolve: { // lets you resolve asynchronously before page is loaded.
      userData: ['$http', 'MY_BACKEND_URL', '$stateParams', '$state', function ($http, MY_BACKEND_URL, $stateParams, $state) {
        return $http
          .get(MY_BACKEND_URL + '/' + $stateParams.username)// returns a promise of the user data fetch from the API.
          .catch(function (err) {
            $state.go('dashboard');
          });
      }]
    }
  });

  $stateProvider.state('dashboard',{
    url: '/',
    template: 'views/dashboard.html',
    controller: 'DashboardCtrl'
  });
}]);

And on the server side :

// config route for API call to user resource 
app.get('/user/:username', function (req, res) {
  var username = req.params.username;
  usersTable.findOne({ 'local.username': username }, function (err, user) {
    if (err) {
      res.json(500, err);
    } else if (!user) {
      res.json(404, {reason: "No such username", username: username});
    } else {
      res.json(200, user);
    }
  });
});

As a rule of thumb, routes on the client-side (Angular) configure pages, whereas routes on the server-side configure your API, i.e endpoints you call for fetching data.

By the way, you should remove the return err; statement from your callback, because there is no point in returning a value from a callback function that is meant for side-effects only. You probably want to replace it with something like :

if(err){
  res.json(500, err); // return internal server error response.
}