For the first time I am very disappointed of angular or myself because I don't know if it's angular problem or mine. I have angular 6.0.0 project which is not very big, but not small too. I implemented angular universal ssr in my project.
Results without ssr : *hint I have lazy modules. Main is my first page only.
Results with ssr on heroku free server (but same main.js size on localhost, just load time on localhost is 40ms) :
I think those numbers are way too big ? SSR works fine, but the load time is terrible - from 184ms to 2.50s.
Detailed project information :
Package.json ( hint: i am using heroku to deploy my web to nodejs server) :
{
....
"engines": {
"node": "8.11.1",
"npm": "6.5.0"
},
"scripts": {
"ng": "ng",
"start:heroku": "node dist/server",
"start": "ng serve --aot",
"build": "ng build",
"lint": "ng lint Deals",
"build:client-and-server-bundles": "ng build --prod --aot && ng run Deals:server:production",
"build:prerender": "npm run build:client-and-server-bundles && npm run compile:server && npm run generate:prerender",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"compile:server": "tsc -p server.tsconfig.json",
"generate:prerender": "cd dist && node prerender",
"serve:prerender": "cd dist/browser && http-server",
"serve:ssr": "node dist/server ",
"postinstall": "npm run build:ssr"
},
"pre-commit": [],
"private": true,
"dependencies": {
"#angular-devkit/build-angular": "0.6.0",
"#angular/animations": "^6.1.10",
"#angular/cdk": "^7.1.0",
"#angular/cli": "6.0.0",
"#angular/common": "^6.0.0",
"#angular/compiler": "^6.0.0",
"#angular/compiler-cli": "^6.0.0",
"#angular/core": "^6.0.0",
"#angular/fire": "^5.1.1",
"#angular/forms": "^6.0.0",
"#angular/http": "^6.0.0",
"#angular/language-service": "^6.0.0",
"#angular/material": "^7.1.0",
"#angular/platform-browser": "^6.0.0",
"#angular/platform-browser-dynamic": "^6.0.0",
"#angular/platform-server": "^6.0.0",
"#angular/pwa": "^0.11.4",
"#angular/router": "^6.0.0",
"#angular/service-worker": "^6.0.0",
"#ngrx/core": "^1.2.0",
"#ngrx/store-devtools": "^6.1.2",
"#nguniversal/common": "^6.0.0",
"#nguniversal/express-engine": "^6.0.0",
"#nguniversal/module-map-ngfactory-loader": "^6.0.0",
"#ngx-translate/core": "^10.0.2",
"#ngx-translate/http-loader": "^4.0.0",
"#types/node": "^8.0.30",
"angular2-cookie": "^1.2.6",
"angularfire2": "^5.1.1",
"angulartics-clicky": "^1.0.0",
"angulartics2": "^6.3.1",
"codelyzer": "^4.0.2",
"core-js": "^2.4.1",
"express": "^4.15.2",
"firebase": "^5.6.0",
"hammerjs": "^2.0.8",
"http-server": "^0.10.0",
"ngx-page-scroll": "^5.0.1",
"nodemailer": "^4.7.0",
"npm": "^6.4.1",
"pre-commit": "^1.2.2",
"reflect-metadata": "^0.1.10",
"rxjs": "^6.2.2",
"ts-loader": "^4.2.0",
"tslint": "^5.7.0",
"typescript": "~2.7.2",
"zone.js": "^0.8.26"
},
Server.ts which renders ssr :
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '#angular/core';
// Express Engine
import { ngExpressEngine } from '#nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '#nguniversal/module-map-ngfactory-loader';
import * as express from 'express';
import { join } from 'path';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Express server
const app = express();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./server/main');
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP),
// In case you want to use an AppShell with SSR and Lazy loading
// you'd need to uncomment the below. (see: https://github.com/angular/angular-cli/issues/9202)
// {
// provide: NgModuleFactoryLoader,
// useClass: ModuleMapNgFactoryLoader,
// deps: [
// Compiler,
// MODULE_MAP
// ],
// },
]
}));
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), {
maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {});
server.tsconfig.json :
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2017",
"dom"
]
},
"include": ["server.ts", "prerender.ts"]
}
Maybe you need to use compression?
https://github.com/Angular-RU/angular-universal-starter/blob/6d1e980655a55b2e1651ac5d040aaff80bc8fe8c/server.ts#L45
server.ts
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
const test = process.env['TEST'] === 'true';
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '.', 'dist', 'index.html')).toString();
const win = domino.createWindow(template);
const files = fs.readdirSync(`${process.cwd()}/dist-server`);
global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
global['document'] = win.document;
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
const { provideModuleMap } = require('#nguniversal/module-map-ngfactory-loader');
const mainFiles = files.filter((file) => file.startsWith('main'));
const hash = mainFiles[0].split('.')[1];
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.${hash}`);
import { ngExpressEngine } from '#nguniversal/express-engine';
import { REQUEST, RESPONSE } from '#nguniversal/express-engine/tokens';
const PORT = process.env.PORT || 4000;
import { ROUTES } from './static.paths';
import { exit } from 'process';
enableProdMode();
const app = express();
app.use(compression());
app.use(cookieparser());
const redirectowww = false;
const redirectohttps = true;
const wwwredirecto = true;
app.use((req, res, next) => {
// for domain/index.html
if (req.url === '/index.html') {
res.redirect(301, 'https://' + req.hostname);
}
// check if it is a secure (https) request
// if not redirect to the equivalent https url
if (
redirectohttps &&
req.headers['x-forwarded-proto'] !== 'https' &&
req.hostname !== 'localhost'
) {
// special for robots.txt
if (req.url === '/robots.txt') {
next();
return;
}
res.redirect(301, 'https://' + req.hostname + req.url);
}
// www or not
if (redirectowww && !req.hostname.startsWith('www.')) {
res.redirect(301, 'https://www.' + req.hostname + req.url);
}
// www or not
if (wwwredirecto && req.hostname.startsWith('www.')) {
const host = req.hostname.slice(4, req.hostname.length);
res.redirect(301, 'https://' + host + req.url);
}
// for test
if (test && req.url === '/test/exit') {
res.send('exit');
exit(0);
return;
}
next();
});
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
}),
);
app.set('view engine', 'html');
app.set('views', 'src');
app.get('*.*', express.static(path.join(__dirname, '.', 'dist')));
app.get(ROUTES, express.static(path.join(__dirname, '.', 'static')));
app.get('*', (req, res) => {
global['navigator'] = req['headers']['user-agent'];
const http =
req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];
const url = req.originalUrl;
// tslint:disable-next-line:no-console
console.time(`GET: ${url}`);
res.render(
'../dist/index',
{
req: req,
res: res,
providers: [
{
provide: REQUEST,
useValue: req,
},
{
provide: RESPONSE,
useValue: res,
},
{
provide: 'ORIGIN_URL',
useValue: `${http}://${req.headers.host}`,
},
],
},
(err, html) => {
if (!!err) {
throw err;
}
// tslint:disable-next-line:no-console
console.timeEnd(`GET: ${url}`);
res.send(html);
},
);
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});
Related
I am running my node backend on port 8000 and my dev server front end on port 9002. Ive tried a few ways to proxy to my backend when running dev, but all seem to fail (maybe because i also have
"type": "module"
)? I have tried http-proxy-middleware, but with no success.
In my server.cjs file, I have an app.get('/properties'....). It retrieves an array of objects with address, city and zip. On postman, running on port 8000, I get the correct response. Additionally, when i run start on port 8000 on my frontend, I also get the correct response. However, when I run on port 9002, my error is http://localhost:9002/properties 404, not found.
For development purposes, I need to be able to work on development, that way I wint have to run build for every UI change I make. Here is some snippets of my code below:
package.json
{
"name": "properties-three",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"proxy": "http://localhost:8000",
"scripts":
{
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config webpack.config.cjs",
"start": "nodemon --use_strict server.mjs",
"build": "webpack --mode production"
}, "
author": "",
"license": "ISC",
"devDependencies": {
"#babel/core": "^7.19.6",
"#babel/preset-env": "^7.19.4",
"#babel/preset-react": "^7.18.6",
"babel-loader": "^8.2.5",
"copy-webpack-plugin": "^11.0.0",
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
"dependencies": {
"axios": "^1.1.3",
"cors": "^2.8.5",
"express": "^4.18.2",
"http-proxy-middleware": "^2.0.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.4.2",
"webpack-dev-server": "^4.11.1"
}
}
setupProxy.js
import {createProxyMiddleware} from "http-proxy-middleware";
module.exports = function(app) {
app.use(
'/api/properties',
createProxyMiddleware({
target: 'http://localhost:8000',
changeOrigin: true,
secure: false,
ws: true
})
);
};
webpack.config.cjs:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const path = require('path');
const htmlPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
});
module.exports = {
entry: "./src/index.js",
output: { // NEW
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js",
publicPath: "/"
}, // NEW Ends
devServer: {
port: 9002,
static: path.join(__dirname, 'dist'),
hot: true,
historyApiFallback: true
},
resolve: {
extensions: ['.ts', ".js", ".jsx"],
},
mode: "development",
plugins: [htmlPlugin],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
server.mjs
import express from 'express';
import cors from 'cors';
import path from "path";
import properties from './routes/properties.routes.js'
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = 8000;
app.use(express.static(`${process.cwd()}/build`));
app.use(express.static(`${process.cwd()}/public`));
app.use(cors());
app.use(express.json());
app.use("/properties", properties);
app.use(express.static(path.join(__dirname, '/dist')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(PORT, () => console.log(`server is running on http://locahost:${PORT}`));
App.js
import React, {useState, useEffect} from "react";
import axios from "axios";
function App() {
const [properties, setProperties] = useState([]);
useEffect(() =>{
axios.get('/properties').then((response) =>{
setProperties(response.data)
})
}, []);
return (
<div className="App">Test</div>
);
}
export default App;
I have tried changing my setupProxy.js to use
import {createProxyMiddleware} from "http-proxy-middleware";
instead of
const { createProxyMiddleware } = require('http-proxy-middleware');
I have also tried changing in the component
axios.get('/properties')
to
axios.get('/api/properties')
I figured out the fix. As #NirAlfasi pointed out, I was running the BE and the FE on the same machine. Additionally, my db is currently using mock data stored within my project (for the meantime).
Within my App.js, all I needed to do was create a const baseUrl pointing to my BE port. Then in the get request on the FE, point to that baseUrl, plus the endpoint.
const baseURL = 'http://localhost:8000';
axios.get(`${baseURL}/properties`).then((response)...
The issue is already describe here
Mocha API Testing: getting 'TypeError: app.address is not a function'
Tried to test my Shopify app using Mocha, Chai created with Nextjs. Tried several workarounds mentioned on the above link. But it still produces the following error
GET requests
GET /customers
1) Success should return true
0 passing (1s)
1 failing
1) GET requests
GET /customers
Success should return true:
TypeError: app.address is not a function
at serverAddress (node_modules\chai-http\lib\request.js:282:18)
at new Test (node_modules\chai-http\lib\request.js:271:53)
at Object.obj.<computed> [as get] (node_modules\chai-http\lib\request.js:239:14)
at Context.<anonymous> (test\appTest.js:44:10)
at processImmediate (node:internal/timers:466:21)
apptest.js
let chai = require("chai");
let chaiHttp = require("chai-http");
const server = require("../server/index");
chai.should();
chai.use(chaiHttp);
describe("GET requests", () => {
describe("GET /customers", () => {
it("Success should return true", (done) => {
chai.request(server)
.get("/customers")
.end((err, res) => {
console.log('Error => ', err);
console.log('Response => ', res);
res.body.success.should.equal(true)
done();
});
});
});
});
server.js
import "#babel/polyfill";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "#shopify/shopify-api";
import dotenv from "dotenv";
import "isomorphic-fetch";
import Koa from "koa";
import Router from "koa-router";
import next from "next";
const mongoose = require("mongoose");
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
const AccessToken = {};
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
API_VERSION: ApiVersion.January22,
//API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
// This should be replaced with your preferred storage strategy
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
AccessToken["access_token"] = accessToken;
ctx.cookies.set("accessToken", accessToken, {
httpOnly: false,
secure: true,
sameSite: "none",
});
ctx.cookies.set("shop_name", shop, {
secure: true,
sameSite: "none",
httpOnly: false,
});
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
ctx.cookies.set("HOST", host, {
secure: true,
sameSite: "none",
httpOnly: false,
});
//Database Connection
const shopName = shop.replace("myshopify.com", "");
const formattedShopName = shopName
.replace(/[^a-z\d\s]+/gi, "")
.replace(/\s+/g, "")
.toLowerCase();
const connectionStr = `${process.env.CONNECTION_STRING}${formattedShopName}`;
await mongoose
.connect(connectionStr, {
// creds
})
.then(async () => {
console.log("database connected");
})
.catch((err) => console.log(err));
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
router.get("/customers", async (ctx, next) => {
// Logics to fetch customers
});
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", verifyRequest(), handleRequest);
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
index.js
require("#babel/register")({
presets: ["#babel/preset-env"],
ignore: ["node_modules"],
compact: false,
});
// Import the rest of our application.
module.exports = require("./server.js");
package.json
{
"name": "shopify-app-node",
"version": "1.0.0",
"description": "Shopify's node app for CLI tool",
"scripts": {
"lint": "next lint",
"test": "mocha",
"dev": "cross-env NODE_ENV=development nodemon ./server/index.js --watch ./server/index.js",
"build": "NEXT_TELEMETRY_DISABLED=1 next build",
"start": "cross-env NODE_ENV=production node ./server/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Shopify/shopify-app-node.git"
},
"author": "Shopify Inc.",
"license": "MIT",
"bugs": {
"url": "https://github.com/shopify/shopify-app-node/issues"
},
"dependencies": {
"#babel/core": "7.12.10",
"#babel/polyfill": "^7.6.0",
"#babel/preset-env": "^7.12.11",
"#babel/register": "^7.12.10",
"#shopify/app-bridge-react": "^2.0.6",
"#shopify/app-bridge-utils": "^2.0.6",
"#shopify/dates": "^1.1.5",
"#shopify/koa-shopify-auth": "^5.0.3",
"#shopify/polaris": "^6.2.0",
"#szhsin/react-menu": "^3.0.1",
"apollo-boost": "^0.4.9",
"axios": "^0.26.0",
"cross-env": "^7.0.3",
"date-fns": "^2.28.0",
"dotenv": "^8.6.0",
"fs": "^0.0.1-security",
"graphql": "^14.5.8",
"isomorphic-fetch": "^3.0.0",
"jquery": "^3.6.0",
"koa": "^2.13.1",
"koa-router": "^10.0.0",
"koa-session": "^6.1.0",
"mdb-react-ui-kit": "^3.0.0",
"moment": "^2.29.1",
"mongodb": "^4.5.0",
"mongoose": "^6.2.9",
"next": "^12.0.8",
"next-env": "^1.1.1",
"node-fetch": "^2.6.1",
"path": "^0.12.7",
"react": "^17.0.2",
"react-apollo": "^3.1.3",
"react-beautiful-dnd": "^13.1.0",
"react-csv": "^2.2.2",
"react-data-table-component": "^7.4.7",
"react-datepicker": "^4.7.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^17.0.2",
"react-moment": "^1.1.1",
"react-router": "^6.2.2",
"react-router-dom": "^6.2.2",
"react-table": "^7.7.0",
"read-json-lines-sync": "^2.2.0",
"recharts": "^2.1.9",
"rsuite": "^5.7.1",
"styled-components": "^5.3.5",
"url": "^0.11.0",
"webpack": "^5.70.0"
},
"devDependencies": {
"#babel/plugin-transform-runtime": "^7.12.10",
"#babel/preset-stage-3": "^7.0.0",
"#wojtekmaj/enzyme-adapter-react-17": "^0.6.6",
"babel-jest": "26.6.3",
"babel-register": "^6.26.0",
"chai": "^4.3.6",
"chai-http": "^4.3.0",
"enzyme": "3.11.0",
"eslint": "^8.9.0",
"eslint-config-next": "^12.0.10",
"husky": "^4.3.6",
"jest": "26.6.3",
"lint-staged": "^10.5.4",
"mocha": "^10.0.0",
"nodemon": "^2.0.7",
"prettier": "2.2.1",
"react-addons-test-utils": "15.6.2",
"react-test-renderer": "16.14.0",
"supertest": "^6.2.4"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,css,json,md}": [
"prettier --write"
]
}
}
I'm moving from Express to Fastify and stuck on starting server with graphql playground endpoint that should be localhost:*/graphql
server.applyMiddleware({ app, path: '/graphql' }) is an express but googled around i needed to use server.register(server.createHandler({ app, path: '/graphql' })) but then i get error root plugin has already booted
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'
import { ApolloGateway } from '#apollo/gateway'
import { ApolloServer } from 'apollo-server-fastify'
import Fastify from 'fastify'
const app = Fastify()
const serviceList = [
{
name: 'Service One',
url: 'http://localhost:8081/service_one/graphql',
},
{
name: 'Service Two',
url: 'http://localhost:8081/service_two/graphql',
},
]
app.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
const gatewayLoad = async () => {
const gateway = new ApolloGateway({
serviceList,
// debug: true,
})
const server = new ApolloServer({
gateway,
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
// debug: true,
})
await server.start()
// Express `applyMiddleware`, how to make fastify?
server.applyMiddleware({ app, path: '/graphql' })
}
gatewayLoad()
app.listen(8080, (err, address) => {
console.info(`Server is listening on port ${address}`)
})
package.json
"dependencies": {
"#apollo/gateway": "2.0.0-alpha.2",
"apollo-server": "^3.6.1",
"apollo-server-fastify": "^3.6.1",
"axios": "^0.24.0",
"dotenv": "^11.0.0",
"fastify": "^3.25.3",
"graphql": "16.2.0",
"pino-http": "^6.5.0",
"pino-pretty": "^7.3.0"
},
"devDependencies": {
"#types/node": "^17.0.8",
"#types/pino": "^7.0.5",
"#typescript-eslint/eslint-plugin": "^5.9.1",
"#typescript-eslint/parser": "^5.9.1",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.1",
"ts-node-dev": "^1.1.8",
"typescript": "^4.5.4"
}
I have created a sample project in reactjs and using node for backend. I have build react project with webpack. Now I want to render my react app from the server-side but got stuck with an error:
const express = require('express');
const path = require('path');
const logger = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const { renderToString } = require("react-dom/server");
const { StaticRouter } = require("react-router-dom")
const React = require("react");
const App = require('./src/App')
const session = require('express-session');
const routes = require('./server/index');
const port = process.env.PORT || 3000;
const app = express();
app.use(express.static(path.join(__dirname, 'dist')));
app.use(cors());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
//app.use(cookieParser());
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET,POST');
// Request headers you wish to allow
res.setHeader(
'Access-Control-Allow-Headers',
'X-Requested-With,content-type',
);
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
// end
app.use('/', routes);
app.use((req, res, next) => {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
if (app.get('env') === 'development') {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
message: err.message,
error: err,
});
});
}
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
message: err.message,
error: {},
});
});
router.get("*", (req, res) => {
// Render the component to a string.
const html = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
const finalDocument =`<html>
<head>
<title>Your App Title</title>
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`;
res.send(finalDocument);
});
app.listen(port, () => {
console.log('Api Server running on port' + port);
});
module.exports = app;
package.json
{
"name": "ssrnodereact",
"version": "0.1.0",
"private": true,
"dependencies": {
"#material-ui/core": "^4.12.2",
"#material-ui/icons": "^4.11.2",
"#testing-library/jest-dom": "^5.14.1",
"#testing-library/react": "^11.2.7",
"#testing-library/user-event": "^12.8.3",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"cryptr": "^6.0.2",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"express-session": "^1.17.2",
"formik": "^2.2.9",
"helmet": "^4.6.0",
"morgan": "^1.10.0",
"nodemailer": "^6.6.3",
"path": "^0.12.7",
"pg": "^8.7.1",
"prettier": "^2.3.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-ga": "^3.3.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"typescript": "^4.3.5",
"web-vitals": "^1.1.2",
"yup": "^0.32.9"
},
"scripts": {
"build": "webpack --mode production",
"start": "npm run build && nodemon server.js",
"test": "react-scripts test",
"eject": "react-scripts eject",
"format": "prettier --write ./**/*.{js,jsx,ts,tsx,css,md,json,html} --config ./.prettierrc"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#babel/core": "^7.15.0",
"#babel/preset-env": "^7.15.0",
"#babel/preset-react": "^7.14.5",
"babel-loader": "^8.2.2",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^3.2.0",
"react-hot-loader": "^4.13.0",
"ts-loader": "^8.3.0",
"webpack": "^4.44.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
}
}
Got following error:
error
I have tried the following solutions but have not been able to resolve the error:
React Serverside rendering Unexpected token, JSX and Babel
I'm not sure if React is a framework that suits your needs. Server-side rendering can be accomplished much easier and more efficiently using a framework like NextJS.
You can check this plugin and it has examples for you to do the SSR easily.
I'm trying to set up webpack-hot-middleware on my node api
but when I made a change on welcome.controller.ts, the console shows the build process but I can't see the changes, what I'm missing?, I'm using typescript and webpack, here is my setup:
Webpack.config.js
var fs = require('fs');
var webpack = require('webpack');
var path = require("path");
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function (x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function (mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
module.exports = {
entry: [
'webpack-hot-middleware/client?
path=http://localhost:3000/__webpack_hmr&reload=true',
'./src/server'
],
output: {
path: __dirname + '/dist',
filename: 'server.js',
publicPath: '/'
},
devServer: {
contentBase: './src'
},
node: {
__dirname: false,
__filename: false
},
resolve: {
// Add '.ts' and '.tsx' as a resolvable extension.
extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
},
devtool: '#source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
module: {
loaders: [
// All files with a '.ts' or '.tsx'
// extension will be handled by 'ts-loader'
{
test: /\.tsx?$/,
loader: 'ts-loader',
},
],
},
target: 'node',
externals: nodeModules
};
server.ts
import * as express from 'express';
import * as webpack from 'webpack';
import * as webpackDevMiddleware from 'webpack-dev-middleware';
import * as webpackHotMiddleware from 'webpack-hot-middleware';
import * as dotenv from 'dotenv';
import * as bodyParser from 'body-parser';
import * as config from '../webpack.config';
import * as welcomeController from './controllers/welcome.controller';
import * as path from 'path'
dotenv.config();
const compiler = webpack(config);
const app = express();
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.path,
stats: {colors: true},
noInfo: true
}));
app.use(webpackHotMiddleware(compiler, {
log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000
}));
app.set('port', process.env.PORT || 3000);
app.use(bodyParser.json());
app.use('/', express.static(path.join(__dirname, "/dist")));
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', welcomeController.index);
app.listen(app.get('port'), () => {
console.log(('App is running at http://localhost:%d in %s mode'),
app.get('port'), app.get('env'));
console.log('Press CTRL-C to stop\n');
});
if (module.hot) {
module.hot.accept('./server', () => {
console.log('si')
});
}
module.exports = app;
welcome.controller.ts
import { Request, Response } from 'express';
let pkg = require(__dirname + '/../../package.json');
export let index = (req: Request, res: Response) => {
res.json({
message: 'Welcome to API sekeleton.',
version: pkg.version,
});
}
package.json
{
"name": "projectq",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "npm run build && node ./dist/server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"dotenv": "^5.0.0",
"express": "^4.16.2",
"ts-loader": "^3.5.0",
"typescript": "^2.7.1",
"typings": "^2.1.1",
"webpack": "^3.11.0",
"webpack-dev-middleware": "^2.0.5",
"webpack-dev-server": "^2.11.1",
"webpack-hot-middleware": "^2.21.0"
}
}