I have been following the www.howtographql.com tutorials but i am stuck as i am not able to add authorization token in http headers. As soon as i write the authorization token it gives an error - 'Server cannot be reached'
index.js
const { ApolloServer } = require('apollo-server');
const { PrismaClient } = require('#prisma/client');
const fs = require('fs');
const path = require('path');
const { getUserId } = require('./utils');
const Query = require('./resolvers/Query');
const Mutation = require('./resolvers/Mutation');
const User = require('./resolvers/User');
const Link = require('./resolvers/Link');
const prisma = new PrismaClient();
const resolvers = {
Query,
Mutation,
User,
Link
}
const server = new ApolloServer({
typeDefs: fs.readFileSync(
path.join(__dirname, 'schema.graphql'),
'utf-8'
),
resolvers,
context:({ req }) => {
return {
...req,
prisma,
userId: req && req.headers.authorization ? getUserId(req) : null
};
}
});
server.listen()
.then(({ url }) => console.log(`Server is running on ${url}`)
);
Related
I have a project where I use Next.js on the front-end and Express.js on the back.
Front-end side
The 'pages' file contains 'index.js'. In it, I am sending the following request.
import Home from "./home/home";
import axios from "axios";
import { useState } from "react";
export default function Index({ data }) {
const [products, setProducts] = useState(data);
const [start, setStart] = useState(1);
const getMoreProducts = async () => {
setStart(start + 1);
const { newProducts } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/test`
);
setProducts([...products, ...newProducts]);
};
return (
<div>
<Home data={products} />
<button onClick={getMoreProducts}> Load more {start}</button>
</div>
);
}
export async function getServerSideProps(context) {
// Fetch data from external API
const { data } = await axios.get(
`${process.env.NEXT_PUBLIC_BACK_END}/productlist/pagination`,
{
params: {
page: 1,
limit: 5,
},
}
);
return {
props: {
data: data || {},
},
};
// Pass data to the page via props
}
Back-end side
const express = require("express");
var cors = require('cors')
const app = express();
require("dotenv").config();
const mongoose = require("mongoose");
mongoose.connect(
"*********",
{ useNewUrlParser: true }
);
const db = mongoose.connection;
db.on("error", (err) => console.error(err));
db.once("open", () => console.log("Connected to Database "));
app.use(express.json());
const productlistRouter = require("./routes/productlist");
const test = require("./routes/test");
app.use("/productlist", productlistRouter);
app.use("/test", test);
app.use(cors())
app.listen(3000, () => console.log("Server is running"));
And here is my Route code :
const express = require("express");
const router = express.Router();
const Product = require("../models/product");
const cors = require("cors");
const corsOptions = {
headers: [
{ key: "Access-Control-Allow-Credentials", value: "true" },
{ key: "Access-Control-Allow-Origin", value: "*" },
// ...
],
origin: "*",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
};
router.get("/showall", async (req, res) => {
try {
const product = await Product.find();
res.json(product);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
router.get("/pagination", cors(corsOptions), async (req, res) => {
const page = req.query.page;
const limit = req.query.limit;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const products = await Product.find();
const result = products.slice(startIndex, endIndex);
res.json(result);
});
So, When the page is first built with Next.js, the api works, but when I click the 'Load more' button, it gives a CORS error. When I use the same query with 'postman' and other tools, it does not give any error.
On the Next.js side, it works when I send a query to another 3rd party API., but it doesn't work when I send it to my own back-end. And no matter what page or component I do this in, only the APIs that are created at the build time are working.
What could be the reason for this? and how can i solve it? I've read and searched a few articles about cors, but I still haven't found a solution for days.
CORS should be placed on top level as javascript is executed one by one line. Place app.use(cors()) just above the line of app.use("/productlist", productlistRouter);
I'm getting a cors error when trying to login into my MERN app
Screenshot.It's working fine locally and in the Network tab it shows a 503 error code.
My Heroku logs show these messages.
const err = new MongooseError(message);
MongooseError: Operation users.findOne() buffering timed out after 10000ms
But, I'm getting the 'User Name verified' console log in the Heroku logs which is present in the validation.js file below. So the backend connection is fine I think.
I used 0.0.0.0/0 in MongoDB network access and used cors package for all origin.
Also tried using mongoose.connect using async/await, and using the older drivers in MongoDB but nothing seems to work. If anyone knows how to fix it please respond.
Here is my code in node.js.
index.js
const express = require('express')
const cors = require('cors')
const DBConnect = require('./mongo')
const Login = require('./routes/login')
const Google = require('./routes/google')
const Register = require('./routes/register')
const SaveData = require('./routes/saveData')
require('dotenv').config()
const port = process.env.PORT || 5000
const app = express()
const corsOptions = {
origin: '*',
credentials: true,
optionSuccessStatus: 200
}
app.use(cors(corsOptions))
app.use(express.json())
DBConnect().then(()=>console.log('DB Connection successfull!'))
app.get('/', (req,res)=>{ res.json('Server running')})
app.use('/api/gauth', Google)
app.use('/api/login', Login)
app.use('/api/register', Register)
app.use('/api/save', SaveData)
app.listen(port,()=>console.log(`Server listening on port ${port}`))
mongo.js
const mongoose = require('mongoose')
module.exports = async () => {
const username = process.env.USER_NAME
const password = process.env.PASSWORD
const cluster = process.env.CLUSTER
const dbname = process.env.DB_NAME
const url = `mongodb+srv://${username}:${password}#${cluster}.mongodb.net/${dbname}?retryWrites=true&w=majority`
const mongooseOptions = {
useNewUrlParser: true,
useUnifiedTopology: true
}
await mongoose.connect(url, mongooseOptions)
.then(x => console.log(`Connected to MongoDB : ${x.connections[0].name}`))
.catch(err => console.error('Error connecting to mongo', err))
}
google.js
const app = require('express')()
const googleAuth = require('../components/validation')
const userModel = require('../components/models')
const bcrypt = require('bcrypt')
const JWT = require('jsonwebtoken')
const dotenv = require('dotenv')
dotenv.config()
app.post('/', async (req,res)=>{
const user = await googleAuth(req.body.token)
const emailExist = await userModel.findOne({ email: user.email })
const token = JWT.sign({_id: user.id}, process.env.TOKEN_KEY)
const data = {
token: token,
email: user.email,
name: user.name,
image: user.image,
data: emailExist && emailExist.data || []
}
if(!emailExist){
const salt = await bcrypt.genSalt(10)
const cipherPass = await bcrypt.hash(user.id, salt)
const newUser = new userModel({
username: user.name,
email: user.email,
password: cipherPass,
image: user.image,
data: []
})
try{
await newUser.save()
res.status(200).json(data)
}catch (err){
res.status(500).json(err)
}
}else{
res.status(200).json(data)
}
})
module.exports = app
validation.js
const { OAuth2Client } = require('google-auth-library')
const client = new OAuth2Client(process.env.OAUTH_ID)
const googleAuth = async token => {
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.OAUTH_ID
})
const payload = ticket.getPayload()
// console.log('Paylod:', payload)
console.log(`User ${payload.name} verified`)
const { sub, name, email, picture } = payload
const userId = sub
return { id:userId, name:name, email:email, image:picture}
}
module.export = googleAuth
I don't get the request variable in the mutation GraphQL on the backend. I don't understand why it doesn't work.
I get the next error:
"Cannot destructure property 'name' of 'undefined' as it is undefined."
That mutation I make in Apollo Studio:
mutation Mutation($createOwnerName: String) {
createOwner(name: $createOwnerName)
}
My variables in Apollo Studio:
{
"createOwnerName": "John"
}
My backend with Express
schema.js:
const { buildSchema } = require("graphql");
const schema = buildSchema(`
type Mutation {
createOwner(name: String): String
}
`);
module.exports = schema;
resolvers.js:
const resolvers = {
Mutation: {
createOwner: ({name}) => {
console.log('createOwner name', name)
return name
}
}
}
server.js:
const { createServer } = require("http");
const express = require("express");
const { execute, subscribe } = require("graphql");
const { ApolloServer } = require("apollo-server-express");
const { SubscriptionServer } = require("subscriptions-transport-ws");
const { makeExecutableSchema } = require("#graphql-tools/schema");
const typeDefs = require("./graphql/schema.js");
const resolvers = require("./graphql/resolvers.js");
require("dotenv").config();
const mongoose = require("mongoose");
// mongoose
mongoose
.connect(process.env.DB_HOST, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("MongoDB connected"))
.catch((err) => console.log(err));
(async () => {
const PORT = 3033;
const app = express();
const httpServer = createServer(app);
app.get("/rest", function (req, res) {
return res.json({ data: "rest" });
});
const schema = makeExecutableSchema({ typeDefs, resolvers });
const server = new ApolloServer({
schema,
});
await server.start();
server.applyMiddleware({ app });
SubscriptionServer.create(
{ schema, execute, subscribe },
{ server: httpServer, path: server.graphqlPath }
);
httpServer.listen(PORT, () => {
console.log(
`🚀 Query endpoint ready at http://localhost:${PORT}${server.graphqlPath}`
);
console.log(
`🚀 Subscription endpoint ready at ws://localhost:${PORT}${server.graphqlPath}`
);
});
})();
You are destructuring the wrong argument. Arguments are in that order:
Parent value
Argument values
Context
GraphQL Resolve Info
Destructure the second parameter:
const resolvers = {
Mutation: {
createOwner: (parent, {name}) => {
console.log('createOwner name', name)
return name
}
}
}
I have built my portfolio webpage with next.js now I need to test it. to test the express server I use supertest. But the problem is I need to refactor express to use it. Because supertest need to access to app() before listening.
I started the way how I used to implement in node.js app. Put the express code in app.js and call it in index.js.
const express = require("express");
const server = express();
const authService = require("./services/auth");
const bodyParser = require("body-parser");
//put all the middlewares here
module.exports = server;
and then in index.js
const server = require("express")();
// const { parse } = require("url");
const next = require("next");
const routes = require("../routes");
const path = require("path");
require("./mongodb");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
// const handle = app.getRequestHandler(); //this is built in next route handler
const handle = routes.getRequestHandler(app);
app
.prepare()
.then(() => {
const server = require("./app");
//I required this outside too but it did not solve the issue
server.listen(3000, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3000");
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
with this set up, express is listening, I am able connect to mongodb, during the start up there is no issue.
When i request to localhost:3000, there is no response from localhost, it is spinning till timeout
Create a test client:
// test-client.ts
import { createServer, RequestListener } from "http";
import { NextApiHandler } from "next";
import { apiResolver } from "next/dist/next-server/server/api-utils";
import request from "supertest";
export const testClient = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
return apiResolver(
req,
res,
undefined,
handler,
{
previewModeEncryptionKey: "",
previewModeId: "",
previewModeSigningKey: "",
},
false
);
};
return request(createServer(listener));
};
Test your APIs with:
// user.test.ts
import viewerApiHandler from "../api/user";
import { testClient } from "../utils/test-client";
const request = testClient(viewerApiHandler);
describe("/user", () => {
it("should return current user", async () => {
const res = await request.get("/user");
expect(res.status).toBe(200);
expect(res.body).toStrictEqual({ name: "Jane Doe" });
});
});
For those who want to add query parameters, here's the answer:
import { createServer, RequestListener } from 'http'
import { NextApiHandler } from 'next'
import { apiResolver } from 'next/dist/server/api-utils/node'
import request from 'supertest'
export const handlerRequester = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
let query = {}
let queryUrl = req.url.split('?')[1]
if (queryUrl) {
queryUrl
.split('&')
.map((p) => [p.split('=')[0], p.split('=')[1]])
.forEach((k) => {
query[k[0]] = k[1]
})
}
return apiResolver(
req,
res,
query,
handler,
{
previewModeEncryptionKey: '',
previewModeId: '',
previewModeSigningKey: '',
},
false
)
}
const server = createServer(listener)
return [request(server), server]
}
I've just released a new npm package which handle this case here:
https://www.npmjs.com/package/nextjs-http-supertest
Feel free to test it and give me feedback !
The app is using next.js connected to express, which is in turn connected to AWS MySql.
I'm trying to initially load some products I have stored on my database. However, i get the following network error:-
TypeError: NetworkError when attempting to fetch resource.
I troubleshooted the frontend code by using an external api and that works fine, so it's something to do with the express middleware.
See code excerpts below...
index.js
import '../scss/style.scss';
import NavBar from '../components/Navbar/Navbar';
import fetch from 'isomorphic-unfetch';
const Index = (props) => (
<div>
<NavBar />
<h1>See our products below yeah</h1>
</div>
)
Index.getInitialProps = async function() {
const res = await fetch('localhost:3000/');
const data = await res.json();
return {
posts: data
}
}
export default Index;
server.js
const express = require('express');
const next = require('next');
const port = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const db = require('../lib/db')
const escape = require('sql-template-strings')
app.prepare().then(() => {
const server = express()
server.get('/', (req, res, next) => {
let sql = 'SELECT * FROM products'
db.query(sql, [], (err, rows) => {
if (err) {
throw err;
} else {
return res.json({
data: rows
})
}
})
})
...
db.js
const mysql = require('serverless-mysql')
const db = mysql({
config: {
host: "XXX",
database: "XXX",
user: "XXX",
password: "XXX"
}
})
exports.query = async query => {
try {
const results = await db.query(query)
await db.end()
return results
} catch (error) {
return { error }
}
}
whenever you have api request from a different server you have to setup CORS on both frontend and backend
Link: https://expressjs.com/en/resources/middleware/cors.html