I'm just getting started with GraphQL and I've worked mostly with featherjs to build REST APIs so far. There's the concept of hooks to run code before or after interacting with a service. I wanted to know if there's a way to achieve something similar in a graphql setup.
The specific stack I'm using is neo4j and graphql, express, and apollo server. Since neo4j has a graphql module, I'm not writing the general queries and mutations, just the model types.
For a concrete example, below is a basic API server. It's put together using docker compose and has users and applications. I would like to in general be able to run arbitrary code before or after a mutation with access to the model being modified. Things like more complex validation before, or side effects after.
Those things could be here, for example, before changing the state of an application to declined, make sure that a note is added. Or after changing the state to approved, send an email to the applicant.
I'm not sure how or where to implement this kind of thing, any help is appreciated! Or also if I'm thinking about graphql wrong and there's a better way of doing what I'm trying to do.
Thanks!
import http from 'http';
import express from 'express';
import neo4j from 'neo4j-driver';
import {Neo4jGraphQL} from '#neo4j/graphql';
import {gql, ApolloServer} from 'apollo-server-express';
import {ApolloServerPluginDrainHttpServer} from 'apollo-server-core';
const typeDefs=gql`
type User {
email: String!
applications: [Application!] #relationship(type: "APPLICANT", direction: OUT)
}
type Application {
user: User! #relationship(type: "APPLICANT", direction: IN)
state: String!
note: String
}
`;
const driver=neo4j.driver(
'neo4j://neo4j:7687',
neo4j.auth.basic('neo4j', 'password')
);
const {schema}=new Neo4jGraphQL({typeDefs, driver});
(async ()=>{
const app=express();
const server=http.createServer(app);
const apollo=new ApolloServer({
schema,
plugins: [ApolloServerPluginDrainHttpServer({httpServer: server})]
});
await apollo.start();
apollo.applyMiddleware({app});
await new Promise(r=>server.listen({port: 4000}, r));
console.log('GraphQL server listening at '+apollo.graphqlPath);
})();
Related
is anyway to use walletconnect with etherejs ?
the demos are nice but they are with wagmi
i cant port all project from etherjs to wagmi
i need this feature this button connect disconect and possibility to use only few networks/chainids
import { arbitrum, mainnet, polygon } from "wagmi/chains";
https://docs.walletconnect.com/2.0/web3modal/react/installation
i this this exactly but with ETHERSJS
const { provider } = configureChains(chains, [
walletConnectProvider({ projectId: "<YOUR_PROJECT_ID>" }),
]);
const wagmiClient = createClient({
autoConnect: true,
connectors: modalConnectors({ appName: "web3Modal", chains }),
provider,
});
// Web3Modal Ethereum Client
const ethereumClient = new EthereumClient(wagmiClient, chains);
I found official example for WalletConnect v2 integration with Ethers.js
See https://github.com/WalletConnect/web-examples/blob/main/dapps/react-dapp-v2-with-ethers/src/contexts/ClientContext.tsx
Bad thing is that it's more complex than one with Wagmi. You have to connect lot of WalletConnect events to keep session info up to date, or to reset connection state.
Also official example (ClientContext.tsx) is IMO overusing React state which is not ideal. Would be nice to have official Ethers wrapper (not React, or other UI library dependent).
But it is definitely useful example to make Ethers integration work.
I'm trying to follow some code to test out instantiating a wallet in code. I'm hardcoding private key (will not do this in prod obviously) just to see how everything works. I'm getting this error:
throw new Error('bad secret key size');
^
Error: bad secret key size
My code is as below:
import { Connection, Keypair, Transaction } from '#solana/web3.js'
import fetch from 'cross-fetch'
import { Wallet } from '#project-serum/anchor'
import bs58 from 'bs58'
const connection = new Connection('https://ssc-dao.genesysgo.net')
const PRIVATE_KEY = 'my secret key is this very dumb long confusing and unnecessary string'
const wallet = new Wallet(Keypair.fromSecretKey(bs58.decode(process.env.PRIVATE_KEY || '')))
I suspect it's either:
PRIVATE_KEY is not loaded properly: your code is missing require("dotenv").config(); at the top.
PRIVATE_KEY is not in the right format in your .env file: from where and how did you export it? Are you sure it's base58 encoded?
So the issue here was the encoding. For ease of use, it's best to use the phantom wallet. I tried converting the seed to bs58 but I was still unsuccessful. If you insist on using another wallet and are using the .js web3 library for Solana, you'd probably be better off using
const wallet = new Wallet(Keypair.fromSecretKey(new Uint8Array([1,2,3,4,5,6])))
but this would require you to keep your private key array exposed which is not recommended.
I'm trying to connect Nuxt 2.14 with firebase firestore
I'm writing a plugin for firebase that runs on the server
I'm using "inject" to pass firebase.firestore() object as db.
But, when I'm using the $db I just injected
and try to make a data variable equal to $db via asyncData,
I get an error:
"Maximum call stack size exceeded"
I guess the firebase.firestore() object has a "circular structure"
Any suggestions?
Should I approach this differently?
Thank you
Here is the code for the different files I'm using:
.env
FB_DB_PATH=XXXXXX
FB_API_KEY=YYYYYY
FB_PROJECT_ID=ZZZZZZ
FB_AUTH_DOMAIN=EEEEEEE
nuxt.config.js
plugins: [
{ src: '~plugins/firebase.js', mode: 'server' },
// runs on server to get the firebase privateRuntimeConfig params
],
publicRuntimeConfig: {
baseURL: process.env.BASE_URL // just for test
},
privateRuntimeConfig: {
fbProjectID: process.env.FB_PROJECT_ID,
fbDBPath: process.env.FB_DB_PATH,
fbApiKey: process.env.FB_API_KEY,
fbAuthDomain: process.env.FB_AUTH_DOMAIN
},
firebase.js - this is the plugin file
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
export default function (context, inject) {
if (!firebase.apps.length) {
firebase.initializeApp({
apiKey: context.$config.fbApiKey,
authDomain: context.$config.fbAuthDomain,
//databaseURL: context.$config.fbDBPath,
projectId: context.$config.fbProjectID
})
}
let auth = firebase.auth();
let db = firebase.firestore();
inject('db', db);
}
in page code:
asyncData ( context ) {
return { db: context.$db }
}
AFAIK, what you're trying to do won't work, and is not secure anyways. Injecting your Firebase auth and firestore will inherently expose the API key client side, because they still need to use it there to communicate with the Firebase.
Further, passing back an object with circular references doesn't work, as Vue cannot serialize it properly, and wouldn't know how to deserialize it on the client either.
If you're just fetching data from Firebase at page load, then you should do so in the asyndData method, and return the fetched data. However, if you need to make calls during runtime, such as storing user data, then you will need an API to keep the keys secure. I usually recommend starting with Nuxts serverMiddleware, as it works out of the box on Nuxt projects.
I'm trying to create a basic chat-like app just for the sake of learning a few things.
I have set up a basic graphql server to handle connecting a user, and let them add a message. Now I'm trying to add some mechanism so that every user can see each others' messages added in real time. I'm new to GraphQL but it would seem subscriptions are what I should use.
Here's my server index.ts:
import { createServer } from 'http';
import jwt from 'jsonwebtoken';
import mongoose from 'mongoose';
import resolvers from 'modules/resolvers';
import typeDefs from 'modules/type-defs';
import { ApolloServer } from 'apollo-server-express';
import cookieParser from 'cookie-parser';
import express from 'express';
import cors from 'cors';
const PORT = 4000;
const getUser = (token: string) => {
// ...
};
const server = new ApolloServer({
context: ({ req, res }) => {
const token = req.cookies.jwt;
const currentUser = getUser(token);
return { currentUser, req, res };
},
resolvers,
subscriptions: {
onConnect: async (connectionParams, webSocket, context) => {
console.log(`Subscription client connected using Apollo server's built-in SubscriptionServer.`)
},
onDisconnect: async (webSocket, context) => {
console.log(`Subscription client disconnected.`)
},
},
typeDefs,
});
const app = express();
const httpServer = createServer(app);
app.use(cors({ credentials: true, origin: 'http://localhost:3000' }));
app.use(cookieParser());
server.applyMiddleware({ app, cors: false });
server.installSubscriptionHandlers(httpServer);
httpServer.listen(PORT, () => {
console.log(`Server ready at http://localhost:${PORT}${server.graphqlPath}`);
console.log(`Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`);
});
mongoose.connect('mongodb://127.0.0.1:27017/irc', {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
});
What I'm trying to do is set up something as simple as possible on the client side (javascript/react) without having to rely on a lib like apollo-client.
I managed to use simple fetch calls to send queries/mutations and was expecting to be able to use subscriptions in a "simple" way too. Apollo-client seems over complicated for what I'm trying to do and I'd like to understand how it actually works - but every tutorial on subscriptions seem to use this lib...
I don't really understand what my server is actually doing regarding subscriptions, and I thought I'd configured it to listen to websockets connections but I'm not so sure anymore.
I tried sending a basic message just to see what would happen:
const ws = new WebSocket('ws://localhost:4000/graphql');$
ws.onopen = event => {
ws.send('Lorem ipsum dolor sit amet.');
};
... but even though my chrome's network tab seems to indicate that everything went fine, my server does not seem to care about my message since nothing gets logged.
Could someone please explain if it's possible to use basic web sockets to use apollo-server's subscriptions? And how?
Thanks
Apollo Server and Apollo Client both use subscriptions-transport-ws under the hood to handle subscriptions. subscriptions-transport-ws uses WebSocket as a transport for exchanging messages between the server and the client. These messages have a specific format used by the library -- in order to use only WebSocket on the client-side, you'd have to send the same sort of messages.
You could inspect the source code to determine what sort of messages are being sent and when. The better option, though, would be to create an instance of SubscriptionClient and utilize its methods. Since usage of the library isn't particularly well documented, you'd have to stumble your way through but it should be possible.
If you're new to GraphQL, though, you should stick with Apollo Client since it's documentation is fairly good (see here) and subscriptions can be pretty complicated to set up, especially once you add authentication.
I'm having a number of issues putting together a very simple piece of code as I learn Meteor. See the comments, which are questions.
server/main.js
import { Meteor } from 'meteor/meteor';
import { Post } from './schema'
// Why is this required to make Post available in Meteor.startup?
// Isn't there auto-loading?
Meteor.startup(() => {
console.log(Post)
// This works, but why isn't Post available to meteor shell?
});
server/schema.js
import { Post } from './models/post'
export { Post }
server/models/post.js
import { Class } from 'meteor/jagi:astronomy';
// Why can't this be imported elsewhere, like main.js?
const Posts = new Mongo.Collection('posts');
const Post = Class.create({
name: 'Post',
collection: Posts,
fields: {
title: { type: String },
userId: String,
publishedAt: Date
},
});
export { Post }
In addition to these questions, how can I load my app into meteor shell? Post is undefined there, even though it's defined in Meteor.startup. I tried using .load with an absolute path, but this breaks my app's imports, which use relative paths.
As for which errors I'm confused about:
When I try and use import inside Meteor.startup(), I get an error that the keyword import is undefined. I'm using the ecmascript package.
When I don't import { Class } in the same file where I use Class, I get an unknown keyword error.
If I don't import { Post } in main.js, then Post is undefined.
Not able to load app into Meteor shell.
To access exported objects in Meteor shell, use require:
> require('server/schema.js').Posts.findOne();
To access objects exported by packages, use the package name:
> require('react').PropTypes;
The reason you can't access an object that's imported by another js file is that each file has its own scope here. When Meteor builds your app, it doesn't just concatenate js files like many other build systems do, and that's really a good thing.
Basically a Javascript object gets created for every js file you write. Anything you export in a js file becomes a field in this object which you can access by using require. And import is just a nicer version of the same thing.