React Router, pushState with an Express Server - express

quick question regarding using React-Router. I'm having trouble getting my server to handle pushState (if this is the correct term). Originally, I was using a module called connect-history-api-fallback, which was a middleware that enabled me to only server up static files form my dist directory. Visiting the client www.example.com obviously worked and I could navigate throughout the site, additionally, refreshing at any route like www.example.com/about - could also work.
However, I recently added one simple API endpoint on my Express server for the React app/client to ping. The problem now is that while I can get the initial page load to work (and thus the /api/news call to work, to fetch data from a remote service), I can no longer do a refresh on any other routes. For example, now going to www.example.com/about will result in a failed GET request for /about. How can I remediate this? Really appreciate the help! PS - not sure if it matters, but I'm considering implementing Server Side Rendering later on.
import express from 'express';
import historyApiFallback from 'connect-history-api-fallback';
import config from '../config';
import chalk from 'chalk';
import fetch from 'node-fetch';
import path from 'path';
const app = express();
// FIXME: Unsure whether or not this can be used.
// app.use(historyApiFallback({
// verbose : true
// }));
//// DEVELOPMENT MODE ONLY - USING EXPRESS + HMR ////
/* Enable webpack middleware for hot module reloading */
if (config.get('globals').__DEV__) {
const webpack = require('webpack');
const webpackConfig = require('../build/webpack/development_hot');
const compiler = webpack(webpackConfig);
app.use(require('./middleware/webpack-dev')({
compiler,
publicPath : webpackConfig.output.publicPath
}));
app.use(require('./middleware/webpack-hmr')({ compiler }));
}
//// PRODUCTION MODE ONLY - EXPRESS SERVER /////
if (config.get('globals').__PROD__) {
app.use(express.static(__dirname + '/dist'));
}
//// API ENDPOINTS FOR ALL ENV ////
app.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
// Wildcard route set up to capture other requests (currently getting undexpected token '<' error in console)
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, '../dist', 'index.html'));
});
export default app;

Express works by implementing a series of middleware that you "plug in" in order via .use. The cool thing is your routes are also just middlware — so you can separate them out, have them before your history fallback, and then only requests that make it past your routes (e.g., didn't match any routes) will hit the fallback.
Try something like the following:
const app = express();
// ...
var routes = exprss.Router();
routes.get('/api/news', function (req, res) {
fetch('http://app-service:5000/news')
.then( response => response.json() )
.then( data => res.send(data) )
.catch( () => res.sendStatus(404) );
});
app.use(routes);
app.use(historyApiFallback({
verbose : true
}));

Related

Express multer - disallow file uploads except for specific routes?

Well currently I am disallowing all file uploads to routes by setting up the server like:
const upload = multer();
const server = express();
module.exports = () => {
// ...
server.use(logger('dev'));
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.use(express.raw());
server.use(cookieParser());
server.use(express.static(path.join(projectRoot, 'public')));
server.set('trust proxy', 1);
server.use(upload.none());
server.use('/', router);
// ...
}
Which correctly blocks all files. Now I wish to allow uploading files only in the POST request to /test:
import * as express from "express";
import multer from "multer";
const upload = multer({storage: multer.memoryStorage()});
const router = express.Router();
router.post('/test', upload.single('pdf'), function(req, res, next) {
const r = 'respond with a test - POST';
res.send(r);
});
However when I try to use this in postman I get the error "multerError", "LIMIT_UNEXPECTED_FILE" for the field 'pdf'. I notice that if I remove the line server.use(multer.none()) it works, but then I can upload files to any place anyways, not exactly what I like?
Nothing will be uploaded to your server unless you specify a multer middleware on the entire server, on a route, or on a particular path. So you can safely remove the server.use(upload.none());.
The middleware will then not try to consume the payload of the incoming request. How much load the receiving (without consumption) of the payload causes on the server, I don't know, but you could theoretically destroy the connection whenever the client tries to submit a payload:
req.on("data", function() {
req.destroy();
});
But perhaps the creation of new connection afterwards causes more load on the server overall.

Handling endpoints APIs on client side instead of serverMiddleware in Nuxt

I'm on Nuxt 2.15.8 and trying to build an offline app with electron.js and prisma+sqlite for local DB.
In nuxt to hit a local endpoint there is a common way of using serverMiddleware and express like this:
// api.js that will be added to nuxt.config.js file as serverMiddleware
import express from 'express'
const app = express()
app.use(express.json())
export default {
path: '/api',
handler: app
}
which send endpoints beginning with api/ through app handler which I can use to access my BD (the common way to access sqlite3 DB is the same)
// added to api.js
import { PrismaClient } from '../../resources/prisma/client'
const prisma = new PrismaClient()
app.get(`/user/info`, async (req, res) => {
const result = await prisma.user.findUnique({
where: {
id: 1,
},
})
console.console.log(res);
res.json(result)
})
this will work fine on nuxt, also fine on nuxt-electron dev mode. but on built exe file serverMiddleware won't be called. So as it has be done by others (nuxt-electron accessing offline local DB) there must be a way to define endpoints on client side. any idea??
Updated:
as I changed my Nuxt-Electron boilerplate I could access serverMiddleware in exe file but it wont hit the endpoints yet!

Express middleware, changes to request object do not persist

I am trying to implement some middleware in Express that should be called for all routes. This middleware should alter the request object.
I've tried several things already but seem to keep having the same issue. Soon as the middleware is left it looks like the request object is changed back to it's original state.
Currently my code resembles (I simplified it with a minimalistic example):
route.js:
const express = require('express');
const router = express.Router();
router.get('/getMe', (req, res) => {
// return the desired data.
// I expect req.params.myString to exist here but it does not.
});
module.exports = router;
index.js:
const express = require('express');
const router = express.Router();
router.use('/', require('./route'));
module.exports = router;
app.js:
const express = require('express');
const app = express();
const routes = require('./index');
app.use((req, res, next) => {
// Adding req.params.myString to the request object.
if (req.params.myString === undefined) req.params.myString = 'hello world';
next();
});
app.use('/api', routes);
As you can see I left out some of the code to keep it more readable. This is the code that gets the response and sets up the server.
Again, I am expecting that req.params.myString becomes available in the endpoint. Does anyone see what I am doing wrong?
In express docs ( http://expressjs.com/en/api.html#req.params ) it says:
If you need to make changes to a key in req.params, use the app.param
handler. Changes are applicable only to parameters already defined in
the route path.
So you need to check app.param handler.
http://expressjs.com/en/4x/api.html#app.param
You should app.set("myString", "hello World") inside your app.js and then you can access the field in your route.js/index.js scripts by using req.app.get("myString"). Or this should work too, set it like app.myString = "Hello world" and access it like req.app.myString.

Hooking up React Native to back-end (with Express)

I made a UI with React Native, as well as a Cheerio.js scraper (with Cron Job to activate it once every day) I'll use to grab certain data from the web, so it can render in the UI. However, I have no idea how to link the two of them.
I am pretty sure I can do this with Express (which I am most comfortable with for the back-end), but can someone tell me exactly what I need to do to connect my front-end to a back-end?
Just in case, I am a junior dev (better on the front-end than the back-end) so please keep your answers simple. Even if your answers are more conceptual, rather than code-based, I'd really appreciate it.
API
I'm quite happy with GraphQL as an alternative to REST. However, there are many ways to connect through an api. Your client needs the link to where your server is running, and your server needs to enable that.
Tutorials
I think I couldn't explain it better than this tutorial (with example on Github): https://medium.com/react-native-training/react-native-with-apollo-server-and-client-part-1-efb7d15d2361
https://medium.com/react-native-training/react-native-with-apollo-part-2-apollo-client-8b4ad4915cf5
And following Stephen Grider's tutorial on Udemy for deeper understanding of GraphQL. He is using React and not React Native in his tutorial but the syntax remains very close.
https://www.udemy.com/graphql-with-react-course/learn/v4/overview
Important notice - The first tutorials use "apollo-server" while udemy's tutorial uses graphql. apollo-server changes quite often and graphql may be clearer.
Example
Here's how my bridge between the two looks like. The biggest difficulty was dealing with Cors for the front-end version of the app (Next.js) and finding out that the server can be accessed on http://10.0.3.2:8080/graphql (may vary) instead of localhost:8080.
My index.android.js (client side):
import React from 'react'
import { AppRegistry } from 'react-native'
import App from './app'
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { ApolloProvider } from 'react-apollo'
const Client = () => {
const networkInterface = createNetworkInterface({
uri: 'http://10.0.3.2:8080/graphql'
})
const client = new ApolloClient({
networkInterface
});
return (
<ApolloProvider client={client}>
<App />
</ApolloProvider>)
}
AppRegistry.registerComponent('apolloclient', () => Client);
My app.js server side
const express = require('express');
// const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const chalk = require('chalk');
// New imports
// NEVER FORGET to require the models,
// in which schemas are registered for a certain model
// forgetting it would throw "Schema hasn't been registered for model..."
const models = require('./models');
const expressGraphQL = require('express-graphql');
const schema = require('./schema/schema');
const app = express();
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// My mongoLab URI
const MONGO_URI = 'mongodb://xxx:xxx#xxx.mlab.com:xxx/xxx';
// mongoose's built in promise library is deprecated, replace it with ES2015 Promise
mongoose.Promise = global.Promise;
// Connect to the mongoDB instance and log a message
// on success or failure
mongoose.connect(MONGO_URI);
mongoose.connection.once('open', () => console.log(`${chalk.blue(`🗲 Connected to MongoLab instance 🗲`)}`));
mongoose.connection.on('error', error => console.log(`${chalk.yellow(`⚠ Error connecting to MongoLab: ` + error + ` ⚠`)}`));
app.use(cors());
// We pass the schema as an option to our expressGraphQL middleware
app.use('/graphql', expressGraphQL({
schema,
graphiql: true
}))
module.exports = app;
my index.js (server side):
const app = require('./app');
const chalk = require('chalk');
const PORT = 8080;
app.listen(PORT, () => {
console.log(`${chalk.green(`✔ Server started on http://localhost:${PORT} ✔`)}`);
});
Assuming you're communicating with an API built with Express then use fetch as described in the docs: https://facebook.github.io/react-native/docs/network.html

Why is router.post(..) not see and router.get(...) works fine?

I have a large express app that I have moved to node 7 and transforming the front end using Angular 2.
All seems to be working, except.... the router.post(...) calls.
All the router.get(...) routes work correctly. BUT the router.post(...) routes all respond with 404's.
A sample of the routing is as follows:
<routes.js>
'use strict';
import errors from './components/errors';
import path from 'path';
export default function(app) {
// Insert routes below
app.use('/api/logins', require('./api/login'));
app.use('/api/users', require('./api/user'));
app.use('/api/version', require('./api/version'));
...
app.use('/auth', require('./auth'));
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get((req, res) => {
res.sendFile(path.resolve(app.get('appPath') + '/index.html'));
});
}
and for example for
'use strict';
let express = require('express');
let router = express.Router();
let controller = require('./user.controller');
let auth = require('../../auth/auth.service');
router.post('/', auth.hasRole('admin'), controller.create);
router.get('/', auth.hasRole('admin'), controller.index);
router.get('/clean', auth.hasRole('admin'), controller.cleanDatabase);
router.get('/user/:sharingFromUserId', auth.isAuthenticated(), controller.getUserSharingFromUser);
....
Yet when I do post I see:
POST http://localhost:9000/users/ 404 (Not Found)
One strange hint in Intellij, suggests that the route.post() is not the correct function???? In the editor, the IDE seems to imply that this is the AuthHttp.post() function which is being used in the webpack section of the frontend. How can this be???