Can't pass request body through next-http-proxy-middleware - javascript

I've been trying to create a frontend using nextjs for a backend I have running in Java.
I'm using the npm package next-http-proxy-middleware and it seems that either my request body is being dropped or I'm using the package incorrectly.
Here's the entirety of my ./src/api/[...all].ts file
import type { NextApiRequest, NextApiResponse } from 'next'
import httpProxyMiddleware from 'next-http-proxy-middleware'
type Data = {
name: string
}
export const config = {
api: {
externalResolver: true,
},
}
export default (req: NextApiRequest, res: NextApiResponse) => {
httpProxyMiddleware(req, res, {
target: `http://${process.env.REACT_APP_URL}`,
changeOrigin: true,
})
}
and here's the snippet of code where I attempt to login, the form that supplies this data is built using react-hook-form
const onSubmit: SubmitHandler<Inputs> = async (data: any) => {
//console.log('username', watch('username'), 'password', watch('password'))
const response = await axios.post(
'/api/session',
new URLSearchParams({
email: data.username,
password: data.password,
})
)
}
I do see that it's connecting to my backend since I'm getting an error that's generated by my Java program and not by next.js, but it just tells me that the email is null which leads me to believe that my args are being lost in translation
"Cannot invoke \"String.trim()\" because \"email\" is null - NullPointerException (DataManager:149 < PermissionsManager:508 < SessionResource:111 < ...)"
I am able to get it working using vanilla react so I'm sure it's something I'm doing wrong in the implementation of the next-http-proxy-middleware but I've been bashing my head against the wall a little too long on this. I appreciate all the help, thanks!

I figured out the solution and I feel a bit silly for not realizing earlier:
https://github.com/vercel/next.js/discussions/11036
Nextjs comes with an automatic body parser and I had to disable it.
export const config = {
api: {
externalResolver: true,
bodyParser: false,
},
}

Related

How to render dynamic route when a new blog post is added in nextjs without running npm run build command

I have developed a blog app in nextjs, while adding new blog in the database, it does not render the page with new route, instead I have to manually do npm run build to pre-render new blog. Until then it keeps showing not found error.
export async function getStaticPaths() {
const posts = await service.getAllPosts();
const paths = posts.map((post) => ({
params: { slug: post.slug },
}));
return { paths, fallback: 'blocking' };
}
export async function getStaticProps({
params,
}: GetStaticPropsContext<{ slug: string }>) {
const slug = params?.slug as string;
try {
const { post } =
await service.getPost(slug);
return {
props: { post },
revalidate: 10,
};
} catch (error) {
return { props: { post: {}, error: error.message} };
}
}
const Blog = ({ post }: Props) => {
return <div>
<p>{post.title}
</div>
}
I am not sure what I am doing wrong here.
When I add a new blog post, I does not render on the client side when i visit: https://example.com/blog/new-slug
This happens because you are using getStaticProps, which only gets called when the website is built. It is useful for pages that don't change very often.
If you want the website to update any time you post something, you should be using getServerSideProps that gets called each time you are visiting the page.
Alternatively you could also do client side fetching, if you don't mind about the SEO.
Another way if you want to keep the performance of a static page, is to use the revalidate option in the getStaticProps. It tells the page to rebuild after a time interval.

How come I can't use environment variables like this?

I have set up a monorepo for my project using lerna, and the website package is written using Next.
I have another package called omega=lib which acts as a library, and contains models/schemas for my mongo database. It also has a function that connects to it.
In my website, I am trying to fetch some data from the Channel schema (which exists in omega-lib), and the code for that looks like this:
Channel.ts
import { Schema, model, Document } from "mongoose";
import dbConnect from "../utils/dbConnect";
export interface IChannel extends Document {
name: string
logging_enabled: boolean
}
const ChannelSchema = new Schema({
name: { type: String, index: { unique: true } },
logging_enabled: Boolean
});
const Channel = model<IChannel>('Channel', ChannelSchema);
export const getChannel = async (username: string) => {
await dbConnect();
return Channel.findOne({ name: username });
}
export default Channel;
I am trying to use this code in one of my pages, using getServerSideProps:
export const getServerSideProps = async (context: GetServerSidePropsContext<ParsedUrlQuery>) => {
console.log("DB", process.env.MONGO_DB_URI);
const userInfo: User = await getUser(context);
const moderators: User[] = await getModerators(context);
const channelInfo = await getChannel(userInfo.login);
console.log("CHANNEL INFO", channelInfo);
return {
props: {
userInfo,
moderators,
channelInfo
}
}
}
IT IS WORTH NOTING, THAT ON LINE 2, THIS VALUE GETS LOGGED TO THE CONSOLE CORRECTLY!
Here's where the problem starts, when this code rus, I get this error:
MongooseError: The uri parameter to openUri() must be a string, got "undefined". Make sure the first parameter to mongoose.connect() or mongoose.createConnection() is a string.
This is coming from when the dbConnect() function is called. Here is the code for that:
import mongoose from 'mongoose';
const dbConnect = (uri?: string) => {
if (mongoose.connection.readyState >= 1) {
return;
}
return mongoose.connect(process.env.MONGO_DB_URI ?? uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
})
}
export default dbConnect;
I do not see how MONGO_DB_URI can be null, it is defined in my next.config.js:
module.exports = {
env: {
PRIVATE_KEY: "xxx",
CLIENT_ID: "xxx",
MONGO_DB_URI: "mongodb://localhost:27017/omegalogs",
REDIRECT_URI: "http://localhost:3000/login",
SCOPES: "user:read:email,moderation:read",
WEBSOCKET_PORT: 9393
},
};
And it is worth noting, that the correct value gets printed in the example code above, however it is undefined when being used in dbConnect().
This dbConnect() function is defined in a the omega-lib package, but is used as a dependency. I don't think this is the problem, because it works fine in another project that has the same MONGO_DB_URI defined in the environment file.
This only works if I actually pass the MONGO_DB_URI environment variable through as a parameter to dbConnect(), which is why it is an optional string.
Can anyone tell me why the dbConnect() function is not picking up the environment variable? It should be doing this server side, so I don't see why it wouldn't.
you already try to create a .env file and store the variables there for the process can see? you can install dotenv with:
npm i dotenv
//add dotenv to your index file
require('dotenv').config()
//this will in you .env file in the root of the project
PRIVATE_KEY: "xxx",
CLIENT_ID: "xxx",
MONGO_DB_URI: "mongodb://localhost:27017/omegalogs",
REDIRECT_URI: "http://localhost:3000/login",
SCOPES: "user:read:email,moderation:read",
WEBSOCKET_PORT: 9393
//now you can use in the same way
process.env.MONGO_DB_URI
is a easy way to do in my opinion

Typescript Error code: 2366, Function lacks ending return statement and return type does not include 'undefined'

I am currently working on the server/database of my project. It is currently composed of Javascript, Typescript, MongoDB, Apollo-Server, and Express. The error above keeps coming up and I am not sure how to solve it. Here is the code I have on my index.ts file for my database folder.
import { MongoClient } from "mongodb";
import { Database, Listing, Booking, User } from '../lib/types';
const url = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_USER_PASSWORD}#${process.env.DB_CLUSTER}.mongodb.net`;
export const connectDatabase = async (): Promise<Database> => {
try {
const client = await MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true });
const db = client.db("main");
return {
bookings: db.collection<Booking>("bookings"),
listings: db.collection<Listing>("listings"),
users: db.collection<User>("users"),
};
} catch (error) {
console.log(error);
}
};
Any help would be greatly appreciated.
You're catching the error but then you're not returning anything from the function. That is why it's complaining. Either remove the try/catch and handle the error in the function calling this one or return something usable to the caller.
Set "noImplicitReturns" to false in tsconfig.json.

Using react hooks inside NextJS /pages/api [duplicate]

I need a graphql client lib to run on node.js for some testing and some data mashup - not in a production capacity. I'm using apollo everywhere else (react-apollo, apollo's graphql-server-express). My needs are pretty simple.
Is apollo-client a viable choice? I can find no examples or docs on using it on node - if you're aware of any, please share.
Or maybe I should/can use the reference graphql client on node?
Apollo Client should work just fine on Node. You only have to install cross-fetch.
Here is a complete TypeScript implementation of Apollo Client working on Node.js.
import { ApolloClient, gql, HttpLink, InMemoryCache } from "#apollo/client";
import { InsertJob } from "./graphql-types";
import fetch from "cross-fetch";
const client = new ApolloClient({
link: new HttpLink({ uri: process.env.PRODUCTION_GRAPHQL_URL, fetch }),
cache: new InMemoryCache(),
});
client.mutate<InsertJob.AddCompany, InsertJob.Variables>({
mutation: gql`mutation insertJob($companyName: String!) {
addCompany(input: { displayName: $companyName } ) {
id
}
}`,
variables: {
companyName: "aaa"
}
})
.then(result => console.log(result));
Newer Apollo version provide a simpler approach to perform this, as described in Apollo docs, check the section "Standalone". Basically one can simply use ApolloLink in order to perform a query or mutation.
Below is copy of the example code from the docs as of writing this, with node-fetch usage as config to createHttpLink. Check the docs for more details on how to use these tools.
import { execute, makePromise } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';
import gql from 'graphql-tag';
import fetch from 'node-fetch';
const uri = 'http://localhost:4000/graphql';
const link = createHttpLink({ uri, fetch });
const operation = {
query: gql`query { hello }`,
variables: {} //optional
operationName: {} //optional
context: {} //optional
extensions: {} //optional
};
// execute returns an Observable so it can be subscribed to
execute(link, operation).subscribe({
next: data => console.log(`received data: ${JSON.stringify(data, null, 2)}`),
error: error => console.log(`received error ${error}`),
complete: () => console.log(`complete`),
})
// For single execution operations, a Promise can be used
makePromise(execute(link, operation))
.then(data => console.log(`received data ${JSON.stringify(data, null, 2)}`))
.catch(error => console.log(`received error ${error}`))
If someone is looking for a JavaScript version:
require('dotenv').config();
const gql = require('graphql-tag');
const ApolloClient = require('apollo-boost').ApolloClient;
const fetch = require('cross-fetch/polyfill').fetch;
const createHttpLink = require('apollo-link-http').createHttpLink;
const InMemoryCache = require('apollo-cache-inmemory').InMemoryCache;
const client = new ApolloClient({
link: createHttpLink({
uri: process.env.API,
fetch: fetch
}),
cache: new InMemoryCache()
});
client.mutate({
mutation: gql`
mutation popJob {
popJob {
id
type
param
status
progress
creation_date
expiration_date
}
}
`,
}).then(job => {
console.log(job);
})
You can make apollo-client work, but it's not the best option for this use case.
Try graphql-request instead.
Minimal GraphQL client supporting Node and browsers for scripts or simple apps
Features per npmjs:
Most simple & lightweight GraphQL client
Promise-based API (works with async / await)
Typescript support
Isomorphic (works with Node / browsers)
example:
import { request, gql } from 'graphql-request'
const query = gql`
{
Movie(title: "Inception") {
releaseDate
actors {
name
}
}
}
`
request('https://api.graph.cool/simple/v1/movies', query).then((data) => console.log(data))
I have no affiliation with this package.
Here is simple node js implementation.
'graphiql' client is good enough for development activities.
1. run npm install
2. start server with "node server.js"
3. hit "http://localhost:8080/graphiql" for graphiql client
server.js
var graphql = require ('graphql').graphql
var express = require('express')
var graphQLHTTP = require('express-graphql')
var Schema = require('./schema')
// This is just an internal test
var query = 'query{starwar{name, gender,gender}}'
graphql(Schema, query).then( function(result) {
console.log(JSON.stringify(result,null," "));
});
var app = express()
.use('/', graphQLHTTP({ schema: Schema, pretty: true, graphiql: true }))
.listen(8080, function (err) {
console.log('GraphQL Server is now running on localhost:8080');
});
schema.js
//schema.js
var graphql = require ('graphql');
var http = require('http');
var StarWar = [
{
"name": "default",
"gender": "default",
"mass": "default"
}
];
var TodoType = new graphql.GraphQLObjectType({
name: 'starwar',
fields: function () {
return {
name: {
type: graphql.GraphQLString
},
gender: {
type: graphql.GraphQLString
},
mass: {
type: graphql.GraphQLString
}
}
}
});
var QueryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: function () {
return {
starwar: {
type: new graphql.GraphQLList(TodoType),
resolve: function () {
return new Promise(function (resolve, reject) {
var request = http.get({
hostname: 'swapi.co',
path: '/api/people/1/',
method: 'GET'
}, function(res){
res.setEncoding('utf8');
res.on('data', function(response){
StarWar = [JSON.parse(response)];
resolve(StarWar)
console.log('On response success:' , StarWar);
});
});
request.on('error', function(response){
console.log('On error' , response.message);
});
request.end();
});
}
}
}
}
});
module.exports = new graphql.GraphQLSchema({
query: QueryType
});
In response to #YakirNa 's comment:
I can't speak to the other needs I described, but I have done a fair amount of testing. I ended up doing all of my testing in-process.
Most testing ends up being resolver testing, which I do via a jig that invokes the graphql library's graphql function with a test query and then validates the response.
I also have an (almost) end-to-end test layer that works at the http-handling level of express. It creates a fake HTTP request and verifies the response in-process. This is all within the server process; nothing goes over the wire. I use this lightly, mostly for testing JWT authentication and other request-level behavior that's independent of the graphql request body.
I was running into your same question, because I wanted to create a middleware service to prepare data from graphQL to a final frontend application,
to have :
optimised data representation (and standard output data interface)
faster response time
assuming that graphQL server is provided by an external provider , so no ownership to data model, directly with GQL
So I didn't want to implement GraphQL Apolloclient directly in a frontend framework like React / Angular, Vuejs... but manage the queries via Nodejs at backend of a REST API.
So this is the class wrapper for Apolloclient I was able to assemble (using typescript):
import ApolloClient from "apollo-client";
import { ApolloLink } from 'apollo-link'
import { HttpLink } from 'apollo-link-http'
import { onError } from 'apollo-link-error'
import fetch from 'node-fetch'
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'
import introspectionQueryResultData from '../../fragmentTypes.json';
import { AppConfig } from 'app-config';
const config: AppConfig = require('../../../appConfig.js');
export class GraphQLQueryClient {
protected apolloClient: any;
constructor(headers: { [name: string]: string }) {
const api: any = {
spaceId: config.app.spaceId,
environmentId: config.app.environmentId,
uri: config.app.uri,
cdnApiPreviewToken: config.cdnApiPreviewToken,
};
// console.log(JSON.stringify(api));
const ACCESS_TOKEN = api.cdnApiPreviewToken;
const uri = api.uri;
console.log(`Apollo client setup to query uri: ${uri}`);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData
});
this.apolloClient = new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }:any) => {
if (graphQLErrors) {
graphQLErrors.map((el:any) =>
console.warn(
el.message || el
)
)
graphQLErrors.map(({ message, locations, path }:any) =>
console.warn(
`[GraphQL error - Env ${api.environmentId}]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${path}`
)
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
}),
new HttpLink({
uri,
credentials: 'same-origin',
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`
},
fetch
})
]),
cache: new InMemoryCache({ fragmentMatcher }),
// fetchPolicy as network-only avoids using the cache.
defaultOptions: {
watchQuery: {
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
},
query: {
fetchPolicy: 'network-only',
errorPolicy: 'all',
},
}
});
}
}
After this constructor I run queries like :
let response = await this.apolloClient.query({ query: gql`${query}` });
As you might have noticed:
I needed to inject fetch on Httplink
I had to setup Authorization headers to access external provider graphQL endpoint
I used IntrospectionFragmentMatcher in order to use Fragments in my queries, along with building schema type ("fragmentTypes.json" with an init script)
Posting this to just add my experience and maybe more info for the question.
Also looking forward for comments and points of improvement for this wrapper.

Graphql Yoga Playground with Lambda - "Server cannot be reached"

I'm in the process of setting a graphql endpoint with servlerless/ lambda and am receiving an error when trying to connect to the graphql playground that comes with graphql-yoga. When I go to my route that has the playground (/playground) it launches the playground interface however it just says:
Server cannot be reached
In the top right of the playground. It's worth noting i'm using the makeRemoteExecutableSchema utility to proxy to another graphql endpoint (which is my CMS called Prismic). I don't believe this is the issue as I have successfully connected to it with the playground when testing on a normal express server.
Here is the code in my handler.js
'use strict';
const { makeRemoteExecutableSchema } = require('graphql-tools');
const { PrismicLink } = require("apollo-link-prismic");
const { introspectSchema } = require('graphql-tools');
const { ACCESS_TOKEN, CMS_URL } = process.env;
const { GraphQLServerLambda } = require('graphql-yoga')
const lambda = async () => {
const link = PrismicLink({
uri: CMS_URL,
accessToken: ACCESS_TOKEN
});
const schema = await introspectSchema(link);
const executableSchema = makeRemoteExecutableSchema({
schema,
link,
});
return new GraphQLServerLambda({
schema: executableSchema,
context: req => ({ ...req })
});
}
exports.playground = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const graphQl = await lambda();
return graphQl.playgroundHandler(event, context, callback);
};
I have followed this guide for getting it running up till here and am fairly sure i've followed similar steps for what applies to what i'm trying to do but can't seem to figure out where i've gone wrong.
Thanks,
Could you take a look at what version of the graphql-yoga package you are using?
I had a similar problem using the Apollo server in combination with Kentico Cloud Headless CMS and I found this issue:
https://github.com/prisma/graphql-yoga/issues/267

Categories

Resources