Access /public from server side code in meteorjs

I was trying to use react-i18n package and wanted my text to come from a yaml file located in /public directory. While it worked perfectly on client, on server-side my code failed to access /public.

I googled around, came across many solutions but most of them did not work. Also it looks like there's no standard way to load yaml files in meteor. Comparatively, loading json files is easier.

I know I could have just used one of the meteor packages for i18n (which I actually ended up choosing later), but before that I wanted to try without the package. Just as an exercise, I wanted to read a file from /public using server side code. I wanted to have clearer understanding of why YAML.load() was not working.

Using Assets API

One way to access assets in meteor.js is using assets API. Assets API makes it fairly easy to get files from /private folder.

import T from 'i18n-react';
import YAML from 'yamljs';

// Gets file from private/texts-en.yml
const dictionary = YAML.parse(Assets.getText('texts-en.yml'));

T.setTexts(dictionary);

export default T;

However that means you will have to keep two files, one in public and another in private folder. You can avoid that easily using soft links though.

cd yourMeteorApp
ln -s public/texts-en.yml private/texts-en.yml

That will allow you to read this file from server side code using Assets API as shown above and on client side you can directly access texts-en.yml as /texts-en.yml.

Finding meteor root path

There is another, a bit harder way, where you won't have to link these files and you can also access files from folders other than public. You will have to find the meteor root path using node's fs api and then go into public folder. The reason YAML.load() does not work on server is, it does not know how and where meteor bundles the public folder while running. In simple words directory structure in which you write meteor code is different from directory structure in which code is executed / served. So if you use relative URLs in code using something like yamljs, you will come across an error looking something like

Error: texts-en.yml file not found ENONT

To add to complexity, the directory structure further changes when we bundle and deploy the app on production server. So using relative URL with yamljs becomes pointless. The only way is to get root path of meteor using following code and then load the yaml file.

import T from 'i18n-react';
import YAML from 'yamljs';

import fs from 'fs';
import path from 'path';

const meteorRoot = fs.realpathSync(`${process.cwd()}/../`);
let applicationRoot = fs.realpathSync(`${meteorRoot}/../`);

// if running on dev mode
if (path.basename(fs.realpathSync(`${meteorRoot}/../../../`)) === '.meteor') {
  applicationRoot = fs.realpathSync(`${meteorRoot}'/../../../../`);
}

const dictionary = YAML.load(`${applicationRoot}/public/texts-en.yml`);

T.setTexts(dictionary);

export default T;

However after all that I ended up using meteor universe i18n package. That allowed me to very easily store files in yml format. I am writing another tutorial on using this package. Stay tuned!