2

For the life of me, I cannot find a server-side rendering solution that works for my Angular 5 site. In my most recent attempt, I've been following a solution provided by AngularFirebase.com: https://angularfirebase.com/lessons/seo-angular-part-1-rendertron-meta-tags/

All of the content of my app is hardcoded, so I skipped the first part of the tutorial. I do use Firestore 2, but only to capture the fields on my contact form. My routing is very simple, all contained within the module.ts file. The meat of what I've done is contained within my index.js file:

const functions = require('firebase-functions');
const express = require('express');
const fetch = require('node-fetch');
const url = require('url');
const app = express('');

const appUrl = 'customapp.firebaseapp.com';
const renderUrl = 'https://render-tron.appspot.com/render';


    function generateUrl(request) {
        return url.format({
            protocol: request.protocol,
            host: appUrl,
            pathname: request.originalUrl
        });
    }
    function detectBot(userAgent){
        const bots = [
            'bingbot',
            'yandexbot',
            'duckduckbot',
            'slurp',
            //Social
            'twitterbot',
            'facebookexternalhit',
            'linkedinbot',
            'embedly',
            'pinterest',
            'W3C_Validator'
        ]

        const agent = userAgent.toLowerCase();

        for (const bot of bots) {
            if (agent.indexOf(bot) > -1) {
                console.log('bot detected', bot, agent);
                return true;
            }
        }

        console.log('no bots found');
        return false;
    }
app.get('*', (req, res) =>{
    const isBot = detectBot(req.headers['user-agent']);

    if (isBot) {
        const botUrl = generateUrl(req);

        fetch(`${renderUrl}/${botUrl}`)
            .then(res => res.text())
            .then(body => {
                res.set('Cache-Control', 'public, max-age=300, s-maxage=600');
                res.set('Vary', 'User-Agent');

                res.send(body.toString())
            });
    } else {
        fetch(`https://${appUrl}/`)
            .then(res => res.text())
            .then(body => {
                res.send(body.toString());
            });
    }
});

exports.app = functions.https.onRequest(app);

I've set up my firebase.json with these rewrites:

"rewrites": [
      {
        "source": "**",
        "function": "app"
      }
    ]

Everything deploys successfully, but when I hit my site, my Firebase functions logs spit out:

Function execution took 677 ms, finished with status: 'crash'
warning  FetchError: request to https://test-297a3.firebaseapp.com/ failed, reason: getaddrinfo ENOTFOUND test-297a3.firebaseapp.com test-297a3.firebaseapp.com:443
        at ClientRequest.<anonymous> (/user_code/node_modules/node-fetch/lib/index.js:1376:11)
        at emitOne (events.js:96:13)
        at ClientRequest.emit (events.js:188:7)
        at TLSSocket.socketErrorListener (_http_client.js:310:9)
        at emitOne (events.js:96:13)
        at TLSSocket.emit (events.js:188:7)
        at connectErrorNT (net.js:1025:8)
        at _combinedTickCallback (internal/process/next_tick.js:80:11)
        at process._tickDomainCallback (internal/process/next_tick.js:128:9)
warning . Unhandled rejection

From what I read here, it seemed like it could be how I'm formatting the URL, but I've tried formatting it differently. In the second fetch statement fetch(https://${appUrl}/) I removed "https://", but fetch requires an absolute URL.

How can I resolve this conflict? Any advice is appreciated!

Kellen
  • 1,072
  • 2
  • 15
  • 32

1 Answers1

0

Looks like you're on free tier Firebase? If so, you don't have access to any network, can only use Google APIs. Need to enable billing.

Have you considered running Angular Universal in FB cloud functions? Angularfire Lite supports that and I believe Angularfire2 will too soon if not already. Can be done without them too but there may be some hurdles to get around, like manually closing socket connections when on server, wrapping things in zone.runOutsideAngular etc.

funkizer
  • 4,626
  • 1
  • 18
  • 20