I know I can provide parameter path when calling server.applyMiddleware({ app, path });. But it looks like this path will be used for both the GraphQL API endpoint (when using XHR) and also for the playground (when visiting in a browser). Is it possible to use different URLs for them? e.g. /api/graphql for the endpoint and /graphql-playground for the playground
It does not look like it's supported.
If this sounds like a valid feature request, I can work on this and submit a merge request next week.
Def interested in this.
My plan is to add a new option playgroundPath in the argument of applyMiddleware. If not specified, it will be the same as path and everything will stay the same; when it is different from path, it will be used as the URL for the playground instead, but the endpoint will still be path. Meanwhile, a property playgroundPath is added to the server instance so people can read it from outside easily.
Usage:
server.applyMiddleware({ app, path: '/graphql', playgroundPath: '/graphql-playground' });
Let me know if it looks good. Thanks!
Any update on this @mondaychen ?
A little too busy recently :(
Hopefully I can submit the MR by the end of this week
Maybe this would work for you.
You can make 2 separate server instances with different parameters but bound to the same express app.
// using 2 separate instances
// 1 for iql and the other for client access
const serverIql = new ApolloServer({
schema: analysedSchema,
introspection: true,
playground: {
settings: {
'editor.theme': 'light',
'request.credentials': 'include'
}
},
context
});
const server = new ApolloServer({
schema: analysedSchema,
introspection: false,
playground: false,
context
});
// attch to express
server.applyMiddleware({ app, path: "/client" });
serverIql.applyMiddleware({ app, path: "/graphql" });
Out of curiosity, why isn't it acceptable to use the same path since they shouldn't interfere with each other? (Unless I'm missing something?)
Hi @abernix
In my case, I need to protect my graphql API with authentication rules via some nginx/gateway configuration. But for the playground in development mode, I want to get rid of them.
Also, people mentioned a few different use cases and concerns in #1974
Same for me, faced same authorization problem as well and want to differentiate API route from Playground one.
If I'm not mistaken, the playground key accepts an endpoint parameter like so:
import { ApolloServer } from 'apollo-server'
const server = new ApolloServer({
playground: {
endpoint: 'http://localhost:4000/graphql', // I think this is the default one
},
})
So in your case, I believe you could do this (using express here as an example)
import express from 'express'
import { ApolloServer } from 'apollo-server-express'
const app = express()
const server = new ApolloServer({
playground: {
endpoint: `http://${your_domain_here}:${your_port_here}/graphql-playground`,
},
})
server.applyMiddleware({
app,
path: '/api/graphql',
})
app.listen(your_port_here, () => {
console.log(`Serving graphql api on /api/graphql and playground on /graphql-playground!`)
})
where you'd replace your_domain_here and your_port_here with your own variables, naturally.
The shape of endpoint can be found here: https://github.com/prisma/graphql-playground#properties.
Also, playground accepts a config key which lets you do more advanced things like set custom headers (seeing from your messages I think maybe you'd like to supply an Authorization token during development that gets you through your API security so you can easily access the playground):
const config = {
projects: {
prisma: {
schemaPath: 'src/generated/prisma.graphql',
includes: ['database/seed.graphql'],
extensions: {
prisma: 'database/prisma.yml',
'prepare-binding': {
output: 'src/generated/prisma.ts',
generator: 'prisma-ts',
},
endpoints: {
dev2: {
url: 'https://eu1.prisma.sh/public-asdf/session65/dev',
headers: {
Authorization:
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJhc2RmQGRldiIsInJvbGVzIjpbImFkbWluIl19LCJpYXQiOjE1MjM1MTg3NTYsImV4cCI6MTUyNDEyMzU1Nn0.fzKhXa1JpN9M1UGTbS6p2KMUWDrKLxYD3i3a9eVfOQQ',
},
},
},
},
},
app: {
schemaPath: 'src/schema.graphql',
includes: ['queries/{booking,queries}.graphql'],
extensions: {
endpoints: {
default: 'http://localhost:4000',
},
},
},
},
}
(taken from https://github.com/prisma/graphql-playground/blob/master/packages/graphql-playground-react/src/localDevIndex.tsx#L47)
Is this enough customisation for you or do you need something else?
Any updates on this? I need this precisely b/c of the scenario @mondaychen described.
btw, @Pocket-titan, what you describe doesn't work as the endpoint config variable refers to the graphql api endpoint playground hits, rather than the endpoint at which playground is served.
Ah, thank you @yukw777, I was misinterpreting the issue.
I think you might be better off forking the specific package that you need and modifying it yourself. For example, glancing at packages/apollo-server-express/src/ApolloServer.ts in the express implementation, all you'd have to add is something like
...
export interface ServerRegistration {
...
playgroundPath?: string;
}
...
public applyMiddleware({
...
playgroundPath,
}: ServerRegistration) {
...
if (!playgroundPath) playgroundPath = path;
...
app.use(Array.from(new Set([path, playgroundPath])), (req, res, next) => {
if (req.path === playgroundPath && this.playgroundOptions && req.method === 'GET') {
...
} else if (req.path === path) {
return graphqlExpress(() => {
return this.createGraphQLServerOptions(req, res);
})(req, res, next);
}
});
}
b/c a PR will probably only be accepted if it includes _all_ apollo-server packages, leaving you to do more work than necessary for you own situation.
Not sure if this would be useful to anyone, but I solved the problem like this:
const path = "/api/graphql";
app.use(path, (req, res, next) => {
// Allow GET (Playground)
if (req.method === "GET") {
return next();
}
// Authenticate other GraphQL queries
authJwt(req, res, next);
});
server.applyMiddleware({ app, path });
I'm really needing this functionality. Any word on this? I'm monitoring #1974 and it seems to be stalled.
We found a workaround for this, by essentially serving GraphQL Playground manually on a separate endpoint, as explained in the examples here. It'd be nice if Apollo offered flexibility here though!
I used @swac workaround, but ran into an issue in that you have to turn on introspection in production (a security no-no).
The fix I found is to enable it in production, but then create an Apollo plugin to intercept any introspection requests and have it block response execution if user isn't authenticated. This way you can go create an Authorization bearer token in the HTTP Headers in the playground to enable things.
Here is some pseudocode for the plugin…
{
requestDidStart() {
return {
/**
* Look at query and return error if query is an introspection but
* not for authenticated request
* @param requestContext
*/
async responseForOperation(requestContext) {
// If it is not introspection, then pass it on through
if ( !requestContext.request.query.includes('__schema') ) {
return null;
}
// Do authentication here `requestContext.context` has context. Return `null` if authenticated.
// Return error
throw new AuthenticationError('GraphQL introspection is not allowed by Apollo Server, but the query contained __schema or __type. To enable introspection, pass introspection: true to ApolloServer in production');
return {};
}, //of responseForOperation
}; // of return
}
Note that looking at the Apollo codebase, it also traps __type queries. Also I doubt AuthenticationError and GraphQLError are identical since they are different parts of the tree. I'm open to any improvements to the above.
The following should allow you to have Playground and Voyager available in a development environment. This will keep the /graphql endpoint protected by auth.
const server = new ApolloServer({
schema,
context: async ({ req }) => {
return getContext(req.user);
},
introspection: process.env.NODE_ENV !== 'production',
playground: process.env.NODE_ENV !== 'production',
});
app.use(
['/api', '/graphql'], // protected endpoints
require('./routes')
);
// only make `/playground` available in `development` environment
const path =
process.env.NODE_ENV === 'development'
? ['/graphql', '/playground']
: ['/graphql'];
server.applyMiddleware({ app, path });
// point voyager to `/playground` instead of `/graphql`
if (process.env.NODE_ENV === 'development') {
app.use('/voyager', voyagerMiddleware({ endpointUrl: '/playground' }));
}
Ideally we would be able to have the playground running with introspection on a separate endpoint e.g. /graphql-playground, but no introspection allowed on the primary world-facing /graphql. Then we could protect the playground API path so that it's just accessible by internal developers, and keep introspection running for only that path.
The separate endpoint is critical. I got it working myself, mainly from threads like these, but it was a major pain in the butt. I really wish the maintainers would see the need and do something about it.
@thardy how did you solve it to have the playground on another uri than the graphql server?
I created two separate Apollo servers with two separate Express apps. A very rough pseudo-code is below...
```
const playgroundApolloServer = createApolloServer();
// main server app
const main = express();
playgroundApolloServer.applyGraphQlPlaygroundMiddleware({
app: main,
path: '/graphql',
});
const apiApolloServer = createApolloServer();
const siteApp = express();
apiApolloServer.applyGraphQlApiMiddleware({
app: siteApp,
path: '/graphql-api',
db: db,
});
```
I don't know if this helps but doing this worked for me
``javascript
const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
cdnUrl: "http://127.0.0.1:7000/"
}
})
````
Here just replacecdnUrl` by the Uri that you have setup to serve the static assets.
I still had to follow a specific directory structure (structure in CDN URL in script tags) to get this done correctly
Most helpful comment
I found https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-express/src/ApolloServer.ts
It does not look like it's supported.
If this sounds like a valid feature request, I can work on this and submit a merge request next week.