I just started to get into GraphQL. I am using GraphQL.js and express. Right now I am trying to build a simple example using a hardcoded JSON as the data in my javascript file. I then want to use express middleware to listen to HTTP requests via curl or insomnia. In the middleware I want to extract the query using body-parser. Right now I am having trouble with resolvers.
Please have a look at my code.
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema, graphql } = require('graphql');
var bodyParser = require('body-parser');
var schema = buildSchema(`
type Product {
name: String!
price: Int!
}
type Query {
product(name: String): Product
}
`);
var products = {
'Mango': {
name: 'Mango',
price: 12,
},
'Apfel': {
name: 'Apfel',
price: 3,
},
};
resolvers = {
Query: {
product: (root, { name}) => {
return products[name];
},
},
};
var app = express();
app.use(bodyParser.text({ type: 'application/graphql' }));
app.post('/graphql', (req, res) => {
graphql(schema, req.body)
.then((result) => {
res.send(JSON.stringify(result, null, 2));
});
});
app.listen(4000);
This does not work. When I post a query using curl with
curl -XPOST -H "Content-Type: application/graphql" -d "{product(name: \"Apfel\"){name price}}" http://localhost:4000/graphql
I get the response {"data". {"product": null}}. The resolver doesn't get called. How can I do this correctly?
Can you try this?
var resolvers = {
product: (args) => {
return products[args.name];
},
};
app.post('/graphql', (req, res) => {
graphql(schema, req.body, resolvers)
.then((result) => {
res.send(JSON.stringify(result, null, 2));
});
});
I think this can solve your issue
I recomend watching FunFunFunction series episode focused on GraphQl:
GraphQl Basics
All of his episodes are quite interesting (and really fun)...
Related
Hi.
I have this most simple express graphql server.
I want to use sofa-api to make it "rest-able".
Two problems:
when you go to /api/hello it should say "Hello World!", now its null. the /graphql route does work correctly and return "Hello World!".
The rest swagger interface is not loading at /api
you can play with it here: https://stackblitz.com/edit/node-p6jnji?file=index.js,package.json
var schema = buildSchema(`
type Query{
hello:String
}`);
const openApi = OpenAPI({
schema,
info: {
title: 'Example API',
version: '3.0.0',
},
});
var root = {
hello: () => {
return 'Hello World!';
},
};
var app = express();
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
})
);
app.use(
'/api',
useSofa({
schema,
basePath: '/api',
onRoute(info) {
openApi.addRoute(info, {
basePath: '/api',
});
},
})
);
writeFileSync('./swagger.json', JSON.stringify(openApi.get(), null, 2));
app.listen(4400);
Thanks
There is no root value defined in Sofa.
In graphqlHttp call, you provide rootValue as an execution parameter but Sofa is not aware of that
I assume the most elegant way is to build your schema with the resolvers (root value)
something like this:
import { makeExecutableSchema } from '#graphql-tools/schema'
export const schema = makeExecutableSchema({ typeDefs, resolvers })
You can find more info here
So first what i want to say, is that none of the public questions on stackoverflow did not helped me with this error. I am running the Stripe CLI on my local machine like this : stripe listen --forward-to localhost:4242/webhook , but weirdly when i try to proccess all the events inside i get the error :
No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
I have already tried using request.rawBody , but it didn't fix my issue.
I am posting all of the code, so maybe someone will see what i can't and help me fixing it
router.js :
let express = require('express');
let router = express.Router();
let bodyParser = require('body-parser')
let postMong = require('./post')
require("dotenv").config()
router.use(express.json());
const YOUR_DOMAIN = 'http://localhost:4242';
const stripe = require('stripe')(process.env.PUBLIC_KEY);
router.post('/checkout/create-order', async (req, res) => {
const price = req.body.order.stripe_price || undefined,
product = req.body.order.stripe_product || undefined
const session = await stripe.checkout.sessions.create({
shipping_address_collection: {
allowed_countries: ['US', 'CA'],
},
shipping_options: [
{
shipping_rate_data: {
type: 'fixed_amount',
fixed_amount: {
amount: 2499,
currency: 'usd',
},
display_name: 'International Shipping',
// Delivers between 5-7 business days
delivery_estimate: {
minimum: {
unit: 'week',
value: 2,
},
}
}
},
],
line_items: [
{
price: price,
quantity: 1,
},
],
payment_method_types: ["card", 'us_bank_account'],
mode: 'payment',
success_url: `${YOUR_DOMAIN}/success.html`,
cancel_url: `${YOUR_DOMAIN}/index.html`,
});
res.json({url: session.url})
});
router.post('/posts/add', async (req,res)=>{
try{
const {author, id, name, picture, pixels, price, size, stripe_price, stripe_product} = req.body
const pos = await postMong.create( {author, id, name, picture, pixels, price, size, stripe_price, stripe_product})
res.json(pos)
} catch(e){
res.status(500).json(e)
}
})
router.get('/ideas', async (req,res)=>{
try{
const posts = await postMong.find()
return res.json(posts);
} catch(e){
reject(res.status(500).json(e))
}
})
const endpointSecret = 'whsec_****';
const fulfillOrder = (session) => {
// TODO: fill me in
console.log("Fulfilling order", session);
}
router.use(bodyParser.json());
router.post('/webhook', (request, response) => {
const payload = request.body;
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret);
console.log(event)
} catch (err) {
console.log(err.message)
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Fulfill the purchase...
fulfillOrder(session);
}
response.status(200);
});
module.exports = router
server.js :
const router = require("./router");
const account = require("./routerAccount");
const express = require('express');
const mongoose = require("mongoose")
const app = express();
const cors = require('cors')
var session = require('express-session');
require("dotenv").config()
const db_url = process.env.MONGO_URL
app.use(session({
cookie: {
httpOnly: true
},
rolling: true,
resave: true,
saveUninitialized: true,
secret: '~~~~~'
}));
app.set('view engine','ejs');
app.use(express.static('public'));
//app.use(express.json());
app.use(cors())
app.use('/', router)
app.use('/', account)
async function startApp(){
try{
await mongoose.connect(db_url, {
useUnifiedTopology: true,
useNewUrlParser:true
})
app.listen(4242, () => {console.log("server is working")})
} catch(e) {
console.log("some error appearead" + e)
}
}
startApp()
Normally when you see this error, it means that, either the HTTP request body Stripe sent to your webhook handler has been altered in some way or You may not be using the correct webhook secret.
The most likely reason it is throwing an exception is because your router is parsing body as JSON with router.use(express.json()). constructEvent requires the raw, unparsed body you receive from the request to verify the signature. To verify you have the raw body you can print it out and see if you get something like <Buffer 28 72 10..>
You can tell your router to keep the request body raw by setting something like this on your route router.use('/webhook', express.raw({type: "*/*"}))
I found the solution for my problem.
What i added is
app.use( "/webhook",express.raw({ type: "*/*" }))
in my server.js file.
I'm developing a Vue.js application which has only frontend (no server) and send a lot of requests to different APIs. The originally quite simple app became more complex. And there are problems with some APIs, because browsers do not accept the responses due to CORS. That is why I'm trying to test, if I can migrate the app to Nuxt.js.
My approach is as follows (inspired by this comment), but I expect, that there is probably a better way to send the requests from the client over the server.
pages/test-page.vue
methods: {
async sendRequest(testData) {
const response = await axios.post('api', testData)
// Here can I use the response on the page.
}
}
nuxt.config.js
serverMiddleware: [
{ path: '/api', handler: '~/server-middleware/postRequestHandler.js' }
],
server-middleware/postRequestHandler.js
import axios from 'axios'
const configs = require('../store/config.js')
module.exports = function(req, res, next) {
let body = ''
req.on('data', (data) => {
body += data
})
req.on('end', async () => {
if (req.hasOwnProperty('originalUrl') && req.originalUrl === '/api') {
const parsedBody = JSON.parse(body)
// Send the request from the server.
const response = await axios.post(
configs.state().testUrl,
body
)
req.body = response
}
next()
})
}
middleware/test.js (see: API: The Context)
export default function(context) {
// Universal keys
const { store } = context
// Server-side
if (process.server) {
const { req } = context
store.body = req.body
}
}
pages/api.vue
<template>
{{ body }}
</template>
<script>
export default {
middleware: 'test',
computed: {
body() {
return this.$store.body
}
}
}
</script>
When the user makes an action on the page "test", which will initiate the method "sendRequest()", then the request "axios.post('api', testData)" will result in a response, which contains the HTML code of the page "api". I can then extract the JSON "body" from the HTML.
I find the final step as suboptimal, but I have no idea, how can I send just the JSON and not the whole page. But I suppose, that there must be a much better way to get the data to the client.
There are two possible solutions:
Proxy (see: https://nuxtjs.org/faq/http-proxy)
API (see: https://medium.com/#johnryancottam/running-nuxt-in-parallel-with-express-ffbd1feef83c)
Ad 1. Proxy
The configuration of the proxy can look like this:
nuxt.config.js
module.exports = {
...
modules: [
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
proxy: {
'/proxy/packagist-search/': {
target: 'https://packagist.org',
pathRewrite: {
'^/proxy/packagist-search/': '/search.json?q='
},
changeOrigin: true
}
},
...
}
The request over proxy can look like this:
axios
.get('/proxy/packagist-search/' + this.search.phpLibrary.searchPhrase)
.then((response) => {
console.log(
'Could get the values packagist.org',
response.data
)
}
})
.catch((e) => {
console.log(
'Could not get the values from packagist.org',
e
)
})
Ad 2. API
Select Express as the project’s server-side framework, when creating the new Nuxt.js app.
server/index.js
...
app.post('/api/confluence', confluence.send)
app.use(nuxt.render)
...
server/confluence.js (simplified)
const axios = require('axios')
const config = require('../nuxt.config.js')
exports.send = function(req, res) {
let body = ''
let page = {}
req.on('data', (data) => {
body += data
})
req.on('end', async () => {
const parsedBody = JSON.parse(body)
try {
page = await axios.get(
config.api.confluence.url.api + ...,
config.api.confluence.auth
)
} catch (e) {
console.log('ERROR: ', e)
}
}
res.json({
page
})
}
The request over API can look like this:
this.$axios
.post('api/confluence', postData)
.then((response) => {
console.log('Wiki response: ', response.data)
})
.catch((e) => {
console.log('Could not update the wiki page. ', e)
})
Now with nuxtjs3 :
nuxtjs3 rc release
you have fetch or useFetch no need to import axios or other libs, what is great, automatic parsing of body, automatic detection of head
fetching data
you have middleware and server api on same application, you can add headers on queries, hide for example token etc
server layer
a quick example here in vue file i call server api :
const { status } = await $fetch.raw( '/api/newsletter', { method: "POST", body: this.form.email } )
.then( (response) => ({
status: response.status,
}) )
.catch( (error) => ({
status: error?.response?.status || 500,
}) );
it will call a method on my server, to init the server on root directory i created a folder name server then api, and a file name newsletter.ts (i use typescript)
then in this file :
export default defineEventHandler(async (event) => {
const {REST_API, MAILINGLIST_UNID, MAILINGLIST_TOKEN} = useRuntimeConfig();
const subscriber = await readBody(event);
console.log("url used for rest call" + REST_API);
console.log("token" + MAILINGLIST_TOKEN);
console.log("mailing list unid" + MAILINGLIST_UNID);
let recipientWebDTO = {
email: subscriber,
subscriptions: [{
"mailingListUnid": MAILINGLIST_UNID
}]
};
const {status} = await $fetch.raw(REST_API, {
method: "POST",
body: recipientWebDTO,
headers: {
Authorization: MAILINGLIST_TOKEN,
},
}).then((response) => ({
status: response.status,
}))
.catch((error) => ({
status: error?.response?.status || 500,
}));
event.res.statusCode = status;
return "";
})
What are the benefits ?
REST_API,MAILING_LIST_UNID, MAILING_LIST_TOKEN are not exposed on
client and even file newsletter.ts is not available on debug browser.
You can add log only on server side You event not expose api url to avoid some attacks
You don't have to create a new backend just to hide some criticals token or datas
then it is up to you to choose middleware route or server api. You don't have to import new libs, h3 is embedded via nitro with nuxtjs3 and fetch with vuejs3
for proxy you have also sendProxy offered by h3 : sendProxy H3
When you build in dev server and client build in same time(and nothing to implement or configure in config file), and with build to o, just don deploy your project in static way (but i think you can deploy front in static and server in node i don't know)
I need to POST a large payload in a GraphQL mutation. How do I increase the body size limit of Apollo Server?
I'm using apollo-server-express version 2.9.3.
My code (simplified):
const myGraphQLSchema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
user: UserQuery,
},
}),
mutation: new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
...UserMutations,
}),
}),
});
const apolloServer = new ApolloServer(schema: myGraphQLSchema);
const app = express();
app.use(apolloServer.getMiddleware({ path: '/graphql' });
Not exactly sure in which version it was added, but on 2.9.15 you can apply it in applyMiddleware function.
const apolloServer = new ApolloServer(someConfig);
apolloServer.applyMiddleware({
app,
cors: {
origin: true,
credentials: true,
},
bodyParserConfig: {
limit:"10mb"
}
});
Simply add an Express body parser before your Apollo server middleware:
import { json } from 'express';
app.use(json({ limit: '2mb' });
app.use(apolloServer.getMiddleware({ path: '/graphql' });
If you want to get fancy, you can have a separate body size limit for authenticated vs unauthenticated requests:
const jsonParsers = [
json({ limit: '16kb' }),
json({ limit: '2mb' }),
];
function parseJsonSmart(req: Request, res: Response, next: NextFunction) {
// How exactly you do auth depends on your app
const isAuthenticated = req.context.isAuthenticated();
return jsonParsers[isAuthenticated ? 1 : 0](req, res, next);
}
app.use(parseJsonSmart);
app.use(apolloServer.getMiddleware({ path: '/graphql' });
My frontend is localhost:3000, and my GraphQL server is localhost:3333.
I've used react-apollo to query/mutate in JSX land, but haven't made a query/mutation from Express yet.
I'd like to make the query/mutation here in my server.js.
server.get('/auth/github/callback', (req, res) => {
// send GraphQL mutation to add new user
});
Below seems like the right direction, but I'm getting TypeError: ApolloClient is not a constructor:
const express = require('express');
const next = require('next');
const ApolloClient = require('apollo-boost');
const gql = require('graphql-tag');
// setup
const client = new ApolloClient({
uri: 'http://localhost:3333/graphql'
});
const app = next({dev});
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
server.get('/auth/github/callback', (req, res) => {
// GraphQL mutation
client.query({
query: gql`
mutation ADD_GITHUB_USER {
signInUpGithub(
email: "email#address.com"
githubAccount: "githubusername"
githubToken: "89qwrui234nf0"
) {
id
email
githubToken
githubAccount
}
}
`,
})
.then(data => console.log(data))
.catch(error => console.error(error));
});
server.listen(3333, err => {
if (err) throw err;
console.log(`Ready on http://localhost:3333`);
});
})
.catch(ex => {
console.error(ex.stack);
process.exit(1);
});
This post mentions Apollo as the solution, but doesn't give an example.
How do I call a GraphQL mutation from Express server :3000 to GraphQL :3333?
This is more likely to be what you're looking for:
const { createApolloFetch } = require('apollo-fetch');
const fetch = createApolloFetch({
uri: 'https://1jzxrj179.lp.gql.zone/graphql',
});
// Example # 01
fetch({
query: '{ posts { title } }',
}).then(res => {
console.log(res.data);
});
// Example # 02
// You can also easily pass variables for dynamic arguments
fetch({
query: `
query PostsForAuthor($id: Int!) {
author(id: $id) {
firstName
posts {
title
votes
}
}
}
`,
variables: { id: 1 },
}).then(res => {
console.log(res.data);
});
Taken from this post, might be helpful to others as well: https://www.apollographql.com/blog/graphql/examples/4-simple-ways-to-call-a-graphql-api/
You can use graphql-request, it is a simple GraphQL client.
const { request } = require('graphql-request');
request('http://localhost:3333/graphql', `mutation ADD_USER($email: String!, $password: String!) {
createUser(email: $email, password: $password) {
id
email
}
}`, {email: 'john.doe#mail.com', password: 'Pa$$w0rd'})
.then(data => console.info(data))
.catch(error => console.error(error));
It also support CORS.
const { GraphQLClient } = require('graphql-request');
const endpoint = 'http://localhost:3333/graphql';
const client = new GraphQLClient(endpoint, {
credentials: 'include',
mode: 'cors'
});
client.request(`mutation ADD_USER($email: String!, $password: String!) {
createUser(email: $email, password: $password) {
id
email
}
}`, {email: 'john.doe#mail.com', password: 'Pa$$w0rd'})
.then(data => console.info(data))
.catch(error => console.error(error));
I use it to make E2E tests.
As you are getting ApolloClient with require instead of import I think you are missing this part:
// es5 or Node.js
const Boost = require('apollo-boost');
const ApolloClient = Boost.DefaultClient;
or
const ApolloBoost = require('apollo-boost');
const ApolloClient = ApolloBoost.default;
Try one of those and see if it works.
I'd like to add one more way to query from express.
This is what I ended up with.
install required packages
npm install graphql graphql-tag isomorphic-fetch
write graphql on separate file (myQuery.js)
const gql = require('graphql-tag');
const query = gql`
query($foo: String) {
// Graphql query
}
}
Main file
const { print } = require('graphql/language/printer');
const query = require('./myQuery');
require('isomorphic-fetch');
// other logic
const foo = "bar"
const token = "abcdef"
await fetch('https://example.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': `Bearer ${token}`,
},
body: JSON.stringify({
query: `${print(query)}`,
variables: { foo },
}),
})