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)
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 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 have added Angular Universal in my project(upgraded to angular 8). Which is comparatively a big project. After solving all the window, jquery and localstorage issue I have successfully able to run the app. But the problem is, the server runs on localhost:4000. when I type localhost:4000 in browser nothing happens. But if I put localhost:4000/index.html, It starting to load fine and return to base localhost:4000. Then every angular route works fine. But if I try to refresh any page it doesn't load anything.
my server.ts
import 'zone.js/dist/zone-node';
// ssr DOM
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join('dist/browser', 'index.html')).toString();
const win = domino.createWindow(template);
global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
// mock documnet
global['document'] = win.document;
// othres mock
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;
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';
// 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/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);
// Example Express Rest API endpoints
// app.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;
if (mainModule && mainModule.filename === __filename) {
run();
}
export * from './src/main.server';
I think i need some change with this file to load correctly.
I followed the angular server-side rendering tutorial and created an express server.
How can I add SSL support?
I found how to add SSL in server.js but I don't know how to add it in Typescript.
server.ts
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { renderModuleFactory } from '#angular/platform-server';
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';
enableProdMode();
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');
const { provideModuleMap } = require('#nguniversal/module-map-ngfactory-loader');
app.engine('html', (_, options, callback) => {
renderModuleFactory(AppServerModuleNgFactory, {
document: template,
url: options.req.url,
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP)
]
}).then(html => {
callback(null, html);
});
});
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
app.get('*', (req, res) => {
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
});
app.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});
——————
I used a Reverse Proxy instead
import the fs and https module
import { readFileSync } from 'fs';
import * as https from 'https';
Now create an object to set the key and certificate paths.
const options = {
key: readFileSync('./ssl/privatekey.pem'),
cert: readFileSync('./ssl/certificate.pem'),
};
https.createServer(options, app).listen(PORT , function(){
console.log("Express server listening on port " + PORT);
});
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.