nimble-restivus package does not work in Meteor GraphQL - api

I setup GraphQL in Meteor server, and I created a simple API using nimble-restivus package. But I can not call to this API. Please help.
This is API setup:
var Api = new Restivus({
useDefaultAuth: true,
prettyJson: true,
});
Api.addRoute('status', {
post: function() {
return { status: 'OK' };
},
});
This is command line to test api:
curl -X POST http://localhost:3000/api/status
This is config GraphQL on Meteor server:
import { Meteor } from 'meteor/meteor';
import express from 'express';
import {
graphqlExpress,
graphiqlExpress,
} from 'graphql-server-express';
import bodyParser from 'body-parser';
import cors from 'cors';
import { execute, subscribe } from 'graphql';
import { createServer } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { schema } from './schema';
const server = express();
const whitelist = [ 'http://localhost:3000' ];
const corsOptions = {
origin(origin, callback){
const originIsWhitelisted = whitelist.indexOf(origin) !== -1;
callback(null, originIsWhitelisted);
},
credentials: true,
};
server.use(cors(corsOptions));
// server.use('*', cors({ origin: Meteor.settings.GraphQL.clientURL }));
server.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));
server.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: 'ws://localhost:3000/subscriptions',
}));
// We wrap the express server so that we can attach the WebSocket for subscriptions
const ws = createServer(server);
ws.listen(3000, () => {
console.log('GraphQL Server is running');
// Set up the WebSocket for handling GraphQL subscriptions
new SubscriptionServer({
execute,
subscribe,
schema
}, {
server: ws,
path: '/subscriptions',
});
});
This is the result after run command:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot POST /api/status</pre>
</body>
</html>

I fixed it. The reason is Meteor server and GraphQL are running in the same port (port 3000). We need to change port of GraphQL to other port (for example: 3002, etc)

Related

Resolving Keycloak Client Adapter Deprecation for Node.js Express Boilerplate using Passport with OpenID Connect Client

I had previously built a Keycloak Express boilerplate using the node client adapter (keycloak-connect). However, Keycloak has deprecated their client adapters for node, so I looked for a solution and found a suggestion to integrate Passport with the OpenID Client for Keycloak on this Stack Overflow question (NodeJS + Keycloak without express). I followed the instructions in this Medium article (https://medium.com/keycloak/keycloak-express-openid-client-fabea857f11f) to build my Keycloak Express boilerplate, but it didn't work for me. My register and login endpoints are functioning properly, but my protected route is not working as expected.Could anyone provide a template or guide on how to properly configure Passport.js? I have attached my current code for reference.
import express, {
Express,
Request,
Response
} from 'express';
import 'reflect-metadata';
import bodyparser from 'body-parser';
import userRouter from './api/routes/userRoutes';
import expressSession from 'express-session';
import cookieParser from 'cookie-parser'
import connection from './database/connection';
import cors from 'cors';
import { corsOptions } from './config/corsOptions';
import { credentials } from './api/middlewares/credentials';
import { Issuer, Strategy } from 'openid-client';
import passport from 'passport';
import config from './config';
import KeycloakStrategy from "#exlinc/keycloak-passport";
//GETTING PORT FROM .ENV FILE:
const PORT = config.port || 3000;
const app: Express = express();
var memoryStore = new expressSession.MemoryStore();
app.use(
expressSession({
secret: 'another_long_secret',
resave: false,
saveUninitialized: true,
store: memoryStore
})
);
app.use(passport.initialize());
app.use(passport.authenticate('session'));
Issuer.discover('http://localhost:8080/realms/Demo').then(function (oidcIssuer) {
var client = new oidcIssuer.Client({
client_id: 'keycloak_practice',
client_secret: 'hkvogjo7jziNV3X4hR2rUILQikNKHpSL',
redirect_uris: ['http://localhost:8000/private'],
response_types: ['code'],
})
passport.use('oidc', new Strategy({client}, (tokenSet, userinfo, done)=>{
return done(null, tokenSet.claims());
})
)
});
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.use(credentials)
app.use(cors(corsOptions));
app.use(express.json());
app.use((bodyparser.urlencoded({ extended: true })));
app.use(cookieParser());
//CONNECTING TO DATABASE:
connection();
app.use('/user', userRouter);
//CONNECTION TO PORT:
app.listen(PORT, () => {
console.log(`This application is listening on port ${PORT}`);
});
Here userRouter.ts
import express, { Router } from 'express';
import { loginUserController, logoutUserController, protectedRouteController, refreshTokenController, registerUserController } from '../controllers/userController';
import passport from 'passport';
const userRouter: Router = express.Router();
userRouter.post('/register', registerUserController);
userRouter.post('/login', loginUserController);
userRouter.get('/refreshToken', refreshTokenController);
userRouter.post('/logout', logoutUserController);
//protected route
userRouter.get('/private', passport.authenticate('oidc'),protectedRouteController);
// keycloak.protect()
export default userRouter;
Request for guidance on properly configuring Passport.js. Could anyone share a template or step-by-step guide on how to set up Passport.js for keycloak effectively?

Swagger UI not getting generated for serverless Express api

My code structure is as follows-
src
-handler.ts
-routes
--user.ts
serverless.yml
node_modules
..etc
Code in 'handler.ts' for is as follows-
import express, { Express, Request, Response, NextFunction } from "express";
import serverless from "serverless-http";
import userRouter from "./routes/user";
import cookieParser from "cookie-parser";
import swaggerUi = require('swagger-ui-express');
import swaggerJsDoc = require('swagger-jsdoc');
const app: Express = express();
app.use(express.json());
app.use(cookieParser());
const swaggerOptions = {
swaggerDefinition: {
info: {
version: "1.0.0",
title: "User service",
description: "user service APIs",
contact: {
name: "Sid"
}
}
},
apis: ["./routes/*.ts"]
};
const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/swagger',swaggerUi.serve, swaggerUi.setup(swaggerDocs,{
swaggerOptions: {
url: "api-docs"
},
}));
app.use('/', userRouter);
module.exports.handler = serverless(app);
After I deploy this lambda using the serverless, my APIs are working fine the swagger-ui URL is not working. Please help

Setup of swagger-ui-express with API Gateway

I am not able to figure out the correct setup of either my API Gateway or swagger-ui-express.
The lambda function runs successful and and return the html but the related resouces are not able to be loaded and get an 404 error.
const app = express();
const swaggerUI = require('swagger-ui-express');
const serverlessExpress = require('#vendia/serverless-express');
const Stage = process.env.Stage;
const apiId = process.env.apiId;
const options = {
customCss: '.swagger-ui .topbar { display: none }',
customCss: '.swagger-ui .topbar { background-color: red }'
}
let serverlessExpressInstance
async function asyncTask() {
// load latest spec from API Gateway export removed to simplicity reasons.
const swaggerDocument = spec;
console.log("load swagger file complete.");
return swaggerDocument
}
async function setup(event, context) {
const swaggerDocument = await asyncTask()
console.log(swaggerDocument)
app.use('/api-doc', swaggerUI.serveWithOptions({ redirect: false }));
app.use('/api-doc', swaggerUI.setup(swaggerDocument, options));
console.log("setup of swagger complete");
serverlessExpressInstance = serverlessExpress({ app })
return serverlessExpressInstance(event, context)
}
function handler(event, context) {
if (serverlessExpressInstance) return serverlessExpressInstance(event, context)
return setup(event, context)
}
exports.handler = handler
Setup on API Gateway is the following:
Both resources are pointing to the lambda function.
When I load the page via: https://.execute-api..amazonaws.com/dev/api-doc
The following errors are raised:
How can I ensure that the resources are loaded correctly via the correct path ...dev/api/doc/...
You're probbably getting these errors because swagger-ui is configured to look for the image/js resources and express doesn't serve these files. You could potentially use something like serverless-http like this, but I'm not really sure it's a good idea to try and force express into the response of an API gateway lambda function.
As an alternative I managed to get swagger up and running on an API Gateway lambda function, fetching the swagger JSON directly.
import { APIGateway } from "aws-sdk";
import { env } from "../../helpers/env";
const API_ID = env("API_ID", "");
export const handler = async () => {
const apiGateway = new APIGateway({
apiVersion: "2018-11-29",
});
const apiGatewayExport = await apiGateway
.getExport({
exportType: "swagger",
restApiId: API_ID,
stageName: "prod",
accepts: "application/json",
parameters: {
extensions: "apigateway",
},
})
.promise();
if (!apiGatewayExport.body) {
throw new Error("No body found in API Gateway Export");
}
return {
statusCode: 200,
headers: {
"Content-Type": "text/html",
},
body: generateSwaggerPageBody(apiGatewayExport.body?.toString()),
};
};
const generateSwaggerPageBody = (swaggerSpec: string) => `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist#3/swagger-ui.css">
</head>
<body>
<div id="swagger"></div>
<script src="https://unpkg.com/swagger-ui-dist#3/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
dom_id: '#swagger',
spec: ${swaggerSpec}
});
</script>
</body>
</html>`;
I'm taking the API information directly from API Gateway via SDK and then I just return an html page with tags loading swagger directly - no express overhead needed.

Node/Express server CORS issue with a different server port

I have a node/graphql server running on sitename.com:3333
I've created another server that I'm running on sitename.com:3334
I'm able to make requests to the server at sitename.com:3333 from sitename.com as well as subdomain.sitename.com
But if I try to connect to sitename.com:3334 (just a different port) from subdomain.sitename.com it gives me a cors error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://sitename.com:3334/graphql. (Reason: CORS request did not succeed)
I've opened the ports in the firewall and setup ssl on the server and client.
Please help!
Client code is below:
import { ApolloClient } from 'apollo-client'
import { withClientState } from 'apollo-link-state'
import { HttpLink } from 'apollo-link-http'
import { Agent } from 'https'
import fs from 'fs'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import { onError } from 'apollo-link-error'
import { ApolloLink } from 'apollo-link'
import decode from 'jwt-decode'
import history from '../history'
import Cookies from 'universal-cookie'
import {
APP,
AUTH,
CLIENT_AUTH_REQUEST_TYPE,
CLIENT_AUTHENTICATION_METHOD,
JWT,
VERSION
} from '../environment'
import https from 'https'
import { defaults, resolvers } from '../api'
import { createUploadLink } from 'apollo-upload-client'
const { CONSTANTS: { UNAUTHORIZED, FORBIDDEN } = {} } = APP
const cookies = new Cookies()
const opts = {
credentials: 'same-origin',
headers: {
'frontend-version': VERSION,
[AUTH.STRATEGIES.CLIENT.AUTH_HEADER]: CLIENT_AUTH_REQUEST_TYPE
}
}
const useLocalStorage = CLIENT_AUTHENTICATION_METHOD.LOCAL_STORAGE
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
// const apolloCache = new InMemoryCache();
const apolloCache = new InMemoryCache({
// dataIdFromObject: e => `${e.__typename}_${e.id}` || null // eslint-
disable-line no-underscore-dangle
})
// const watchedMutationLink = new WatchedMutationLink(apolloCache,
watchedMutations);
const stateLink = withClientState({
cache: apolloCache,
defaults,
resolvers
})
const uploadLink = createUploadLink({
// uri: 'http://localhost:3333/graphql',
uri: 'https://demo.MYSITE.in:3334/graphql',
fetchOptions: {
agent: new https.Agent()
}
})
const httpLink = new HttpLink({
uri: 'https://demo.MYSITE.in:3334/graphql',
...opts
})
const TOKEN_NAME = 'x-connector-token'
const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => {
const token = cookies.get('token')
if (token) {
headers = { ...headers, 'x-connector-token': token }
}
return { headers }
})
return forward(operation)
})
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors && graphQLErrors.filter(e => e).length > 0) {
graphQLErrors.map(({ message = '', status = 200 }) => {
if (UNAUTHORIZED === message || status === 401) {
if (
history &&
history.location &&
history.location.pathname !== '/login'
) {
history.push('/login')
}
}
if (FORBIDDEN === message || status === 403) {
history.push(`/error-page/403`)
}
return null
})
}
if (networkError && networkError.statusCode === 401) {
// eslint-disable-next-line
history.push('/login')
}
if (networkError && networkError.statusCode === 403) {
// Do something
}
if (networkError && networkError.statusCode === 400) {
// Do something
}
if (networkError && networkError.statusCode >= 500) {
// eslint-disable-next-line
history.push(`/error-page/${networkError.statusCode}`)
}
})
let links = [errorLink, stateLink, httpLink]
links = [
errorLink,
stateLink,
// afterwareLink,
// authMiddlewareLink,
authLink,
// watchedMutationLink,
// httpLink,
uploadLink
]
const link = ApolloLink.from(links)
export default new ApolloClient({
link,
cache: apolloCache,
connectToDevTools: true,
// opts: {
// agent
// },
fetchOptions: {
agent: new https.Agent()
// rejectUnauthorized: false
},
defaultOptions: {
query: {
errorPolicy: 'all'
}
},
onError: ({ networkError, graphQLErrors }) => {}
})
Server Code:
const app = express();
// tried this too
const corsOptions = {
origin: 'https://demo.MYSITE.in',
}
// also tried app.use(cors)
app.use(cors({
'allowedHeaders': ['Content-Type'],
'origin': '*',
'preflightContinue': true
}));
app.use(helmet());
// app.use(cors());
The browser will not make requests to a server of different origin than the origin that the web page itself came from (and a different port constitutes a different origin) unless you specifically enable that request for that new origin on your server. This is a garden variety CORs issue of which there are millions of posts and articles on how to handle. Since you show NONE of your code in your question, we can't recommend a specific code fix to your code.
Your server needs to support the specific CORS request you are trying to do. If you're using Express, then the CORS module does a lot of the work for you if properly implemented. CORS is there for your site's protection so Javascript in other people's web pages run from a browser can't arbitrarily use your APIs so be careful in exactly what you open up to CORS requests.
And, since this seems like a new issue to you, I would strongly suggest you read and learn about what CORs is and how it works.
Also, note that there are "Simple" CORS requests and "Pre-flighted Requests" (non-simple requests) and more work is required to enable Pre-flighted requests. The browser decides whether a given cross origin request is simple or requires pre-flight based on the exact parameters of the request and your server has to do more things to enable pre-flighted requests.

REST API router forward to Apollo GraphQL endpoinr

I have a node/express/Apollo application to provide GraphQL service to the frontend application. I also have the REST API endpoing in the application to provide service for legacy applications. I want to forward the REST API calls to the GraphQL endpoint. For example:
From
GET /api/roles
to
POST /graphql
{ *body* }
I tried like this:
// app.js
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import routes from './routes';
const port = process.env.PORT || 8088;
const app = express();
app.use('/api/roles', routes.role);
const server = new ApolloServer({
......
},
});
server.applyMiddleware({ app, path: '/graphql' });
app.listen({ port: port }, () => {
console.log(`Apollo Server on http://localhost:${port}/graphql`);
});
// routes/role.js
import { Router } from 'express';
const router = Router();
router.get('/', (req, res, next) => {
req.url = '/graphql';
req.originalUrl = '/graphql';
req.method = 'POST';
req.body = `
{
findRoles {
data {
roleId
name
}
}
}`;
return router.handle(req, res, next);
});
It doesn't work and gives the error "Cannot POST /graphql". Any idea how to do it?