Upgrade Prisma 1 to Prisma 2 with Apollo + GraphQL - 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 :)

Related

nestjs + apollo graphql federated gateway can't introspect services because of "bad request", reproducible git repository available

In an NX monorepo I'm building 3 nestjs application, an auth-service + user-service and a gateway to start off with. They're all powered by apollo graphql and following the official nestjs documentation.
The issue that I'm having is that with both the user-service and auth-service up and processing requests successfully as individual servers, at the same time, the gateway throws
Couldn't load service definitions for "auth" at http://localhost:3100/apis/auth-service/graphql: 400: Bad Request
The services themselves are standard graphql applications, nothing basic.
The definitions for the gateway are as such:
{
server: {
debug: true,
playground: true,
autoSchemaFile: './apps/gateway/schema.gql',
sortSchema: true,
introspection: true,
cors: ['*'],
path: '/apis/gateway/graphql';
},
gateway: {
supergraphSdl: new IntrospectAndCompose({
subgraphHealthCheck: true,
subgraphs: [
{
name: 'user',
url: resolveSubgraphUrl('user'),
},
{
name: 'auth',
url: resolveSubgraphUrl('auth'),
},
],
}),
}
}
I have created an nx based monorepository that allows you to reproduce this issue easily, by spinning up all 3 servers in watch mode at the same time, on localhost, on different ports.
Everything is mapped as it should. Link: https://github.com/sebastiangug/nestjs-federation.git
The README contains the two commands necessary to run it as well as the same set of instructions plus the health-checks queries available from each service.
Versions used:
"#apollo/subgraph": "2.1.3",
"#apollo/federation": "0.37.1",
"#apollo/gateway": "2.1.3",
"apollo-server-express": "3.6.7",
"graphql": "16.5.0",
"#nestjs/graphql": "10.1.3",
"#nestjs/platform-express": "9.0.8",
Any ideas what I'm doing wrong here or what further configuration is necessary to achieve this?
Thank you

how to hot reload federation gateway in NestJS

Problem
In a federated nest app, a gateway collects all the schemas from other services and form a complete graph. The question is, how to re-run the schema collection after a sub-schema has been changed?
Current Workaround
Restarting the gateway solves the problem, but it does not seem like an elegant solution.
Other Resources
Apollo server supports managed federation which essentially reverts the dependency between the gateway and the services. Sadly I couldn't find anything relating it to NestJS.
When configuring gateway application with NestJS, and when already have integrated with Apollo studio, then you need not define any serviceList in GraphQLGatewayModule. This is how your module initialization should look like:
GraphQLGatewayModule.forRootAsync({
useFactory: async () => ({
gateway: {},
server: {
path: '/graphql',
},
}),
})
Following environment variables should be declared on the machine hosting your gateway application:
APOLLO_KEY: "service:<graphid>:<hash>"
APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: "https://uplink.api.apollographql.com/"
Post deployment of Federated GraphQL service, you may need to run apollo/rover CLI service:push command like below to update the schema which writes to schema registry and then gets pushed to uplink URL which is polled by gateway periodically:
npx apollo service:push --graph=<graph id> --key=service:<graph id>:<hash> --variant=<environment name> --serviceName=<service name> --serviceURL=<URL of your service with /graphql path> --endpoint=<URL of your service with /graphql path>
You can add a pollIntervalInMs option to the supergraphSdl configuration.
That will automatically poll the services again in each interval.
#Module({
imports: [
GraphQLModule.forRootAsync<ApolloGatewayDriverConfig>({
driver: ApolloGatewayDriver,
useFactory: async () => ({
server: {
path: '/graphql',
cors: true
},
gateway: {
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'example-service', url: 'http://localhost:8081/graphql' },
],
pollIntervalInMs: 15000,
})
},
})
})
],
})
export class AppModule {}

Vercel/NextJS: How to access serverless functions from frontend during local development?

My React/NextJS front end has a Button component that fetches data via a serverless function when the button is clicked. I want to test this functionality during local development with the Vercel dev/CLI tools. I am getting a 404 result when attempting to access my lambda functions. Here are the approximate steps that I've gone through so far:
Create package.json with a dev script:
...
"scripts": {
"dev": "yarn codegen && next --hostname=0.0.0.0 --port=3001",
}
...
Link to deployed vercel project
Create vercel.json to specify builds and routes:
...
"builds": [
{ "src": "*.html", "use": "#now/static" },
{ "src": "pages/api/*.py", "use": "#now/python" },
],
"routes": [
{ "src": "/api/validate", "dest": "/pages/api/validate.py" }
]
...
Create my test Lambda function (in python):
from http.server import BaseHTTPRequestHandler
from datetime import datetime
class handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')).encode())
return
Create my Button component:
...
<Button
variant="contained"
color="secondary"
onClick={() => {
fetch('api/validate')
.then(response => { console.log(response)
response.json() })
.then(data => console.log(data))
}}
>
Generate sample dataset
</Button>
...
Run vercel dev
Access website at localhost:3001 (next dev server address)
Click button
Result:
I'm receiving a 404 response
Note: I can access the lambda function from localhost:3000/pages/api/validate.py (vercel dev server address). This appears to manually kickstart the lambda function build and serve process. I thought that it should have been built and served already from the vercel.json specification and be available at localhost:3001/api/validate. This seems to agree with the Vercel documentation.
Note 2: Next dev/CLI tools build and serve javascript/typescript files just fine. I'm using python and Go functions as well, which are supported by Vercel dev/CLI but not Next
My solution was to use vercel dev instead of next dev or yarn dev, and to use an environment variable in a .env file that points to the function url. This env variable should be prepended with NEXT_PUBLIC_ so that it is registered by next.js and passed to process.env during the build process.
# .env
NEXT_PUBLIC_FUNCTIONS_BASE_URL="http://localhost:3000" # 3000 is vercel port
# component.js
...
fetch(process.env.NEXT_PUBLIC_FUNCTIONS_BASE_URL + '/api/function-name')
...
You need to pass the port from vercel dev to the upstream CLI, in this case next dev.
{
"scripts": {
"dev": "yarn codegen && next dev --port=$PORT",
}
}
Now when you run vercel dev, the ephemeral port will be proxied from the dev server.
You can also remove vercel.json if you rename /pages/api to /api.

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.

Auto Reload of gateway for schema changes in federated service apollo GraphQL

In Apollo Federation, I am facing this problem:
The gateway needs to be restarted every time we make a change in the schema of any federated service in service list.
I understand that every time a gateway starts and it collects all the schema and aggregates the data graph. But is there a way this can be handled automatically without restarting the Gateway as it will down all other unaffected GraphQL Federated services also
Apollo GraphQL , #apollo/gateway
There is an experimental poll interval you can use:
const gateway = new ApolloGateway({
serviceList: [
{ name: "products", url: "http://localhost:4002" },
{ name: "inventory", url: "http://localhost:4001" },
{ name: "accounts", url: "http://localhost:4000" }
],
debug: true,
experimental_pollInterval:3000
})
the code above will pull every 3 seconds
I don't know other ways to automatically reload the gateway other than polling.
I made a reusable docker image and i will keep updating it if new ways to reload the service emerge. For now you can use the POLL_INTERVAL env var to periodically check for changes.
Here is an example using docker-compose:
version: '3'
services:
a:
build: ./a # one service implementing federation
b:
build: ./b
gateway:
image: xmorse/apollo-federation-gateway
ports:
- 8000:80
environment:
CACHE_MAX_AGE: '5' # seconds
POLL_INTERVAL: '30' # seconds
URL_0: "http://a"
URL_1: "http://b"
You can use express to refresh your gateway's schema. ApolloGateway has a load() function that go out to fetch the schemas from implementing services. This HTTP call could potentially be part of a deployment process if something automatic is needed. I wouldn't go with polling or something too automatic. Once the implementing services are deployed, the schema is not going to change until it's updated and deployed again.
import { ApolloGateway } from '#apollo/gateway';
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
const gateway = new ApolloGateway({ ...config });
const server = new ApolloServer({ gateway, subscriptions: false });
const app = express();
app.post('/refreshGateway', (request, response) => {
gateway.load();
response.sendStatus(200);
});
server.applyMiddleware({ app, path: '/' });
app.listen();
Update: The load() function now checks for the phase === 'initialized' before reloading the schema. A work around might be to use gateway.loadDynamic(false) or possibly change gateway.state.phase = 'initialized';. I'd recommend loadDyamic() because change state might cause issues down the road. I have not tested either of those solutions since I'm not working with Apollo Federation at the time of this update.

Resources