I'm trying to figure out how to open the apollo studio for my next js app.
When I run the local host, I get a message that says:
Server started at http://localhost:5555/graphql
When I click that link, I get a page that says:
GET query missing.
I'm trying to find a way to get to the apollo studio explorer.
For others looking (or maybe for myself the next time I forget), the address: http://localhost:5555/graphql gets inserted in the sandbox address bar, that you find at the url: https://studio.apollographql.com/sandbox/explorer. It won't work if you put the local host address in the url bar
I faced the same issue and have managed to solve it by connecting to apollo studio as a deployed graph (not using the sandbox) but running locally.
Firstly I followed this tutorial https://master--apollo-docs-index.netlify.app/docs/tutorial/production/ which does not use NextJS but it does connect a react app to the apollo studio sandbox then by section 5 it connects the deployed graph to apollo studio. Unfortunately section 5 is quite outdated so i will try to fill in the blanks and get you up and running.
After you have set up an account in apollo studio add a new graph (+ New Graph button). Use whatever architecture you like but I tried this using 'supergraph'.
On the next page ('Publish your schema using Federation') I used the 'schema document' tab and pipeline: 'Federation 2 Supergraph'.This generates the 2 of the 3 env keys you need to add to your local env file and feed it into your app. keys as follows:
APOLLO_KEY - this starts 'service:' and ends before the space, it is a single line about 50 characters long.
APOLLO_GRAPH_REF - this can be found at the end of the line below the APOLLO_KEY. it is a single word with a '#' symbol in the middle.
APOLLO_SCHEMA_REPORTING=true - written as shown here.
Do not close the 'Publish your schema using Federation' page/ re-open it if you have closed it as it will indicate that you have successful connected the graph when you run the app locally after the next step.
Start the app locally using the CLI and in the browser request a page that queries the apollo server.
Watch the CLI as the page is served and you should see the comment 'Apollo usage reporting starting!', also the 'Publish your schema using Federation' page should confirm the graph has been connected. Now you can use all the features of the sandbox as well as monitoring etc.
Hope this helps.
The reason why Next.js doesn't allow you to connect to Apollo Studio is because Next.js does not allow CORS by default in api handlers.
Apollo Studio tries to send a request from its own domain and it's blocked by Next.js default setup.
Let's assume you have your graphql/Apollo server in your NextJs app at /api/graphql path. When you navigate to that path (from your local) by using http://localhost:3000/api/graphql it will show you the welcome page and allow you to access Apollo Sandbox.
Once you enter the Apollo Sandbox in the bottom right corner it will display this message:
When you run the diagnose problem on your local you'll see the following message:
$ npx diagnose-endpoint#1.1.0 --endpoint=http://localhost:3000/api/graphql
Diagnosing http://localhost:3000/api/graphql
⚠️ OPTIONS response is missing header 'access-control-allow-methods: POST'
⚠️ POST response missing 'access-control-allow-origin' header.
If using cookie-based authentication, the following headers are required from your endpoint:
access-control-allow-origin: https://studio.apollographql.com
access-control-allow-credentials: true
Otherwise, a wildcard value would work:
access-control-allow-origin: *
(📫 Interested in previewing a local tunnel to bypass CORS requirements? Please let us know at https://docs.google.com/forms/d/e/1FAIpQLScUCi3PdMerraiy6GpD-QiC_9KEKVHr4oDL5Vef5fIvzqqQWg/viewform )
The solution for the problem looks like this:
import type { NextApiRequest, NextApiResponse } from "next";
import Cors from "cors";
import { server } from "../../apollo";
// Initializing the cors middleware
// You can read here: https://github.com/expressjs/cors#configuration-options
const cors = Cors({
methods: ["POST"],
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(
req: NextApiRequest,
res: NextApiResponse,
fn: Function
) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export const config = {
api: {
bodyParser: false,
},
};
const startServer = server.start();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Run cors middleware (to allow Apollo Studio access)
await runMiddleware(req, res, cors);
// run apollo server
await startServer;
await server.createHandler({ path: "/api/graphql" })(req, res);
}
It combines using the Apollo server and this CORS example
the import:
import { server } from "../../apollo"
from the example above is the apollo server that looks like this:
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "./schema";
import { resolvers } from "./resolvers";
export const server = new ApolloServer({
typeDefs,
resolvers,
});
You can also use alternative options like embedding sandbox into your app but I'm finding the above solution a bit easier for my current needs so hope it helps you as well.
Related
I have an react frontend and a python backend (using ariadne==0.13.0, uvicorn==0.15.0, uvicorn[standard]==0.15.0, fastapi==0.68.1) communicationg over graphql subscriptions. Everything works fine as long as I do not reload the page or load the page in a new browser window from same IP. Then the page crashes and takes some time to recover - Depending on the websocket timeout configured in uvicorn. I am experiencing the same issue with both my frontend and the graphql playgorund.
I understand that the different browsers or tabs are identified with the same IP, Port and protocol what possibly messes up the existing connection, but still it should be possible the use the page from different tabs as seen in:
https://fastapi.tiangolo.com/advanced/websockets/
My code:
SCHEMA = load_schema_from_path("schema.graphql")
query = QueryType()
subscription = SubscriptionType()
app = FastAPI()
schema = make_executable_schema(SCHEMA, [query, subscription])
graphql_server = GraphQL(schema, debug=True)
app.add_route("/graphql", graphql_server)
app.add_websocket_route("/graphql", graphql_server)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["POST", "GET"],
allow_headers=[
"accept",
"accept-language",
"content-language",
"content-type",
"x-apollo-tracing",
],
)
app.debug = True
uvicorn.run(app, host='0.0.0.0', port=7996)
The default setup for uvicorn is single threaded and the method implementing the subscription was synchronous and blocking. I had to reimplement it in an async manner.
So I wanted to make a small project with a DB, Backend and mobile frontend. I have a mariadb database on a raspberry pi and have everything setup for connections from anywhere. I created my backend server with TypeORM and hosted it on heroku. The problem is that my heroku server has a dynamic ip and I want to only have a small amount of whitelisted IP's. So I added quotaguard to my heroku app. The problem is the only way to setup that proxy connection (from the quotaguard documentation) is through socksjs (once again from the documentation on quotaguard) which creates a SockConnection object. I know if I use mysql.createConnection() there's a stream option that allows me to pass in that object, but I don't see it in the createConnection function from TypeORM. I have a variable called sockConn and I have verified that the connection is made on the quotaguard dashboard, but I don't know how to add it as an option to the TypeORM createConnection function.
Here is the index.ts file from my project:
import "reflect-metadata";
import {createConnection, getConnectionManager} from "typeorm";
import express from "express";
import {Request, Response} from "express";
import {Routes} from "./routes";
import { DB_CONNECTION } from "./database";
import { MysqlConnectionOptions } from "typeorm/driver/mysql/MysqlConnectionOptions";
import { config } from 'dotenv';
import { parse } from 'url';
var SocksConnection = require('socksjs');
config().parsed
const setupConnection = () => {
DB_CONNECTION.username = process.env.DB_USER;
DB_CONNECTION.password = process.env.DB_PASS;
DB_CONNECTION.host = process.env.DB_HOST;
DB_CONNECTION.port = parseInt(process.env.DB_PORT);
DB_CONNECTION.debug = true;
// ---- this section from quotaguard documentation ---- //
var proxy = parse(process.env.QUOTAGUARDSTATIC_URL),
auth = proxy.auth,
username = auth.split(':')[0],
pass = auth.split(':')[1];
var sock_options = {
host: proxy.hostname,
port: 1080,
user: username,
pass: pass
};
var sockConn = new SocksConnection({host: DB_CONNECTION.host, port: DB_CONNECTION.port}, sock_options);
// ---- this section above from quotaguard documentation ---- //
}
setupConnection();
createConnection(DB_CONNECTION as MysqlConnectionOptions).then(async connection => {
// create express app
const app = express();
app.use(express.json());
// register express routes from defined application routes
Routes.forEach(route => {
(app as any)[route.method](route.route, (req: Request, res: Response, next: Function) => {
const result = (new (route.controller as any)())[route.action](req, res, next);
if (result instanceof Promise) {
result.then(result => result !== null && result !== undefined ? res.send(result) : undefined);
} else if (result !== null && result !== undefined) {
res.json(result);
}
});
});
// setup express app here
// start express server
app.listen(3000);
console.log("Express server has started on port 3000.
Open http://localhost:3000 to see results");
}).catch(error => console.log(error));
There might also just be a better package, but as I've never worked with this type of thing, I only went off of what the documentation had on it.
So I reached out to QuotaGuard and they gave me an answer that works. The answer is below:
However we usually recommend that you use our QGTunnel software for database connections.
The QGTunnel software is a wrapper program that presents a socket to your application from the localhost. Then you connect to that socket as if it were your database. Below are some setup instructions for the QGTunnel.
Download QGTunnel into the root of your project
Log in to our dashboard and setup the tunnel
Using the Heroku CLI you can log into our dashboard with the following command:
heroku addons:open quotaguardstatic
Or if you prefer, you can login from the Heroku dashboard by clicking on QuotaGuard Static on the resources tab of your application.
Once you are logged into our dashboard, in the top right menu, go to Setup. On the left, click Tunnel, then Create Tunnel.
Remote Destination: tcp://hostname.for.your.server.com:3306
Local Port: 3306
Transparent: true
Encrypted: false
This setup assumes that the remote database server is located at hostname.for.your.server.com and is listening on port 3306. This is usually the default port.
The Local Port is the port number that QGTunnel will listen on. In this example we set it to 3306, but if you have another process using 3306, you may have to change it (ie: 3307).
Transparent mode allows QGTunnel to override the DNS for hostname.for.your.server.com to 127.0.0.1, which redirects traffic to the QGTunnel software. This means you can connect to either hostname.for.your.server.com or 127.0.0.1 to connect through the tunnel.
Encrypted mode can be used to encrypt data end-to-end, but if your protocol is already encrypted then you don't need to spend time setting it up.
Change your code to connect through the tunnel
With transparent mode and matching Local and Remote ports you should not need to change your code. You can also connect to 127.0.0.1:3306.
Without transparent mode, you will want to connect to 127.0.0.1:3306.
Change your startup code.
Change the code that starts up your application. In heroku this is done with a Procfile. Basically you just need to prepend your startup code with "bin/qgtunnel".
So for a Procfile that was previously:
web: your-application your arguments
you would now want:
web: bin/qgtunnel your-application your arguments
If you do not have a Procfile, then heroku is using a default setup in place of the Procfile based on the framework or language you are using. You can usually find this information on the Overview tab of the application in Heroku's dashboard. It is usually under the heading "Dyno information".
Commit and push your code.
Be sure that the file bin/qgtunnel is added to your repository.
If you are using transparent mode, be sure that vendor/nss_wrapper/libnss_wrapper.so is also added to your repository.
If you have problems, enable the environment variable QGTUNNEL_DEBUG=true and then restart your application while watching the logs. Send me any information in the logs. Please redact any sensitive information, including your QuotaGuard connection URL.
VERY IMPORTANT
7. After you get everything working, I suggest you download your QGTunnel configuration from our dashboard as a .qgtunnel file and put that in the root of your project. This keeps your project from not relying on our website during startup.
This did work for me, and I was able to make a connection to my database.
We have a bot running in Azure (Web App Bot) that I'm trying to embed on a website. The bot is based of the Bot Builder V4 SDK Tamplate CoreBot v4.9.2. At first I used the iframe to embed the bod. This worked but didn't provide the features we need, so now im changing it to use DirectLine.
My code on the webpage looks like this:
<script crossorigin="anonymous"
src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<div id="webchat" role="main"></div>
<script>
(async function () {
const res = await fetch('https://[my bot name here].azurewebsites.net/.bot/v3/directline/tokens/generate',
{
method: 'POST',
headers: new Headers({
'Authorization': "Bearer [my token here]"
})
});
const { token } = await res.json();
window.WebChat.renderWebChat(
{
directLine: await window.WebChat.createDirectLineAppServiceExtension({
domain: 'https://[my bot name here].azurewebsites.net/.bot/v3/directline',
token
})
},
document.getElementById('webchat')
);
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
</script>
After some struggles I managed to fetch a token from https://[my bot name here].azurewebsites.net/.bot/v3/directline.
And I can see the chat window on my webpage, but is says connecting for a while then it changes to Taking longer than usual to connect, like this:
In the Chrome console there is an error saying Failed to connect Error: Connection response code 500. When I check Chrome's Network tab I can see that the token generated completed with status 200 and that the websocket connection is open, like this:
----------EDIT---------
I just noticed that when go to https://[my bot name here].azurewebsites.net/.bot using a webbrowser, the resulting json is
{"v":"1.0.0.0.55fa54091a[some key?]","k":true,"ib":false,"ob":false,"initialized":true}
ib and ob should be true but are false, maybe this is part of the problem.
----------EDIT 2---------
OK so now I'm starting to go crazy.
Ashish helped me and at some point the ib and ob were true. They were true for most of yesterday. At some point yesterday they turned false for a short while (no more than 2 hours). I checked if someone had triggered the release pipeline but no recent releases. After that ib and ob magically turned true again and connecting to the direct line worked again.
Now this morning ib and ob were false again. And again no recent releases. I don't know what is causing this.
Does anybody know what's going on here or how to fix this? How do I find what causes ib and ob to be false?
Any help is appreciated! Thanks in advance. If you need more information, just ask and I'll post it.
If the ib and ob values displayed by the *.bot endpoint are false this means the bot and the Direct Line app service extension are unable to connect to each other.
Make sure you verify below things:
Double check the code for using named pipes has been added to the
bot.
Confirm the bot is able to start up and run at all. Useful
tools are Test in WebChat, connecting an additional channel, remote
debugging, or logging.
Restart the entire Azure App Service the bot
is hosted within, to ensure a clean start up of all processes.
Please check troubleshooting guide, it seems updated today. (still old date reflected some how, not sure why)
I'm trying to build an app that does something when it is first installed onto a workspace, eg: Ping every team member.
I couldn't find an event type that gets triggered upon app install:
https://api.slack.com/events
Is there a way to make this happen?
I think there might be a misunderstanding of the events concepts here. Events are always directly linked to one specific Slack app and needs to be processed by that very app. There is no such thing as "general" events for things happening on a workplace, like a new app being installed. Ergo there is no event for app installation.
Nevertheless you can implement the functionality you mentioned with Slack, e.g. pinging all team members once an app is first installed. All you need to do is include this function in the installation process of your Slack app and e.g. start pinging after the installation process is complete and the app verified that it was the first installation to this workspace. You do not need an event for that.
This is a partial answer because I was wondering the same thing and wanted to share what I found. On this oauth tutorial, it has the following code snippet:
app.get('/auth', function(req, res){
if (!req.query.code) { // access denied
return;
}
var data = {form: {
client_id: process.env.SLACK_CLIENT_ID,
client_secret: process.env.SLACK_CLIENT_SECRET,
code: req.query.code
}};
request.post('https://slack.com/api/oauth.access', data, function (error, response, body) {
if (!error && response.statusCode == 200) {
// Get an auth token
let oauthToken = JSON.parse(body).access_token;
// OAuth done- redirect the user to wherever
res.redirect(__dirname + "/public/success.html");
}
})
});
I believe instead of the line res.redirect(__dirname + "/public/success.html"); at that point you can make a request to ping everyone or even call a function to do so directly there, and it will trigger immediately once the app has been installed.
I am trying to follow the apollo client installation instructions.
I'm copying exactly what they are doing into my project, but whenever I call client.query, I get a 400 status code with the following error:
Error: Schema must be an instance of GraphQLSchema. Also ensure that there are not multiple versions of GraphQL installed in your node_modules directory.
This is all the code in my project's index.js:
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';
const client = new ApolloClient({
link: new HttpLink({ uri: 'https://q80vw8qjp.lp.gql.zone/graphql' }),
cache: new InMemoryCache()
});
client.query({ query: gql`{ hello }` }).then(console.log);
I've configured my babelrc file with the following option per the instructions in apollo's documentation:
{
"presets": ["env", "react"],
"plugins": [
"graphql-tag"
]
}
My project is just a basic webpack configuration with react. I've tried de-duping my npm packages, and checked if there are conflicting versions of graphql in my project but had no success. My best guess is that graphql-tag isn't working correctly and my query isn't being translated into the appropriate syntax for the request. It feels like I am missing something very basic. I would greatly appreciate any clarification. Thanks!
This doesn't look like an issue with your client config so much as a bug with Launchpad. If you access the GraphiQL interface for that endpoint and attempt to run a query, it'll throw the same error.
If you log into Launchpad with your Github account, you can start a new pad (which will be identical to that one), save it and then use that url in your project instead.