disable graphiql on production - apollo-server

How can I disable graphiql on production but still able to access it on development?
With express-graphql we can do something like
app.use('/graphql', graphqlHTTP({
schema: MySessionAwareGraphQLSchema,
graphiql: process.env.NODE_ENV === 'development',
}));
With apollo server, my setup is
import {graphqlExpress, graphiqlExpress} from 'graphql-server-express'
const app = new Express()
app
.all('/graphql', bodyParser.json())
.all('/graphql', graphqlExpress({
schema
)
.all('/graphiql', graphiqlExpress({
endpointURL: 'http://localhost/graphql'
})
)
and I can't find a way to pass to NODE_ENV to enable/disable graphiql.

Do you mean to enable graphiql on development only and disable it on production. If so just exclude the /graphiql handler
if (process.env.NODE_ENV === 'development') {
app.all(
'/graphiql',
graphiqlExpress({
endpointURL: '/graphql',
}),
);
}

Here's what I have in a koa setup
export default () => (
convert(graphqlHTTP((req, res, ctx) => ({
schema: require('app/graphql/schema'),
context: {
...ctx.app.context,
...ctx.state,
},
// Enable graphql for development environments only
graphiql: config.environment === 'development',
formatError: error => ({
message: error.message,
stack: error.stack,
locations: error.locations,
}),
})))
)
Note graphiql: config.environment === 'development', from here you could pass a custom environment variable and start your app with it.
ENABLE_GRAPHIQL=true NODE_ENV=production npm start_my_server
Depending on how you manage your environment variables, you could change the expression to
graphiql: myEnv.ENABLE_GRAPHIQL || myEnv.environment === 'development',
FWIW you should not be enabling graphiql in production

When you start the app you need to define the environment variables.
Do it using your package.json scripts.
"start:dev": "cross-env NODE_ENV=development **<your application start script>**"
"start:prod": "cross-env NODE_ENV=production **<your application start script>**"
If you develop on Windows install and use cross-env package to support environment variables.

Related

Laravel mix 6 hot with https

Configuration:
Laravel Mix 6.0.16
Laravel Valet 2.13.19
webpack 5.30.0
webpack-cli 4.6.0
webpack-dev-server 4.0.0-beta.1
Description:
I try to enable https for hot replacement in Laravel 8 on MacOs with Valet. I did my site secure, add --https in my package.json
"hot": "mix watch --hot --https"
Then I launch command in CLI
yarn hot
And it was successful, but when I open my site I saw error in browser console http://joxi.ru/nAypq4ZTwJBP52
net::ERR_CERT_AUTHORITY_INVALID
I found the solution.
Remove --https from script definition in package.json
Add configuration in the file webpack.mix.js
mix.options({
hmrOptions: {
host: url,
port: 8080
}
})
mix.webpackConfig({
devServer: {
https: {
key: fs.readFileSync('/Users/alex/.config/valet/Certificates/castle.test.key'),
cert: fs.readFileSync('/Users/alex/.config/valet/Certificates/castle.test.crt')
}
}
})
In my case I got the mixed content issue. I found out the publicPath was still using http instead of https. I fixed the public path by overriding it. Also made added a few checks to read the certificates if they exist and made the certificate path generic.
const homeDir = process.env.HOME;
const host = process.env.APP_URL.split('//')[1];
const port = '8080';
mix.options({
hmrOptions: {
host,
port
}
});
if (
process.argv.includes("--hot") &&
fs.existsSync(path.resolve(homeDir, `.config/valet/Certificates/${host}.key`)) &&
fs.existsSync(path.resolve(homeDir, `.config/valet/Certificates/${host}.crt`))
) {
mix.webpackConfig({
devServer: {
https: {
key: fs.readFileSync(path.resolve(homeDir, `.config/valet/Certificates/${host}.key`)).toString(),
cert: fs.readFileSync(path.resolve(homeDir, `.config/valet/Certificates/${host}.crt`)).toString()
},
}
});
// overriding publicPath as it was using http and causing mixed-content
mix.override(c => {
c.output.publicPath = process.env.APP_URL + `:${port}/`
});
}
Update
laravel-mix-valet is available on npm which works with laravel valet
require('laravel-mix-valet');
const host = process.env.APP_URL.split('//')[1];
mix.js('resources/js/app.js', 'public/js')
.valet(host)
.vue();
This works for our team (running "hot": "mix watch --hot", without the --https):
if (!mix.inProduction() && process.env.DEV_SERVER_KEY) {
let proxy = process.env.APP_URL.replace(/^(https?:|)\/\//,'');
mix.options({
hmrOptions: {
host: proxy,
port: '8080',
https: {
key: process.env.DEV_SERVER_KEY,
cert: process.env.DEV_SERVER_CERT
}
}
});
}

Upgrade Prisma 1 to Prisma 2 with Apollo + GraphQL

I have a problem upgrading from Prisma 1 to Prisma 2.
The documentation is quite complicated for me.
I currently have a small project using :
"dependencies": {
"bcryptjs": "2.4.3",
"graphql-yoga": "1.18.3",
"jsonwebtoken": "8.5.1",
"prisma-binding": "1.5.19"
},
"devDependencies": {
"dotenv": "5.0.1",
"graphql-cli": "2.17.0",
"nodemon": "1.19.4",
"npm-run-all": "4.1.5",
"prisma": "^1.34.10"
}
My prisma.yml :
endpoint: ${env:PRISMA_ENDPOINT}
secret: ${env:PRISMA_SECRET}
datamodel: datamodel.graphql
hooks:
  post-deploy:
    - prisma generate
generate:
  - generator: graphql-schema
    output: ../src/generated/prisma.graphql
I used scripts :
"scripts": {
"start:dev": "nodemon -e js,graphql -x node -r dotenv/config src/index.js",
"start": "node src/index.js",
"debug": "nodemon -e js,graphql -x node --inspect -r dotenv/config src/index.js",
"playground": "graphql playground",
"dev": "npm-run-all --parallel start playground",
"deploy": "prisma1 deploy --env-file .env"
},
And this graphqlconfig
projects:
app:
schemaPath: "src/schema.graphql"
extensions:
endpoints:
default: "http://localhost:4000"
prisma:
schemaPath: "src/generated/prisma.graphql"
extensions:
prisma: database/prisma.yml
How can I update prisma?
Knowing that my frontend is based on VueJS with ApolloClient, graphlq, graphql-tag
Thanks a lot to you, the backend part is not something simple for me
And here is my tree structure
E D I T
Thanks for your anwser. Nice, #nburk
But I have a problem on the 3rd step : https://www.prisma.io/docs/guides/upgrade-guides/upgrade-from-prisma-1/upgrading-the-prisma-layer-postgres
(Connection URL)
Previously I don't used "docker-compose". I deployed my front + back + DB on Heroku with Prisma
const { Prisma } = require("prisma-binding");
const resolvers = require("./resolvers");
// GraphQL Yoga Server
const server = new GraphQLServer({
typeDefs: "src/schema.graphql",
resolvers,
context: (req) => ({
...req,
db: new Prisma({
typeDefs: "src/generated/prisma.graphql", // DB Prisma Schema
endpoint: process.env.PRISMA_ENDPOINT, // Prisma Service
secret: process.env.PRISMA_SECRET, // Prisma Secret
debug: true,
}),
}),
});
server.start(() =>
console.log(`Server is running on ${process.env.PRISMA_ENDPOINT}`)
);
With a DB hosted on Heroku (.env file)
PRISMA_ENDPOINT="https://lprojet-name-db.herokuapp.com/database/prod"
And when I used npx prisma introspect I have this error
Introspecting based on datasource defined in prisma/schema.prisma …
Error: P1001
Can't reach database server at `'localhosh':'5432'
Please make sure your database server is running at 'localhost':'5432'
I think the problem comes from the schema.prisma which requires to have a url starting with postgresql:// but with Prisma1 I didn't need to go through that.
How can I transform my old DB URL (currently hosted on Heroku)?
Thanks
EDIT 2
I used
DATABASE_URL=postgres://..........eu-west-1.compute.amazonaws.com:5432/d9ptc61fera9g1
And I have a "database empty" error, but my database isn't empty. This UR come from Heroku Database Config
Nikolas from Prisma here!
We have written extensive upgrade documentation that walks you through the upgrade process. Here's some guides you can follow:
How to upgrade: Provides a general overview and explains different upgrade strategies
Upgrading the Prisma layer: Explains how to adjust your database schema using the Prisma Upgrade CLI
prisma-binding to SDL-first: Explains how to upgrade your GraphQL schema and resolvers
Feel free to follow up in case you have any questions along the way, always happy to help :)

prisma.yml could not be found

I am trying to generate schema for my prisma data model while at the same time using secrets to restrict prisma access. After running prisma delete and prisma deploy, I run the command graphql get-schema -p prisma and get the following error message:
✖ prisma/prisma.yml could not be found.
Is there something wrong I am doing in my .graphqlconfig or how I am listing my prisma.yml? Thanks.
.graphqlconfig:
{
"projects": {
"prisma": {
"schemaPath": "generated/prisma.graphql",
"extensions": {
"prisma": "prisma/prisma.yml",
"endpoints": {
"default": "http://localhost:4466"
}
}
}
}
}
prisma/prisma.yml:
endpoint: http://localhost:4466
datamodel: datamodel.prisma
secret: 'secretFoo'
index.js:
import http from 'http';
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import resolvers from './resolvers';
import schema from './generated/prisma.graphql';
import { Prisma } from 'prisma-binding';
const prisma = new Prisma({
endpoint: 'http://localhost:4466',
secret: 'secretFoo',
typeDefs: 'server/generated/prisma.graphql',
});
const server = new ApolloServer({
context: {
prisma,
},
resolvers,
typeDefs: schema,
});
const app = express();
server.applyMiddleware({ app });
const PORT = 5000;
const httpServer = http.createServer(app);
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}`);
});
if (module.hot) {
module.hot.accept();
module.hot.dispose(() => server.stop());
}
You can generate a schema directly from your prisma.yml file, by adding the following to the file:
generate:
- generator: graphql-schema
output: ./generated/prisma.graphql
Then you can refer your .graphqlconfig to the generated file:
projects:
prisma:
schemaPath: generated/prisma.graphql
extensions:
endpoints:
dev: http://localhost:4466
You would generally restrict access to the management functionality of your endpoint through the Prisma docker-compose file (managementApiSecret in PRISMA_CONFIG). Then when you run commands like prisma deploy you would need to pass the appropriate environment variables through either the --env-file flag, or by having a dotenv file in the root of your application's directory (you also need the dotenv package installed in package.json.
Another way to secure your endpoint is to disable the GraphQL Playground altogether. I believe Apollo Server does this automatically when NODE_ENV is set to production, although you can do it explicitly with:
const server = new ApolloServer({
context: {
prisma,
},
resolvers,
typeDefs: schema,
playground: false, // <- Here
});
I'm sorry, I don't think this directly answered your question, but it may assist either way.

Heroku Deployment Error: displaying backend, not frontend

I am trying to deploy my local MERN application to Heroku. Application works offline. After deployment, when click "Open app", all I see is the data from the backend. Not the front end. Deployment here: https://whispering-falls-45660.herokuapp.com/.
Set up new Heroku project, and after successful "git push Heroku master", application only shows backend data. Heroku CLI version: heroku/7.24.1, Node version: v10.13.0.
Github repo: https://github.com/neilhsieh/whereToEat
Package.json file has the proper scripts in place as per Brad Traversy:
"scripts": {
"start": "node server.js",
"server": "nodemon server.js",
"client": "npm start --prefix client",
"dev": "concurrently \"npm run server\" \"npm run client\"",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run build --prefix client"
},
server.js code has appropriate code to point to client build file:
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html')) // relative path
})
}
Expecting front end deployed on Heroku, ended up only having backend.
UPDATE: Moved process.env.NODE_ENV test to before all API calls, this fixed my issue.
First if you have a route like this in your server/app.js file
app.get("/", (req, res) => res.send("Api Running"));
Definitely get rid of that
Second add these lines in your server/app.js file
const path = require("path");
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}

NestJS on Heroku always failing to bind port

I am trying to deploy my NestJS REST API on Heroku but I always get the following error:
Web process failed to bind to $PORT within 60 seconds of launch
My configuration is pretty straight forward:
In my main.ts I start my server with:
await app.listen(process.env.PORT || AppModule.port);
I added a Procfile in the root directory of my project which contains:
web: npm run start:prod
My package.json files contains these scripts:
"build": "tsc -p tsconfig.build.json",
"prestart:prod": "rimraf dist && npm run build",
"start:prod": "node dist/main.js",
The process on Heroku builds succesfully, prints out these seamingly reassuring lines:
TypeOrmModule dependencies initialized
SharedModule dependencies initialized
AppModule dependencies initialized
But then immediately crashes with:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch
I use .env configuration across my application but I removed all HOST and PORT variables (and code references), so I have no clue what could be the cause of this error.
Am I missing something?
EDIT
I am hereby sharing my app.module and main.ts files:
app.module.ts
#Module({
imports: [
SharedModule,
TypeOrmModule.forRootAsync({
imports: [SharedModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
type: 'postgres',
host: configService.getString('POSTGRES_HOST'),
port: configService.getNumber('POSTGRES_DB_PORT'),
username: configService.getString('POSTGRES_USER'),
password: configService.getString('POSTGRES_PASSWORD'),
database: configService.getString('POSTGRES_DB'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
} as PostgresConnectionOptions),
}),
UserModule,
],
controllers: [
AppController,
],
providers: [
AppService,
],
})
export class AppModule {
static port: number;
static isDev: boolean;
constructor(configurationService: ConfigService) {
console.log(process.env.PORT);
AppModule.port = configurationService.getNumber('PORT');
AppModule.isDev = configurationService.getBoolean('ISDEV');
}
}
My configuration.service.ts is a simple utility that reads from .env files:
import * as dotenv from 'dotenv';
import * as path from 'path';
#Injectable()
export class ConfigService {
constructor() {
const filePath = path.resolve('.env');
dotenv.config({
path: filePath,
});
}
getNumber(key: string): number | undefined {
return +process.env[key] as number | undefined;
}
getBoolean(key: string): boolean {
return process.env[key] === 'true';
}
getString(key: string): string | undefined {
return process.env[key];
}
}
And finally my main.ts file:
async function bootstrap() {
console.log(process.env.PORT);
const app = await NestFactory.create(AppModule);
app.enableCors();
app.useGlobalPipes(new ValidationPipe(), new TimeStampPipe());
app.use(json({ limit: '5mb' }));
app.setGlobalPrefix('api/v1');
await app.listen(process.env.PORT || AppModule.port);
}
bootstrap();
Could it be that my configuration.service.ts is interfering with heroku's env file?
If you are using fastify instead express as your platform, you need to define the host to 0.0.0.0 explicitly like this :
const port = process.env.PORT || AppModule.port;
const host = '0.0.0.0';
await app.listen(port, host);
This problem is caused by the fastify library. See the related discussion here: Fastify with Heroku.
Just as a summary, be careful on the database connection timeout that could lead to a global timeout of the heroku bootstrap as describe above

Resources