Insert data from javascript file into mongodb using graphql - javascript

I am new to using Graphql and MongoDB. I am trying to insert data from an existing javascript file where the data has been defined. I was trying to use a mutation in order to achieve this but I have no clue what I'm really doing. Any help would be nice.
const dotenv = require('dotenv');
dotenv.config();
const { ApolloServer, gql } = require('apollo-server');
const { MongoClient } = require('mongodb');
const items = require('./itemsListData');
const typeDefs = gql`
type Query {
items:[Item!]!
}
type Item{
id:ID!,
name:String!,
aisle:String!,
bay:String!,
price:Float!,
xVal:Int!,
yVal:Int!
}
type Mutation {
createItem(name: String!, aisle: String!): Item!
}
`;
console.log(items)
const resolvers = {
Query: {
items:() => items,
},
Item:{
id: ( { _id, id }) => _id || id,
},
Mutation: {
createItem: async(_, { name }, { db }) => {
// name:String!, bays:[Bay!]!, xStartVal:Int!, xEndVal:Int!, yStartVal:Int!, yEndVal:Int!
const newItem = {
items
}
// insert Item object into database
const result = await db.collection('Items').insert(newItem);
console.log("This is the result " + result);
return result.ops[0]; // first item in array is the item we just added
}
}
};
const start = async () => {
const client = new MongoClient("mongodb+srv://admin:admin#quickkartcluster.o0bsfej.mongodb.net/test", { useNewUrlParser: true, useUnifiedTopology: true });
await client.connect();
const db = client.db("QuickKart");
const context = {
db,
}
const server = new ApolloServer({
typeDefs,
resolvers,
context,
introspection: true
});
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
}
start();
here is my javascript data file
https://pastebin.com/wvGANBgR

Related

Couldn't connect to mongodb database to make graphql queries

My graphql queries work with the local DB file but while trying to connect to MongoDB it doesn't work as expected.
mongo.js
require("dotenv").config();
const mongoose = require("mongoose");
const MONGODB_URI = process.env.MONGODB_URI;
if (!MONGODB_URI) {
throw new Error(
"Please define the MONGODB_URI environment variable inside .env.local"
);
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections from growing exponentially
* during API Route usage.
*/
let cached = global.mongoose;
if (!cached) {
cached = global.mongoose = { conn: null, promise: null };
}
async function dbConnect() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
bufferCommands: false,
};
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose;
});
}
cached.conn = await cached.promise;
return cached.conn;
}
module.exports = dbConnect;
MONGODB_URI=mongodb+srv://xxxx:xxxx#cluster0.yxbw7.mongodb.net/?retryWrites=true&w=majority
graphql query to get all the products:
exports.typeDefs = gql`
type Query {
products: [Product!]!
}
type Product {
id: ID!
name: String!
description: String!
price: Float!
image: String!
}
`;
Resolvers:
const { Product } = require("../models/Product");
exports.Query = {
products: async (parent, args, context) => {
let products = await Product.find({}).exec();
return products;
},
};
The DB has data in it but still making the query,
query{
products {
description
id
name
price
image
}
}
It returns an empty product array,
{
"data": {
"products": []
}
}
Something seems wrong with MongoDB to connect with graphql, the queries work with the local DB file which has a dummy object product data.

Access token missing from Keycloak context

I am trying to make an authenticated request from postman to my node, apollo, express backend. I am gettting an error saying that the user is unauthenticated. When I look at the context object, there is no access token and calling context.kauth.isAuthenticated() returns false.
Looking at the access token, I can see that accessToken is indeed blank, but there does exist the Bearer Token in the request header.
So I am not sure why the access token is not being included.
I am making the request from postman, I am including the token in the request like so:
In order to get this access token, I am first making a postman request to Keycloak to generate this token like so (note that I am intentionally not showing my username and password for this post
I am using the above access token in my postman request above.
This is what my index.js file looks like:
require("dotenv").config();
import { ApolloServer } from "apollo-server-express";
import { ApolloServerPluginDrainHttpServer } from "apollo-server-core";
const { makeExecutableSchema } = require('#graphql-tools/schema');
import { configureKeycloak } from "./auth/config"
import {
KeycloakContext,
KeycloakTypeDefs,
KeycloakSchemaDirectives,
} from "keycloak-connect-graphql";
import { applyDirectiveTransformers } from "./auth/transformers";
import express from "express";
import http from "http";
import typeDefs from "./graphql/typeDefs";
import resolvers from "./graphql/resolvers";
import { MongoClient } from "mongodb";
import MongoHelpers from "./dataSources/MongoHelpers";
async function startApolloServer(typeDefs, resolvers) {
const client = new MongoClient(process.env.MONGO_URI);
client.connect();
let schema = makeExecutableSchema({
typeDefs: [KeycloakTypeDefs, typeDefs],
resolvers
});
schema = applyDirectiveTransformers(schema);
const app = express();
const httpServer = http.createServer(app);
const { keycloak } = configureKeycloak(app, '/graphql')
const server = new ApolloServer({
schema,
schemaDirectives: KeycloakSchemaDirectives,
resolvers,
context: ({ req }) => {
return {
kauth: new KeycloakContext({ req }, keycloak)
}
},
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
server.applyMiddleware({ app });
await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
}
startApolloServer(typeDefs, resolvers);
And this is my keycloak.json file:
I am really quite stummped, my initial thought is that I am not making the reqest from postman correctly. Am grateful for any guidance
Requirements:
use node, apollo, express to get keycloak Authentication and Authorization based on the keycloak-connect middleware
using Postman to make an authenticated call with a Bearer token.
index.js in the question is not a minimal, reproducible example because, for example, the parts in typeDefs, ./auth/transformers and so on are missing.
There is a cool description at https://github.com/aerogear/keycloak-connect-graphql with nice example code.
So if one changes your approach only slightly (e.g. mongodb is not needed) and then adds the also slightly changed code from the description of the Github page accordingly, one can get a standalone running index.js.
For example, it might look something like this:
"use strict";
const {ApolloServer, gql} = require("apollo-server-express")
const {ApolloServerPluginDrainHttpServer} = require("apollo-server-core")
const {makeExecutableSchema} = require('#graphql-tools/schema');
const {getDirective, MapperKind, mapSchema} = require('#graphql-tools/utils')
const {KeycloakContext, KeycloakTypeDefs, auth, hasRole, hasPermission} = require("keycloak-connect-graphql")
const {defaultFieldResolver} = require("graphql");
const express = require("express")
const http = require("http")
const fs = require('fs');
const path = require('path');
const session = require('express-session');
const Keycloak = require('keycloak-connect');
function configureKeycloak(app, graphqlPath) {
const keycloakConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'config/keycloak.json')));
const memoryStore = new session.MemoryStore();
app.use(session({
secret: process.env.SESSION_SECRET_STRING || 'this should be a long secret',
resave: false,
saveUninitialized: true,
store: memoryStore
}));
const keycloak = new Keycloak({
store: memoryStore
}, keycloakConfig);
// Install general keycloak middleware
app.use(keycloak.middleware({
admin: graphqlPath
}));
// Protect the main route for all graphql services
// Disable unauthenticated access
app.use(graphqlPath, keycloak.middleware());
return {keycloak};
}
const authDirectiveTransformer = (schema, directiveName = 'auth') => {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const authDirective = getDirective(schema, fieldConfig, directiveName)?.[0];
if (authDirective) {
const {resolve = defaultFieldResolver} = fieldConfig;
fieldConfig.resolve = auth(resolve);
}
return fieldConfig;
}
})
}
const directive = (keys, key, directive, directiveName) => {
if (keys.length === 1 && keys[0] === key) {
let dirs = directive[keys[0]];
if (typeof dirs === 'string') dirs = [dirs];
if (Array.isArray(dirs)) {
return dirs.map((val) => String(val));
} else {
throw new Error(`invalid ${directiveName} args. ${key} must be a String or an Array of Strings`);
}
} else {
throw Error(`invalid ${directiveName} args. must contain only a ${key} argument`);
}
}
const permissionDirectiveTransformer = (schema, directiveName = 'hasPermission') => {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const permissionDirective = getDirective(schema, fieldConfig, directiveName)?.[0];
if (permissionDirective) {
const {resolve = defaultFieldResolver} = fieldConfig;
const keys = Object.keys(permissionDirective);
let resources = directive(keys, 'resources', permissionDirective, directiveName);
fieldConfig.resolve = hasPermission(resources)(resolve);
}
return fieldConfig;
}
})
}
const roleDirectiveTransformer = (schema, directiveName = 'hasRole') => {
return mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const roleDirective = getDirective(schema, fieldConfig, directiveName)?.[0];
if (roleDirective) {
const {resolve = defaultFieldResolver} = fieldConfig;
const keys = Object.keys(roleDirective);
let role = directive(keys, 'role', roleDirective, directiveName);
fieldConfig.resolve = hasRole(role)(resolve);
}
return fieldConfig;
}
})
}
const applyDirectiveTransformers = (schema) => {
return authDirectiveTransformer(roleDirectiveTransformer(permissionDirectiveTransformer(schema)));
}
const typeDefs = gql`
type Query {
hello: String #hasRole(role: "developer")
}
`
const resolvers = {
Query: {
hello: (obj, args, context, info) => {
console.log(context.kauth)
console.log(context.kauth.isAuthenticated())
console.log(context.kauth.accessToken.content.preferred_username)
const name = context.kauth.accessToken.content.preferred_username || 'world'
return `Hello ${name}`
}
}
}
async function startApolloServer(typeDefs, resolvers) {
let schema = makeExecutableSchema({
typeDefs: [KeycloakTypeDefs, typeDefs],
resolvers
});
schema = applyDirectiveTransformers(schema);
const app = express();
const httpServer = http.createServer(app);
const {keycloak} = configureKeycloak(app, '/graphql')
const server = new ApolloServer({
schema,
resolvers,
context: ({req}) => {
return {
kauth: new KeycloakContext({req}, keycloak)
}
},
plugins: [ApolloServerPluginDrainHttpServer({httpServer})],
});
await server.start();
server.applyMiddleware({app});
await new Promise((resolve) => httpServer.listen({port: 4000}));
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
}
startApolloServer(typeDefs, resolvers);
The corresponding package.json:
{
"dependencies": {
"#graphql-tools/schema": "^8.3.10",
"#graphql-tools/utils": "^8.6.9",
"apollo-server-core": "^3.6.7",
"apollo-server-express": "^3.6.7",
"express": "^4.17.3",
"express-session": "^1.17.2",
"graphql": "^15.8.0",
"graphql-tools": "^8.2.8",
"http": "^0.0.1-security",
"keycloak-connect": "^18.0.0",
"keycloak-connect-graphql": "^0.7.0"
}
}
Call With Postman
As one can see, the authenticated call is then successful. Also, with the above code, the accessToken is logged correctly to the debug console:
This is certainly not the functionality that exactly meets your requirements. But you may be able to gradually make the desired/necessary changes from this running example depending on your requirements.

Apollo GraphQL - TypeError: Cannot read property 'findOrCreateUser' of undefined

I'm following this tutorial https://www.apollographql.com/docs/tutorial/mutation-resolvers/#book-trips
and have ended up getting this error:
"TypeError: Cannot read property 'findOrCreateUser' of undefined",
" at login
Below is the function I use to call login:
mutation LoginUser {
login(email: "daisy#apollographql.com") {
token
}
}
The file below is where findOrCreateUser is called:
src/resolvers.js
module.exports = {
Mutation: {
login: async (_, {email}, {dataSources}) => {
const user = await dataSources.userAPI.findOrCreateUser({ email });
if (user) {
user.token = Buffer.from(email).toString('base64');
return user;
}
},
},
This is where dataSources is defined:
src/index.js
require('dotenv').config();
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const { createStore } = require('./utils');
const resolvers = require('./resolvers');
const isEmail = require('isemail');
const LaunchAPI = require('./datasources/launch');
const UserAPI = require('./datasources/user');
const store = createStore();
const server = new ApolloServer({
context: async ({req}) => {
const auth = req.headers && req.headers.authorization || '';
const email = Buffer.from(auth, 'base64').toString('ascii');
if (!isEmail.validate(email)) return {user: null};
// find a user by their email
const users = await store.user.findOrCreate({ where: { email } });
const user = users && users[0] || null;
return { user: {...user.dataValues } };
},
dataSources: () => ({
launchAPI: new LaunchAPI(),
UserAPI: new UserAPI({store})
}),
typeDefs,
resolvers,
});
server.listen().then(() => {
console.log(`
Server is running!
Listening on port 4000
Explore at https://studio.apollographql.com/dev
`)
});
As xadm mentioned in the comment the issues is your usage of userAPI vs UserAPI. the datasource object is created in index.js in the following block
dataSources: () => ({
launchAPI: new LaunchAPI(),
UserAPI: new UserAPI({store})
}),
here you defined datasources.UserAPI however in resolvers.js you refer to it as datasources.userAPI (note the difference in capitalization of userAPI). Your problem can be resolved by changing the above code block to
dataSources: () => ({
launchAPI: new LaunchAPI(),
userAPI: new UserAPI({store})
}),

Can't get Graphql-tools to read my schema.graphql file

I am using Apollo-server-express and Graphql-tools. I have been all over the Graphql-tools documentation and I can't get this to work. I'm trying to get my schema.graphql file to import as my typeDefs. It seems like Graphql-tools should be making this easy, but something isn't falling into place.
index.js
const { ApolloServer } = require("apollo-server-express");
const { makeExecutableSchema } = require('#graphql-tools/schema');
const express = require("express");
const { join } = require("path");
const { loadSchema } = require("#graphql-tools/load");
const { GraphQLFileLoader } = require("#graphql-tools/graphql-file-loader");
const { addResolversToSchema } = require("#graphql-tools/schema");
const app = express();
const resolvers = {
Query: {
items: (parent, args, ctx, info) => {
return ctx.prisma.item.findMany();
},
},
Mutation: {
makeItem: (parent, args, context, info) => {
const newItem = context.prisma.item.create({
data: {
...args,
price: parseInt(Math.ceil(args.price * 100)),
},
});
return newItem;
},
deleteItem: (parent, args, context, info) => {
return context.prisma.item.delete({
where: {
id: args.id,
},
});
},
},
};
const schemaSource = loadSchemaSync(join(__dirname, "schema.graphql"), {
loaders: [new GraphQLFileLoader()],
});
const schema = makeExecutableSchema({
typeDefs: schemaSource,
resolvers,
});
const server = new ApolloServer({
schema,
resolvers,
});
server.applyMiddleware({ app });
app.listen(
{ port: 4000 },
() =>
console.log(
`🌎 => Backend server is now running on port http://localhost:4000`
)
);
schema.graphql
type Query {
items: [Item!]!
}
type Mutation {
makeItem(
piece: String!
image: String!
identifier: String!
price: Float!
itemNumber: Int!
): Item!
deleteItem(id: ID!): Item!
}
type Item {
id: ID!
piece: String!
image: String!
identifier: String!
price: Int!
itemNumber: Int!
}
In its current state I am getting an error that says: "Error: typeDefs must be a string, array or schema AST, got object"
As I understand it makeExecutableSchema should be doing all the necessary steps, like changing the schema into a string. I can't seem to figure out what is going on here and any help would be greatly appreciated.
loadSchemaSync will load a GraphQLSchema object using the provided pointer. You should use loadTypedefsSync instead.
const sources = loadTypedefsSync(join(__dirname, "schema.graphql"), {
loaders: [new GraphQLFileLoader()],
});
const typeDefs = sources.map(source => source.document)
const server = new ApolloServer({ typeDefs, resolvers })
If you want to use loadSchema, you don't need to use makeExecutableSchema because your schema has already been created. So you would do this instead as shown in the docs:
const schema = loadSchemaSync(join(__dirname, "schema.graphql"), {
loaders: [new GraphQLFileLoader()],
});
const resolvers = {...};
const schemaWithResolvers = addResolversToSchema({
schema,
resolvers,
});
const server = new ApolloServer({ schema: schemaWithResolvers })

GraphQL server with Deno

It works just once for the below code
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
buildSchema,
} from "https://cdn.pika.dev/graphql/^15.0.0";
import { serve } from "https://deno.land/std#0.50.0/http/server.ts";
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "RootQueryType",
fields: {
hello: {
type: GraphQLString,
resolve() {
return "world";
},
},
},
}),
});
var query = "{ hello }";
graphql(schema, query).then((result) => {
console.log(result);
});
How to keep it listening, just like express
Something like this
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
var root = {
hello: () => {
return 'Hello world!';
},
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');
import {
graphql,
buildSchema,
} from "https://cdn.pika.dev/graphql/^15.0.0";
import {Application, Router} from "https://deno.land/x/oak/mod.ts";
var schema = buildSchema(`
type Query {
hello: String
}
`);
var resolver = {hello: () => 'Hello world!'}
const executeSchema = async (query:any) => {
const result = await graphql(schema, query, resolver);
return result;
}
var router = new Router();
router.post("/graph", async ({request, response}) => {
if(request.hasBody) {
const body = await request.body();
const result = await executeSchema(body.value);
response.body = result;
} else {
response.body = "Query Unknown";
}
})
let app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
console.log("Server running");
app.listen({port: 5000})
You can now use https://deno.land/x/deno_graphql to achieve this goal.
It provides everything needed out-of-the-box and works with multiple Deno frameworks (oak, abc, attain, etc).
This is how you code looks like (with oak for example):
import { Application, Context, Router } from "https://deno.land/x/oak/mod.ts";
import {
gql,
graphqlHttp,
makeExecutableSchema,
} from "https://deno.land/x/deno_graphql/oak.ts";
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => "Hello world!",
},
};
const context = (context: Context) => ({
request: context.request,
});
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = new Application();
const router = new Router();
router.post("/graphql", graphqlHttp({ schema, context }));
app.use(router.routes());
await app.listen({ port: 4000 });
PS : i'm the author of the package, so you can ask me anything.
Hope this helps!
Here is an example using oak working with your GraphQL code.
First let's say you have a repository graphRepository.ts with your graph schema:
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} from "https://cdn.pika.dev/graphql/^15.0.0";
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "RootQueryType",
fields: {
hello: {
type: GraphQLString,
resolve() {
return "world";
},
},
},
}),
});
export async function querySchema(query: any) {
return await graphql(schema, query)
.then(async (result) => {
return result;
});
}
Now start your app.ts listener with the routes, and use the following URL to call the endpoint:
http://localhost:8000/graph/query/hello
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import { querySchema } from "./graphRepository.ts";
const router = new Router();
router
.get("/graph/query/:value", async (context) => {
const queryValue: any = context.params.value;
const query = `{ ${queryValue}}`
const result = await querySchema(query);
console.log(result)
context.response.body = result;
})
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 8000 });
here is a code example using oak and middleware.
You also can enjoy the playground GUI like an apollo one.
import { Application } from "https://deno.land/x/oak/mod.ts";
import { applyGraphQL, gql } from "https://deno.land/x/oak_graphql/mod.ts";
const app = new Application();
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.headers.get("X-Response-Time");
console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
const types = gql`
type User {
firstName: String
lastName: String
}
input UserInput {
firstName: String
lastName: String
}
type ResolveType {
done: Boolean
}
type Query {
getUser(id: String): User
}
type Mutation {
setUser(input: UserInput!): ResolveType!
}
`;
const resolvers = {
Query: {
getUser: (parent: any, {id}: any, context: any, info: any) => {
console.log("id", id, context);
return {
firstName: "wooseok",
lastName: "lee",
};
},
},
Mutation: {
setUser: (parent: any, {firstName, lastName}: any, context: any, info: any) => {
console.log("input:", firstName, lastName);
return {
done: true,
};
},
},
};
const GraphQLService = applyGraphQL({
typeDefs: types,
resolvers: resolvers
})
app.use(GraphQLService.routes(), GraphQLService.allowedMethods());
console.log("Server start at http://localhost:8080");
await app.listen({ port: 8080 });
I have created gql for making GraphQL servers that aren't tied to a web framework. All of the responses above show Oak integration but you don't really have to use it to have a GraphQL server. You can go with std/http instead:
import { serve } from 'https://deno.land/std#0.90.0/http/server.ts'
import { GraphQLHTTP } from 'https://deno.land/x/gql/mod.ts'
import { makeExecutableSchema } from 'https://deno.land/x/graphql_tools/mod.ts'
import { gql } from 'https://deno.land/x/graphql_tag/mod.ts'
const typeDefs = gql`
type Query {
hello: String
}
`
const resolvers = {
Query: {
hello: () => `Hello World!`
}
}
const schema = makeExecutableSchema({ resolvers, typeDefs })
const s = serve({ port: 3000 })
for await (const req of s) {
req.url.startsWith('/graphql')
? await GraphQLHTTP({
schema,
graphiql: true
})(req)
: req.respond({
status: 404
})
}

Categories

Resources