Custom 404 page using Angular and Express - node.js

I am using a MEAN stack and I am trying to build a custom 404 page for my project using Angular.
In Angular app.module.ts I have written this:
const appRoutes: Routes = [
{
path: 'students',
component: StudentComponent,
data: {title: 'Student List'}
},
{
path: 'student-details/:id',
component: StudentDetailComponent,
data: {title: 'Student Details'}
},
{
path: 'student-create',
component: StudentCreateComponent,
data: {title: 'Create a new Student'}
},
{
path: 'student-edit/:id',
component: StudentEditComponent,
data: {title: 'Edit Student'}
},
{
path: '',
redirectTo: '/students',
pathMatch: 'full'
},
{
path: '**',
component: PageNotFoundComponent,
data: {title: 'Page Not Found'}
}
];
#NgModule({
declarations: [
AppComponent,
StudentComponent,
StudentDetailComponent,
StudentCreateComponent,
StudentEditComponent,
PageNotFoundComponent,
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot(
appRoutes,
{enableTracing: true}
)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
But in fact I think that when I write an URL that doesn't exist Express make a response, not Angular. I know that Express is answering because, when I removed this code from server.js:
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
When I write an url that doesn't exist, I get the answer "Cannot GET /urlThatDoesntExist".
The matter is that the other routing urls set in Angular are working but the 404 page is not.
I am using Express to serve the Angular app using:
app.use('/', express.static(path.join(__dirname, 'dist')));
How can I set the Angular 404 page working above Express? Thanks!

If you want every request to reach angular (so you can handle 404s on the client side) you can use an express route like this:
// all routes lead to to index.html
const router = express.Router();
router.get('*', (req, res) => {
res.sendFile(path.join(DIR, 'index.html'));
});
this.express.use('*', router);

Related

504 Error during the Proxying of Angular with NodeJS inside it

Currently we are trying to run the nodejs server in angular application(net core -angular -spa).We went through this source-
https://www.simplilearn.com/tutorials/angular-tutorial/what-is-angular-node
As per the project we went on creating the angular application and set the proxy based upon the link : https://angular.io/guide/build#proxy-multiple-entries
The Step we are following this:
Step 1: Creation Node Server, which is working individually.
The 504 error shows here.
var express = require('express');
var app = express();
app.use(express.static("myApp")); // myApp will be the same folder name.
// PORT
const port = process.env.PORT || 8000
app.get('/', (req,res) => {
res.send('App Works !');
});
app.get('/externalapi/get', (req, res) => {
console.log(req);
console.log('App Works !');
res.send('Got Files!');
return "Got Files";
});
app.listen(port, () => {
console.log(`Server listening on the port no.:${port}`);
});
Step 2 : Proxy.config.js (not json)
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
},
{
context :[
"/externalapi/*",
],
target : "http://localhost:8000",
secure: false,
changeOrigin: true,
}
]
module.exports = PROXY_CONFIG;
Step 3: Creation of the Services
export class ExampleServices{
_nodeUrl : any = '/externalapi/';
constructor( private httpClient: HttpClient){
}
mergeExamples(files:any) : Observable<any> {
console.log(this._nodeUrl);
return this.httpClient.get(this._nodeUrl+"get", files);
}
}
There are two from us ->
Why 504 Error occurs in this code ?Is it due to https-> http calling?
Is there any possibility to run the nodejs command from parent folder itself(currently the externalapi is inside the angular app, have its own node_modules)
Kindly help us with relevant answer and supporting documents.

React SSR express,webpack,babel babel doesn't like the CSS in my components

I have been trying to learn how to SSR with React(without using Next.js) I've gotten to the point that I'm seeing the HTML rendered on the page but once I add the css imports to style the component babel start throwing errors (
C:\Users\Frozen\Desktop\ssr stuff\ssr4\src\App.css:1
body {
^
SyntaxError: Unexpected token '{'
)
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"],
}
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './build'),
filename: "bundle.js",
publicPath: "/"
},
devServer: {
port: 3010,
static: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env', "#babel/preset-react"]
}
},
},
{
test: /\.(css|scss)$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
publicPath: "/"
})
]
};
server.js
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './src/App.js';
import path from 'path';
import fs from 'fs';
const app = express();
app.get('/', (req, res) => {
console.log('in /');
fs.readFile(
path.resolve('./build/index.html'),
'utf8',
(err, data) => {
if (err) {
console.log(err);
return res.status(500).send('Internal Server Error');
}
const html = data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
console.log(html)
return res.send(html);
}
);
});
app.use(express.static(path.resolve(__dirname,'build')));
app.listen(3000, () => {
console.log('server listening on port 3000')
})
I also fail to understand why do I have to put the app.use(static) below the app.get('/') aren't middlewares supposed to go one after another in order top to bottom (if I move the app.use(static) above the app.get('/') where I return the component I don't see the console.log('in /');
index.js
import React from "react";
import ReactDOM from 'react-dom'
import App from "./App";
const appElement = document.getElementById('root');
ReactDOM.hydrate(<App/>, appElement)
I run
npx webpack
npx babel-node server.js
And I get the css error (if I remove the css import I don't get it but I don't have css in the component)
Also is there a way to rebuild and run server.js once I change something ( like when you use create-react-app ) Also any other suggestions on what I can improve in the current setup will be much appreciated.
Install babel-plugin-css-modules-transform npm install --save-dev babel-plugin-css-modules-transform.Then include it in .babelrc "plugins": ["css-modules-transform"]

Why I can't run VueJS with Express backend with hot reload?

I'm trying to use Express as a backend running a VueJS web application with hot reload, but I can't FETCH the content from the server.
vue.config.js:
module.exports = {
devServer: {
proxy: 'http://localhost:3000'
}
}
server.js:
const express = require('express');
const app = express();
const port = 3000
app.get('/hello', (req, res) => {
res.send({ "message": "Hello World" }) //Content
});
app.listen(port, () => {
console.log(`WebServer listening at port`);
});
That is running and /hello is working at port 3000.
Now, I'm starting both this way:
npm run server & nodemon server.js
Trying to fetch /hello in the Vue application, but it's not working. Am I missing anything?
<template>
<div class="flex-col">{{tasks}}
</div>
</template>
<script>
export default {
name:"ListToDo",
data(){
return{
tasks: []
}
},
methods:{
FETCH: function(){
fetch("/tasks/")
.then(res => res.json())
.then(data => this.tasks=data)
}
},
mount(){
this.FETCH()
}
}
The front-end is fetching /tasks, but the server does not have a route for /tasks, so it will respond with 404 Not Found.
One solution is to add a route for /tasks to your server's Express instance:
app.get('/tasks:myOptions(/*)?', (req, res) => {
res.send({ message: 'tasks', myOptions: req.params.myOptions })
})
Or you can update your component to use the /hello route already setup in the server:
export default {
methods: {
FETCH: function() {
fetch("/hello") 👈
.then(res => res.json())
.then(data => this.tasks=data)
}
}
}
}
If you prefer to keep your original /hello route while using /tasks from the client, the path will need to be rewritten client-side through the proxy, using the pathRewrite config shown below. However, this can't be done with the simple string proxy config, and specific route contexts (i.e., /tasks) must be specified:
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/tasks': {
target: 'http://localhost:3000',
pathRewrite: { '^/tasks' : '/hello' }
}
}
}
}

How to access swagger api docs from one project to another project in nodejs and express?

Swagger Implementation:
const express = require('express');
const router = express.Router();
const swaggerUi = require('swagger-ui-express');
const swaggerJSDoc = require('swagger-jsdoc');
module.exports = async (app) => {
router.use('/', swaggerUi.serve, swaggerUi.setup(
swaggerJSDoc({
swaggerDefinition: { ...require('../swagger.json') },
apis: ['./app/**/*.js']
}),
{
swaggerOptions: {
displayRequestDuration: true,
docExpansion: "none",
filter: false,
showExtensions: true,
showCommonExtensions: true,
displayOperationId: true
}
}
));
app.use('/api-docs', router); // SET SWAGGER DOCS
}
I have 2 nodejs-express projects and implemented swagger as per above code:
First Project running in http:localhost:3001
Second project running in http:localhost:3002
I have third project only for swagger docs, that is running in http:localhost:3000, I want to access both (1), (2) of the projects docs in this third project using explorer for only swagger api docs, but i can not able to access it direct by url of project,
router.use('/', swaggerUi.serve, swaggerUi.setup(
swaggerJSDoc({
swaggerDefinition: { ...require('../swagger.json') }
}),
{
explorer: true,
swaggerOptions: {
displayRequestDuration: true,
docExpansion: "none",
filter: false,
showExtensions: true,
showCommonExtensions: true,
displayOperationId: true,
urls: [
{
url: 'http://localhost:3001',
// url: 'http://localhost:3001/api-docs', // also tried but not working
name: 'Project 1'
},
{
url: 'http://localhost:3002',
// url: 'http://localhost:3002/api-docs', // also tried but not working
name: 'Project 2'
}
]
}
}
));
app.use('/api-docs', router); // SET SWAGGER DOCS
It says failed to load api definition.
Am i doing wrong way or is there any other npm i have to implement for this?
I just learned about to set route in app.get method and send response as swaggerJSDoc, got little help from this question
const definition = swaggerJSDoc({
swaggerDefinition: { ...require('../swagger.json') },
apis: ['./app/**/*.js']
});
app.get('/api-docs.json', function (req, res) {
res.header("Content-Type", "application/json");
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.send(definition);
});
Now i am able to access both projects docs using "http://localhost:3001/api-docs.json" and "http://localhost:3002/api-docs.json"

Angular2 Router and Express Integration

I'm having a problem using the Angular2 router and express, but none of the previous questions seem to have the solution I need. I'm using the latest version of the angular2-cli.
When I direct my browser to localhost:3000, I get the generic "app works" message that the angular2-cli generates. My problem arises when I try to navigate to one of my child routes, such as localhost:3000/auth/login - it redirects me back to the same "app works" page, instead of showing me a page that says "login works".
Looking at other questions on stack exchange, I figure that the issue is here:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
When I replace the * with a /, instead of redirecting me back to my target page, it gives me the cannot GET error.
In summary, my problem is that url navigation of my angular2 routes integrated into my express app is not working.
My express code in server.js is as follows:
var express = require("express");
var app = express();
var bodyParser = require("body-parser");
var mongoose = require("mongoose");
var path = require("path");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static(path.join(__dirname, 'dist')));
// Catch all other routes and return the index file
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
app.listen(3000, () => {
console.log("Server has started");
});
My application's routing module is as follows (app-routing.module.ts):
import {Route, RouterModule} from '#angular/router';
import {NgModule} from '#angular/core';
import {LoginComponent} from './login/login.component';
import {RegisterComponent} from './register/register.component';
import {FeedComponent} from './feed/feed.component';
import {FindComponent} from './find/find.component';
import {SubmitComponent} from './submit/submit.component';
import {ProfileComponent} from './profile/profile.component';
export const routeConfig = [
{
path: "auth",
children: [
{
path: "login",
component: LoginComponent
},
{
path: "register",
component: RegisterComponent
},
{
path: "",
redirectTo: "/login",
pathMatch: "full"
}
]
},
{
path: "app",
children: [
{
path: "feed",
component: FeedComponent
},
{
path: "find",
component: FindComponent
},
{
path: "submit",
component: SubmitComponent
},
{
path: "profile",
component: ProfileComponent
},
{
path: "",
redirectTo: "/feed",
pathMatch: "full"
}
]
}
];
#NgModule({
imports: [RouterModule.forRoot(routeConfig)],
exports: [RouterModule]
})
export class AppRoutingModule{
}
My application module is as follows:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import {AppRoutingModule} from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { FeedComponent } from './feed/feed.component';
import { FindComponent } from './find/find.component';
import { SubmitComponent } from './submit/submit.component';
import { ProfileComponent } from './profile/profile.component';
#NgModule({
declarations: [
AppComponent,
LoginComponent,
RegisterComponent,
FeedComponent,
FindComponent,
SubmitComponent,
ProfileComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
All the components have been defined in their respective files and are the barebones default templates generated by the angular2-cli.
I have no idea what to do at this point. I've even tried restarting and rewriting this project multiple times to see if I went wrong somewhere (this is my 3rd attempt).
For angular2 and nodejs integration you can either put this in server.js:
var cors = required('cors');
app.use(cors());
or you can make use of proxy in your web server. For example in apache2 inside virtualhost tag you can do
ProxyPass /api/ http://localhost:3000/
ProxyReverse /api/ http://localhost:3000/
Now instead of using "http://localhost:3000/" in your http request function (although you are not using it) you can use just "api/".

Resources