Description of the problem:
I am developing my Nodejs backend with Typescript Mongo and Express, I implemented the
and I declare the routes and they don't work apparently the router is not working
routes = (controllers: IController[]) => {
controllers.forEach((controller: IController) => {
this.app.use('/api/', controller.router);
});
};
I put log in the method that declares the routes and if it enters, I also put logs in the for and this also enters.
controllers.forEach((controller: IController) => {
console.log('inside');
this.app.use('/api/', controller.router);
});
I also tried putting manually my route and it worked:
routes = (controllers: IController[]) => {
this.app.use('/api/',(req,res)=>{
console.log('inside');
});
};
my interface IController:
export interface IController{
path:string;
router:Router;
initRoutes():void;
}
I solved this by specifying again that it is a Router type of express and at this moment all the routes worked:
routes = (controllers: IController[]) => {
controllers.forEach((controller: IController) => {
const routeTemp :Router = controller.router;
this.app.use('/api/', controller.router);
});
};
Related
Can anyone tell me how to implement GraphQL Subscriptions using Express-GraphQL in Node?
I have also run into the same problem. I wasn't able to find a clear solution to this in the documentation. So i have just switched to graphql-yoga instead. But i did find this thread so do check it out
I've been researching this same issue.
I've read the GitHub issues for express-graphql subscriptions and a member of that repo suggested using graphql-ws on the closing comment.
Here's a link to my GitHub project shammelburg/express-graphql-api, you can npm start load grapiql to test queries and mutation.
To test subscriptions, I've created an Angular project which implements graphql-ws's observables example. shammelburg/graphql-rxjs-angular
The Angular project also uses graphql-request for queries and mutations.
This is a very lightweight solution and works perfectly.
They've added the doc fragment mentioning Subscription Support with an example implementation in Nov 2020.
But unfortunately that never got released, there's an issue here mentioning that.
My workaround for now's been switching over to Express Playground for the subscriptions-transport-ws socket (Playground doesn't support graphql-ws yet) and Apollo Sandbox for the graphql-ws.
Then my subscription creation options are the following.
Where createScopedPermissionWrapper is just an execute wrapper with #graphql-authz and createGraphqlContext a factory function validating auth and creating a custom context for my resolvers.
import { Server } from 'http'
import { useServer } from 'graphql-ws/lib/use/ws' // subscription with graphql-ws
import { SubscriptionServer } from 'subscriptions-transport-ws' // subscription with subscriptions-transport-ws
export const createSubscriptionsTransportWs = (server: Server) => {
const wsServer = new SubscriptionServer(
{
schema,
execute: createScopedPermissionWrapper(),
subscribe,
onConnect: (args: { authentication?: string }) =>
createGraphqlContext({
authentication: args.authentication,
}),
},
{ server, path }
)
const wsAddress = wsServer.server.address() as AddressInfo
depClients.logger.success(
`Graphql subscription socket up on ${wsAddress.address}:${wsAddress.port}${path}`
)
}
export const createGraphqlWS = (server: Server) => {
const wsServer = new ws.Server({ server, path })
useServer(
{
schema,
execute: createScopedPermissionWrapper(),
subscribe,
context: (args: { connectionParams: { authentication?: string } }) =>
createGraphqlContext({
authentication: args.connectionParams.authentication,
}),
},
wsServer
)
const wsAddress = wsServer.address() as AddressInfo
depClients.logger.success(
`Graphql subscription socket up on ${wsAddress.address}:${wsAddress.port}${path}`
)
}
See Authentication and Express Middleware
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
ip: String
}
`);
const loggingMiddleware = (req, res, next) => {
console.log('ip:', req.ip);
next();
}
var root = {
ip: function (args, request) {
return request.ip;
}
};
var app = express();
app.use(loggingMiddleware);
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
I've created a Node(express)/React app that uses socket.io and Redux's store as follows:
import io from "socket.io-client";
import * as types from "../actions/types";
import { cancelReview, startReview } from "./actions";
const socket = io("http://localhost:8080", {
transports: ["websocket"]
});
export const init = store => {
socket.on("connect", () => {
console.log("websocket connection successful...");
socket.on("cancelReview", (id, name) => {
cancelReview(store, id, name);
});
socket.on("startReview", (id, name) => {
startReview(store, id, name);
});
});
};
This function is then called from store.js as follows:
import { createStore, applyMiddleware } from "redux";
import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
import { init } from "./socket/socket";
// Initial state
const initialState = {};
// Middleware
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
init(store);
export default store;
Everything works fine on my local machine, but I'm now realizing after doing some research that this will not work on Google's app engine because instead of http://localhost:8080 I need to get the actual IP address from Google's metadata server and pass in EXTERNAL_IP + ":65080". So I'm able to get the external IP in my express app as follows:
const METADATA_NETWORK_INTERFACE_URL =
"http://metadata/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip";
function getExternalIp(cb) {
const request = axios.create({
baseURL: METADATA_NETWORK_INTERFACE_URL,
headers: { "Metadata-Flavor": "Google" }
});
request
.get("/", (req, res) => {
return cb(res.data);
})
.catch(err => {
console.log("Error while talking to metadata server, assuming localhost");
return cb("localhost");
});
}
However, if I pass this value into my render function as seen below, React creates a prop to pass into components (as far as I understand from the info I could find):
app.get("*", (req, res) => {
getExternalIp(extIp => {
res.render(path.resolve(__dirname, "client", "build", "index.html"), {
externalIp: extIp
});
});
I am not able to access this value via the window global. So my question is, how do I access this external IP from my store initialization, since it is not an actual React component?
Thanks in advance.
I am using InfluxDB as database, I used influxdb-nodejs library module to create API to write data and Query data from Influxdb.
The Queryfunction.js API code is as follows:
module.exports = {
queryfunc: function() {
const Influx = require('influxdb-nodejs');
const client = new Influx('http://127.0.0.1:8086/mydb');
client.query('http')
.where('type', '2')
.then(console.info)
.catch(console.error);
}
}
I use a script.js file to call queryfunc() in Queryfunction.js API:
const myModule = require('./Queryfunction');
let val = myModule.queryfunc();
I use command node script to run the script file.
The result is an array
C:\Users\Admin\Desktop\reactApp\API>node script
{ results: [ { statement_id: 0, series: [Array] } ] }
I am using ReactJS to create front end UI components. How to fetch the resultant array data in ReactJS?
You're either need to write to a json file or you'll need to have an express wrapper around your db calls.
const express = require('express')
const app = express();
app.get('/api', (req, res, next) => {
const Influx = require('influxdb-nodejs');
const client = new Influx('http://127.0.0.1:8086/mydb');
client.query('http')
.where('type', '2')
.then(data => res.json(data)
.catch(err => next(err)); // error middleware to handle
}
app.listen('3000', () => console.log('running on http://localhost:3000'))
Within react you do a fetch:
Class App extends React.Component {
componentDidMount() {
fetch('http://localhost:3000')
.then(res => res.json())
.then(data => this.setState( {data} ) )
render() {
....
}
}
I am following the project on github and working through personal modifications
It could probably be answered with google/documentation, but I don't know the right keywords to pull it in yet. One day, soon.
This doesn't line up with the file naming conventions I have seen.
Heroes.js imports api.js. api.js talks to the express backend via a proxy setting at the /api/* route. Problem is that the api.js file never declares an "api" component.
How is it that api.js is exporting "heroService", yet when its being imported, its operating under "api"? Is it because the file is named api.js and it defaults to that as the component name?
Heroes.js
import api from '../api';
//Example function
handleDelete(event, hero) {
event.stopPropagation();
api.destroy(hero).then(() => {
let heroes = this.state.heroes;
heroes = heroes.filter(h => h !== hero);
this.setState({ heroes: heroes });
if (this.selectedHero === hero) {
this.setState({ selectedHero: null });
}
});
}
api.js
const baseAPI = '/api';
const heroService = {
//example function
destroy(hero) {
return new Promise((resolve, reject) => {
fetch(`${baseAPI}/hero/${hero.id}`, { method: 'DELETE' })
.then(response => response.json())
.then(json => resolve(json))
.catch(err => {
reject(err);
});
});
}
export default heroService;
api.js is exporting a default.
Since this is not a named export, the binding takes on the name that you specified upon import.
As you wrote import api from '../api';, you use api.destroy().
If you wrote import monkeys from '../api';, you would use monkeys.destroy() instead.
References:
https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
I have an API build using sailsjs and a react redux attach to a nodejs backend, and i am trying to implement socket.io for a realtime communication, how does this work?
is it
socket.io client on the react side that connects to a socket.io server on its nodejs backend that connects to a socket.io server on the API
socket.io client on the react side and on its nodejs backend that connects to a socket.io server on the API
i have tried looking around for some answers, but none seems to meet my requirements.
to try things out, i put the hello endpoint on my API, using the sailsjs realtime documentation, but when i do a sails lift i got this error Could not fetch session, since connecting socket has no cookie (is this a cross-origin socket?) i figure that i need to pass an auth code inside the request headers Authorization property.
Assuming i went for my #1 question, and by using redux-socket.io,
In my redux middleware i created a socketMiddleware
import createSocketIoMiddleware from 'redux-socket.io'
import io from 'socket.io-client'
import config from '../../../config'
const socket = io(config.host)
export default function socketMiddleware() {
return createSocketIoMiddleware(
socket,
() => next => (action) => {
const { nextAction, shuttle, ...rest } = action
if (!shuttle) {
return next(action)
}
const { socket_url: shuttleUrl = '' } = config
const apiParams = {
data: shuttle,
shuttleUrl,
}
const nextParams = {
...rest,
promise: api => api.post(apiParams),
nextAction,
}
return next(nextParams)
},
)
}
and in my redux store
import { createStore, applyMiddleware, compose } from 'redux'
import createSocketIoMiddleware from 'redux-socket.io'
...
import rootReducers from '../reducer'
import socketMiddleware from '../middleware/socketMiddleware'
import promiseMiddleware from '../middleware/promiseMiddleware'
...
import config from '../../../config'
export default function configStore(initialState) {
const socket = socketMiddleware()
...
const promise = promiseMiddleware(new ApiCall())
const middleware = [
applyMiddleware(socket),
...
applyMiddleware(promise),
]
if (config.env !== 'production') {
middleware.push(DevTools.instrument())
}
const createStoreWithMiddleware = compose(...middleware)
const store = createStoreWithMiddleware(createStore)(rootReducers, initialState)
...
return store
}
in my promiseMiddleware
export default function promiseMiddleware(api) {
return () => next => (action) => {
const { nextAction, promise, type, ...rest } = action
if (!promise) {
return next(action)
}
const [REQUEST, SUCCESS, FAILURE] = type
next({ ...rest, type: REQUEST })
function success(res) {
next({ ...rest, payload: res, type: SUCCESS })
if (nextAction) {
nextAction(res)
}
}
function error(err) {
next({ ...rest, payload: err, type: FAILURE })
if (nextAction) {
nextAction({}, err)
}
}
return promise(api)
.then(success, error)
.catch((err) => {
console.error('ERROR ON THE MIDDLEWARE: ', REQUEST, err) // eslint-disable-line no-console
next({ ...rest, payload: err, type: FAILURE })
})
}
}
my ApiCall
/* eslint-disable camelcase */
import superagent from 'superagent'
...
const methods = ['get', 'post', 'put', 'patch', 'del']
export default class ApiCall {
constructor() {
methods.forEach(method =>
this[method] = ({ params, data, shuttleUrl, savePath, mediaType, files } = {}) =>
new Promise((resolve, reject) => {
const request = superagent[method](shuttleUrl)
if (params) {
request.query(params)
}
...
if (data) {
request.send(data)
}
request.end((err, { body } = {}) => err ? reject(body || err) : resolve(body))
},
))
}
}
All this relation between the middlewares and the store works well on regular http api call. My question is, am i on the right path? if i am, then what should i write on this reactjs server part to communicate with the api socket? should i also use socket.io-client?
You need to add sails.io.js at your node server. Sails socket behavior it's quite tricky. Since, it's not using on method to listen the event.
Create sails endpoint which handle socket request. The documentation is here. The documentation is such a pain in the ass, but please bear with it.
On your node server. You can use it like
import socketIOClient from 'socket.io-client'
import sailsIOClient from 'sails.io.js'
const ioClient = sailsIOClient(socketIOClient)
ioClient.sails.url = "YOUR SOCKET SERVER URL"
ioClient.socket.get("SAILS ENDPOINT WHICH HANDLE SOCKET", function(data) {
console.log('Socket Data', data);
})