I have my basic server.js :
var express = require('express');
var path = require('path');
var port = process.env.PORT || 8080;
var app = express();
app.use(express.static('src/client/'));
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname + '/src/client/index.html'))
});
app.listen(port);
console.log('server started');
however I now have a react-routing that I want to import so that when the browser is refreshed on the server the page is still accessible.
My attempt so far is:
import express from 'express';
import path from 'path';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './src/client/app/config/routes.jsx';
let port = process.env.PORT || 8080;
let app = express();
app.use(express.static('src/client/'));
// app.get('/', (req, res) from> {
// res.sendFile(path.resolve(__dirname + '/src/client/index.html'))
// });
app.get('/', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
// in case of error display the error message
if (err) {
return res.status(500).send(err.message);
}
// in case of redirect propagate the redirect to the browser
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
// generate the React markup for the current route
let markup;
if (renderProps) {
// if the current route matched we have renderProps
markup = renderToString(<RouterContext {...renderProps}/>);
}
// else {
// // otherwise we can render a 404 page
// markup = renderToString(<NotFoundPage/>);
// res.status(404);
// }
// render the index template with the embedded React markup
return res.render(path.resolve(__dirname + '/src/client/index.html'));
}
);
});
app.listen(port);
console.log('server started');
but when I run "node server.js" I get the following error:
(function (exports, require, module, __filename, __dirname) { import express from 'express';
^^^^^^
SyntaxError: Unexpected reserved word
Just about everything in ES6 works in new versions of node except for the import/export keyword. You'll have to transpile it to ES5 with gulp, webpack, etc.
Related
Im using a Express server and trying to implement Socket.io for the first time, and I keep getting this whenever I open it:
GET /socket.io/?EIO=4&transport=polling&t=OKgNATB 404 0.526 ms - 149
GET /socket.io/?EIO=4&transport=polling&t=OKgNBww 404 0.332 ms - 149
GET /socket.io/?EIO=4&transport=polling&t=OKgNDOV 404 0.411 ms - 149
app.ts:
import express from 'express';
import morgan from 'morgan';
import favicon from 'serve-favicon';
import path from 'path';
import cors from 'cors';
import { errorManager } from './middlewares/errors_middleware.js';
import { ordersRouter } from './routers/OrdersRouter.js';
import { usersRouter } from './routers/UsersRouter.js';
import { createServer } from 'http';
import { Server } from 'socket.io';
export const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: '*',
},
});
io.on('connection', (socket) => {
console.log('HELLO');
});
app.use(favicon(path.join('.', 'public', 'favicon.ico')));
app.use(cors());
app.use(morgan('dev'));
app.use(express.json());
app.get('/', (_req, res, next) => {
res.send('H');
next();
});
Routers
app.use('/orders', ordersRouter);
app.use('/users', usersRouter);
app.use(errorManager);
index.ts:
import http from 'http';
import createDebug from 'debug';
import { app } from './app.js';
import { dbConnect } from './dbconnect/dbconnect.js';
const debug = createDebug('StoryLine:INDEX');
const port = 3300;
const server = http.createServer(app);
server.on('listening', () => {
const addr = server.address();
if (addr === null) return;
let bind: string;
if (typeof addr === 'string') {
bind = 'pipe ' + addr;
else {
bind =
addr.address === '::'
? `http://localhost:${addr?.port}`
: `port ${addr?.port}`;
}
debug(`SERVER: Listening on: ${bind}`);
});
server.on('error', (error: Error, response: http.ServerResponse) => {
response.write(error.message);
response.end();
});
dbConnect()
.then((mongoose) => {
debug(
'Database connection -> SUCCESSFUL: ',
mongoose.connection.db.databaseName
);
server.listen(port);
})
.catch((error) => {
debug('Database connection -> COULD NOT CONNECT');
server.emit(error);
});
I simply want to get a console.log telling me that a user is connecting. I have tried following the Socket.io docs for express but its not working for me.
I'm trying to add websockets to a server that's using angular universal. As far as I can tell, express is consuming my request before it gets to my sockets, but I could be wrong about that.
I get this error in chrome:
WebSocket connection to 'ws://localhost:4200/socket' failed: Connection closed before receiving a handshake response
and I get this error in firefox:
Firefox can’t establish a connection to the server at ws://localhost:4200/socket.
when I run a separate nodejs server without the angular code, the websockets work fine.
Here is the relevant part of my server.ts
import 'zone.js/dist/zone-node'
import { ngExpressEngine } from '#nguniversal/express-engine'
import * as express from 'express'
import { join } from 'path'
import { AppServerModule } from './src/main.server'
import { APP_BASE_HREF } from '#angular/common'
import { existsSync } from 'fs'
import * as WebSocket from 'ws'
// 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/CAHClone/browser')
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'
// 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)
server.use(express.json())
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}))
// All regular routes use the Universal engine
server.get('*', (req, res, next) => {
if (req.url === '/socket') return next() // leave '/socket' open for the websockets
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 expressApp = app()
const server = expressApp.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`)
})
// Websockets
const wss = setupWebsockets(server)
}
function setupWebsockets(server) {
const wss = new WebSocket.Server({ server, path: '/socket' })
wss.on('connection', ws => {
console.log('Client connected.')
ws.send({message: 'Hi there!'})
})
wss.on('message', msg => {
console.log('Client said: ' + msg.toString())
})
return wss
}
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'
And here is a server.js file that works without angular related code
const express = require('express')
const WebSocket = require('ws')
const { join } = require('path')
const { existsSync } = require('fs')
// The Express app is exported so that it can be used by serverless Functions.
function app() {
const server = express()
server.use(express.json())
server.use('*', (req, res, next) => {
if (req.url === '/socket') return next()
res.end('Hello, world!')
})
return server
}
function setupWebsockets(server) {
const wss = new WebSocket.Server({ server, path: '/socket' })
console.log('set up websocket server');
wss.on('connection', ws => {
console.log('Client connected.');
ws.send('Hi there!')
})
wss.on('message', msg => {
console.log('Client said: ' + msg.toString());
})
}
function run() {
const port = process.env.PORT || 4000
const expressApp = app()
const server = expressApp.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`)
})
// Websockets
const wsServer = setupWebsockets(server)
}
run()
Could anyone help me understand what part of angular universal is breaking my websockets, and how to fix it?
I ran into this issue with the 'ws' module and passing in an express server. Try creating your server with the 'http' module and pass it into both express and 'ws'. I also recommend taking care of any authorization that you are going to do in the 'upgrade' event if necessary.
Did you solve the matter with dev:ssr ?
It is indeed working fine with serve:ssr but the only way I made it work with dev:ssr in local development is by using a proxy from ws://localhost:4200/ws to ws://localhost:4000
{
"/ws": {
"target": "ws://localhost:4000",
"secure": false,
"ws": true
}
}
Se my question here : Angular Universal (ssr-dev-server) Setting or getting the random port of the express app in dev:ssr
server/src/index.js
import './env';
import fs from 'fs';
import cors from 'cors';
import path from 'path';
import helmet from 'helmet';
import morgan from 'morgan';
import express from 'express';
import favicon from 'serve-favicon';
import bodyParser from 'body-parser';
import compression from 'compression';
import * as Sentry from '#sentry/node';
import routes from './api/routes';
import json from './api/middlewares/json';
import logger, { logStream } from './utils/logger';
import * as errorHandler from './api/middlewares/errorHandler';
import initMongo from './config/mongo';
// Init MongoDB
initMongo();
// Initialize Sentry
// https://docs.sentry.io/platforms/node/express/
Sentry.init({ dsn: process.env.SENTRY_DSN });
// Initialize Express
const app = express();
const APP_PORT =
(process.env.NODE_ENV === 'test' ? process.env.TEST_APP_PORT : process.env.APP_PORT) || process.env.PORT || '3000';
const APP_HOST = process.env.APP_HOST || '0.0.0.0';
const pathToSwaggerUi = require('swagger-ui-dist').absolutePath();
app.set('port', APP_PORT);
app.set('host', APP_HOST);
app.locals.title = process.env.APP_NAME;
app.locals.version = process.env.APP_VERSION;
// This request handler must be the first middleware on the app
app.use(Sentry.Handlers.requestHandler());
app.use(express.static(path.join(__dirname, './public')));
app.use(cors());
app.use(helmet());
app.use(compression());
app.use(morgan('tiny', { stream: logStream }));
app.use(bodyParser.json());
app.use(errorHandler.bodyParser);
app.use(json);
// API Routes
app.use('/api', routes);
// Swagger UI
// Workaround for changing the default URL in swagger.json
// https://github.com/swagger-api/swagger-ui/issues/4624
const swaggerIndexContent = fs
.readFileSync(`${pathToSwaggerUi}/index.html`)
.toString()
.replace('https://petstore.swagger.io/v2/swagger.json', '/api/swagger.json');
app.get('/', (req, resp) => resp.sendFile(path.resolve(path.join(__dirname, './public', 'index.html'))));
app.get('/api-docs/index.html', (req, res) => res.send(swaggerIndexContent));
app.get('/api-docs', (req, res) => res.redirect('/api-docs/index.html'));
app.use('/api-docs', express.static(pathToSwaggerUi));
// This error handler must be before any other error middleware
app.use(Sentry.Handlers.errorHandler());
// Error Middlewares
app.use(errorHandler.genericErrorHandler);
app.use(errorHandler.methodNotAllowed);
app.listen(app.get('port'), app.get('host'), () => {
logger.info(`Server started at http://${app.get('host')}:${app.get('port')}/api`);
});
// Catch unhandled rejections
process.on('unhandledRejection', err => {
logger.error('Unhandled rejection', err);
try {
Sentry.captureException(err);
} catch (err) {
logger.error('Raven error', err);
} finally {
process.exit(1);
}
});
// Catch uncaught exceptions
process.on('uncaughtException', err => {
logger.error('Uncaught exception', err);
try {
Sentry.captureException(err); } catch (err) {
logger.error('Raven error', err);
} finally {
process.exit(1);
}
});
export default app;
server/src/api/routes/quizz.js
import express from 'express';
import passport from'passport';
import * as quizzController from '../controllers/quizz';
const router = express.Router();
const requireAuth = passport.authenticate('jwt', {
session: false,
});
/**
* GET /api/quizz
*/
router.get('/all', quizzController.fetchAll);
export default router;
server/src/api/routes/index.js
import express from 'express';
import fs from 'fs';
import { removeExtensionFromFile } from '../middlewares/utils';
import swaggerSpec from "../../utils/swagger";
import * as utils from '../middlewares/utils';
const router = express.Router();
const routesPath = `${__dirname}/`;
const isDynamicImport = routeFile =>
routeFile !== 'index' && routeFile !== 'auth';
/*
* Load routes statically and/or dynamically
*/
/**
* GET /api/swagger.json
*/
router.get('/swagger.json', (req, res) => {
res.json(swaggerSpec);
});
// Load Auth route
router.use('/', require('./auth'));
// Loop routes path and loads every file as a route except this file and Auth route
fs.readdirSync(routesPath).filter(file => {
// Take filename and remove last part (extension)
const routeFile = removeExtensionFromFile(file);
// Prevents loading of this file and auth file
return isDynamicImport(routeFile)
? router.use(`/${routeFile}`, require(`./${routeFile}`))
: '';
});
router.get('/', (req, res) => {
res.json({
app: req.app.locals.title,
apiVersion: req.app.locals.version
});
});
/*
* Handle 404 error
*/
router.use('*', (req, res) => {
utils.handleError(res, {
code: 404,
message: 'URL_NOT_FOUND',
});
});
export default router;
I am trying to get a list of all the quizzes but it returns this error
"throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))"
If someone has been in the same situation please help me please
You should check all your JavaScript files. It seems that this line is missing in any JS file:
module.exports = router
I'm using ReactDOMServer to render an app, and I got the following error:
I've setup express in react typescript project. What am I doing wrong? Please help me out, thanks you.
Here's the relevant code:
App.tsx
import * as React from 'react';
class App extends React.Component {
render() {
return (
<h1>Testing App Page</h1>
);
}
}
export default App
entry.js
require('ignore-styles');
require("#babel/register")({
ignore: [ /(node_modules)/ ],
presets: [[
"#babel/preset-env",
{
"targets": {
"esmodules": true
}
}
], '#babel/preset-react']
});
require('./index');
index.js
import express from 'express';
import serverRenderer from './middleware/renderer';
const PORT = 3000;
const path = require('path');
const app = express();
const router = express.Router();
router.use('^/$', serverRenderer);
// other static resources should just be served as they are
router.use(express.static(
path.resolve(__dirname, '..', 'build'),
{ maxAge: '1d' },
));
app.use(router);
app.listen(PORT, (error) => {
console.log("App port listen on ", PORT);
});
middleware/renderer.js
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import App from '../../src/App';
// const App = require("../../src/App"); <= also try this but not work
const path = require("path");
const fs = require("fs");
export default (req, res, next) => {
// point to the html file created by CRA's build tool
const filePath = path.resolve(__dirname, '..', '..', 'build', 'index.html');
fs.readFile(filePath, 'utf8', (err, htmlData) => {
if (err) {
console.error('err', err);
return res.status(404).end()
}
// render the app as a string
const html = ReactDOMServer.renderToString(React.createElement(<App />));
// inject the rendered app into our html and send it
return res.send(
htmlData.replace(
'<div id="root"></div>',
`<div id="root">${html}</div>`
)
);
});
}
Your problem is not related to typescript. In middleware/renderer.js you wrote:
const html = ReactDOMServer.renderToString(React.createElement(<App />));
This is incorrect. Change it to:
const html = ReactDOMServer.renderToString(React.createElement(App));
And you're good to go.
You should read the error message. It's already told you what is going wrong. Also learn more about the React concepts, especially the difference between "JSX Element" and "JSX Component".
I'm trying to serve up React from my server via express.
The error I'm getting, though, when I hit localhost:3000 is:
TypeError: (0 , _reactRouter.match) is not a function
at /Users/chris/code/affordance/app/server.js:20:3
Here's the server.js file that does that:
import path from 'path';
import { Server } from 'http';
import Express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import NotFoundPage from './components/NotFoundPage';
// Initialize the express server, and tell it to use ejs
const app = new Express();
const server = new Server(app);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Tell express where the static assets are
app.use(Express.static(path.join(__dirname, 'static')));
app.get('*', (req, res) => {
match(
{ routes, location: req.url },
(err, redirectLocation, renderProps) => {
if (err) {
return res.status(500).send(err.message);
}
if (redirectLocation) {
return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}
let markup;
if (renderProps) {
markup = renderToString(<RouterContext {...renderProps}/>);
} else {
markup = renderToString(<NotFoundPage/>);
res.status(404);
}
return res.render('index', { markup });
}
);
});
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
So as far as I can tell, I'm importing it the way I see it used elsewhere (for instance, here).
I'm missing something, and I don't know what to guess or think next. What am I doing wrong?
ps - react-router is at version 4.0.0, docs for match are here
Your code looks correct if you used react router prior to v4, but react-router v4 has breaking changes throughout the codebase, including the method for server rendering. In v4, there is a new component specifically for server rendering - StaticRouter.
Your code should looks something like this with v4:
import path from "path";
import { Server } from "http";
import Express from "express";
import React from "react";
import { renderToString } from "react-dom/server";
import { StaticRouter } from "react-router";
import App from "./app";
import NotFoundPage from "./components/NotFoundPage";
// Initialize the express server, and tell it to use ejs
const app = new Express();
const server = new Server(app);
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
// Tell express where the static assets are
app.use(Express.static(path.join(__dirname, "static")));
app.get("*", (req, res) => {
// This context object contains the results of the render
const context = {};
const html = renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
res.status(200).send(html);
});
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || "production";
server.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
Here is a really nice annotated article by EbayTech showing how to set up an app with StaticRouter(for server) and BrowserRouter(for client)