Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Code / Application layer in front of (delegating to) Postgraphile #736

Closed
1 of 3 tasks
james-ff opened this issue Apr 11, 2018 · 6 comments
Closed
1 of 3 tasks
Labels
📄 add-to-docs ❔ question 🔁 revisit-in-v5 Not happy with this behaviour, but changing it is a breaking change.

Comments

@james-ff
Copy link

I'm submitting a ...

  • bug report
  • feature request
  • question

PostGraphile version: 4.0.0-beta.5

Hi,

Postgraphile is fantastic! The utility of being able to focus on setting up a postgres db and then instantly have a graphql api ready to go based on that is incredible, thank you for this library (and the hard work is going into it)!

I am however unsure how to proceed in regards to adding an application layer / business logic layer / custom code on top of an instance of postgraphile. i.e. (to build out a graphql server that delegates database queries and mutations to a postgraphile instance / schema).

[(Apollo) Client] ---> [Graph-Server] ---> [Postgraphile] ---> [PostgreSQL]

I have started to explore schema-only usage (using graphql-yoga) which builds fine, connects to my db, and seems to successfully connect to the schema, but queries and mutations don't work at runtime (tested in a graphiql client), please feel free to try it out (code included below).

const Pool = require('pg-pool')
const { GraphQLServer } = require('graphql-yoga')
const { createPostGraphileSchema } = require('postgraphile')

var pool = new Pool({...}) // connection details ommitted, enter yours here
(async function () {
  const schema = await createPostGraphileSchema(pool, 'myschema')
  const server = new GraphQLServer({ schema })
  server.start({ port: 4000 }, ({ port }) => console.log(`Server is running on localhost:${port}`))
})()

A similar library Prisma uses GraphQL-Bindings to acheive the aim of delegation quite nicely. Summary: [scroll to How Prisma fits into the GraphQL ecosystem] https://blog.graph.cool/introducing-prisma-1ff423fd629e

I prefer Postgraphile to Prisma becuase

  • I prefer postgresql (currently prisma only supports mysql)
  • Prisma can't work with existing databases (like this library does very brilliantly).

But I can't commit to using Postgraphile unless there is a clear way that I can put custom code in front of Postgraphile, like I would be able to with Prisma right now.

Questions:

  • Would it be possible to create a bindings library similar to Prisma's?
  • Would a full example of how to setup a graph-server that delegates to a postgraphile instance be possible now without a bindings library?

Please let me know if I need to clarify anything,

Many Thanks!

@benjie
Copy link
Member

benjie commented Apr 12, 2018

If you spin up PostGraphile and then use it as a remote schema you should be able to process it using any GraphQL tooling that supports remote GraphQL APIs (e.g. over HTTP). I believe GraphQL bindings can use remote GraphQL APIs (e.g. graphql-binding-github) so you should be able to use PostGraphile in the same way.

The issue that you face is because we require the pgClient to be instantiated and passed via context to the graphql function; whereas yoga does not do this for us. This is for legacy reasons, and I intend to change it in version 5 so this is no longer necessary.

@james-ff
Copy link
Author

james-ff commented Jun 4, 2018

Am working on developing an example of how this might work. For me the added layer of business logic is crucial if I am to use this library over Prisma (I'm currently much preferring to use postgraphile!). Will post back here if I get somewhere that might be useful to share

@james-ff
Copy link
Author

james-ff commented Jun 5, 2018

Binding using Introspection:

const { Binding } = require('graphql-binding')
const { importSchema } = require('graphql-import')
const { GraphQLServer } = require('graphql-yoga')
const { createHttpLink } = require('apollo-link-http')
const { makeRemoteExecutableSchema, introspectSchema } = require('graphql-tools')

class ServiceBinding extends Binding {
  constructor(postgraphileLink, introspectedSchema) {
    super({ schema: makeRemoteExecutableSchema({ schema: introspectedSchema, link: postgraphileLink() }) })
  }
}

async function run() {
  try {
    const postgraphileLink = () => createHttpLink({ uri: `http://localhost:3000/graphql`, fetch })
    const introspectedSchema = await introspectSchema(postgraphileLink())
    const binding = new ServiceBinding(postgraphileLink, introspectedSchema)
    console.log(await binding.query.allCustomers())
    console.log(await binding.query.allCustomers({}, `{ nodes { id firstName lastName } }`))
  } catch(error) {
    console.log(error)
  }
}

run()

Using the generated schema file

  const postgraphileLink = () => createHttpLink({ uri: `http://localhost:3000/graphql`, fetch })
  const remoteSchema = importSchema(fs.readFileSync(path.join(__dirname, `./generated/schema.graphql`), 'utf-8'))

@benjie benjie added the 🔁 revisit-in-v5 Not happy with this behaviour, but changing it is a breaking change. label Aug 15, 2018
@saerdnaer
Copy link

Are there any updates on this idea? It would be nice to embed postgraphile directly e.g. in a stiching GraphQL gateway without http in-between gateway and postgraphile.

@benjie
Copy link
Member

benjie commented Nov 25, 2018

@saerdnaer It seems like you're requesting something slightly different - i.e. schema stitching? Stitching PostGraphile with another schema should work fine; the problem most users face is that you need to provide the correct per-request things on the context you feed to the graphql function. Many of the servers do not support this, they expect context to only contain garbage-collectable values, whereas PostGraphile requires that each request has its own pgClient with a transaction, and after the request that transaction must be committed and the pgClient released. We have a helper for this: withPostGraphileContext:

withPostGraphileContext(stuff, context => graphql(...))

How you use this with your GraphQL server of choice really depends on the server. I made a plugin for the latest Apollo Server to allow mounting PostGraphile in there:

https://github.com/graphile/postgraphile-apollo-server/blob/d94ae7d8b05d50532f68f6a67618867ec8607897/index.js#L87

Alternatively, with postgraphile@next and graphile-utils@next you can use makeProcessSchemaPlugin to replace the schema with a stitched schema and run it from within the PostGraphile server; e.g.

module.exports = makeProcessSchemaPlugin(schema => {
  return stitchOtherSchemasInto(schema);
});

https://github.com/graphile/postgraphile/releases

Let me know where you get stuck and I'll do my best to unstick you 👍

@benjie
Copy link
Member

benjie commented Feb 28, 2019

Closing this as it exists on the v5 board.

@benjie benjie closed this as completed Feb 28, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
📄 add-to-docs ❔ question 🔁 revisit-in-v5 Not happy with this behaviour, but changing it is a breaking change.
Projects
None yet
Development

No branches or pull requests

3 participants