0

I am at a complete loss, as to what is going on, so I will post the Angular Universal server code that is running my express server.

I think there is something within Angular Unviersal's render engine that is completely just blocking websockets.

No configuration or anything I do can resolve the websocket without getting the:

'ws://localhost:4201/echo' failed: Connection closed before receiving a handshake response

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/ai-ggggg-frontend-website/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  server.use('/swagger/api-docs', express.static('./dist/ai-heagggggd-website/browser/swagger'));

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run() {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();

  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';
Christian Matthew
  • 4,014
  • 4
  • 33
  • 43
  • 2
    I don't think you need to use websockets during server side rendering. So you should only try to connect when the code is executed client side – David Jun 10 '20 at 09:28
  • @David are you telling me not to use websockets? How would I connect to a live mutating database from an api? – Christian Matthew Jun 10 '20 at 12:59
  • I was hinting you not to use it when you are doing SSR. Can't you just connect while on client side? – David Jun 10 '20 at 13:16
  • @David sorry I am new to SSR and how it is different than non-SSR. I want to accomplish having a live connection to my server so that when data changes from a form entry I can see it real-time on the client. How is that possible with SSR – Christian Matthew Jun 10 '20 at 13:23
  • Just checking, can you fetch data without sockets? – David Jun 10 '20 at 13:39
  • Of course with an event that would fetch that data. But there is data that is being entered from another source so it would not be a triggered event to the front-end client. so how else would I do that without a websocket? – Christian Matthew Jun 10 '20 at 13:47
  • What I was thinking is: if the code is executed server side, then fetch data without websockets. Then, when the page is rendered and the client app takes over, connect the websockets. Not sure if that would suit your needs. You can do these chekcs with `isPlatformBrowser` – David Jun 10 '20 at 14:00
  • isn't connecting the websockets the issue? I can't connect the websockets at all – Christian Matthew Jun 10 '20 at 14:43
  • meaning from the server side. otherwise your solution seems completely viable – Christian Matthew Jun 10 '20 at 15:13
  • @David I placed a bounty on it – Christian Matthew Jun 12 '20 at 00:55
  • Does that help? https://stackoverflow.com/a/41721980/1160794 – David Jun 12 '20 at 06:41
  • my issue is from the server side. Like I can't even create a socket. – Christian Matthew Jun 12 '20 at 21:14
  • @David I did figure out the socket part and yes that link you sent now helps. It seems like a super hack and slows the down the webpage considerably. – Christian Matthew Jun 14 '20 at 19:46
  • So it your problem solved? If not, you should update your question with what you've done and what the exact problem is – David Jun 15 '20 at 11:53
  • It is. express-ws is defunct. And unfortunately many of the web tutorials and instruction are using that as their example. It hasn't been updated in 2 years I should have saw that. That's my fault. I will give an update soon about it – Christian Matthew Jun 15 '20 at 14:25

2 Answers2

2

Not sure why this great question is down-voted.

Your client ws request fails because the development tool stack for Angular Universal includes BrowserSync. So the upgrade of incoming ws requests are misunderstood by BrowserSync and immediately ended.

So ignore the failed ws request while you are developing your project as Socket.io will fall back to HTTP poll requests.

To test that your app does work with a web socket connection build your app (npm run build:ssr) and the run it locally (node dist/[project-name]/server/main.js).

For anyone unsure how to add Socket.io in server.ts:

import * as SocketIO from 'socket.io';

...

function run(): void {
  const port = +process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  const service = server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
  
  const io = SocketIO(service, {perMessageDeflate:false,serveClient:false});
  ...
}

My answer is 3 months late and you've probably figured this out already.

gwest7
  • 1,556
  • 19
  • 26
0

Wether you are using express-ws or an alternative (socket based or SSE), I don't think that you need to connect to websockets while the application is being rendered server side.

That's why checking if the code is executed client side or server side with isPlatformBrowser makes sense. It is not a hack, it is usually used to avoid referencing objects/APIs that are not available on the server (navigator, localStorage,...). I don't really see how it could slow the website.

What I was also suggested is to retrieve the initial data another way (if possible) when the code is executed server side, so that the component's data is not blank when displayed in the browser

export class MyComponent 

    private isBrowser: boolean;

    constructor(@Inject(PLATFORM_ID) private platformId: Object) 
    {
        this.isBrowser = isPlatformBrowser(this.platformId);
    }

    ngOnInit()
    {
        if (isBrowser) 
        {
            //connect yo websockets here
        }
        else
        {
            //Either do nothing, or try to fetch initial data without websockets if possible
        }
    }
}
David
  • 33,444
  • 11
  • 80
  • 118