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

New Pothos Plugin for EdgeDB #534

Open
baristikir opened this issue Aug 15, 2022 · 9 comments
Open

New Pothos Plugin for EdgeDB #534

baristikir opened this issue Aug 15, 2022 · 9 comments

Comments

@baristikir
Copy link
Contributor

Introduction

https://www.edgedb.com/docs

Just found out about EdgeDB and it looks like a new database which also has its own schema and query language, according to my understanding. However it also contains a node.js client for type generations of the underlying database schema, which could be ideally defined in a .esdl file in projects directory.

The node.js client actually splits into two different libraries, providing a Driver API for executing db queries and a Query Builder API to write typesafe queries. For now I would say that the Query Builder API would be the entry point, focusing on typesafe integration of schema types rather than providing any performance improvements or query optimizations (also optimizations aren't really documented).

The QueryBuilder gets generated from edgeDB-js lib and looks mappable to a pothos object. The Pothos plugin API would be actually pretty similar to the prisma plugin, providing advantages of object declartion without explicitly typing the objectRef.

But I would say that the biggest benefit could be from the GraphQL query, instead loading manually the schema fields or everything at once, we could determine which were requested from the query and construct the select out of it.

builder.queryType({
  fields: (t) => ({
    me: t.field({
      type: User, // simple objectRef<User> for db schema
      resolve: async (root, args, ctx, info) => {
            const db_query = await e
                .select(e.User, (user) => {
                    // Manually querying fields
                    name:true,
                    email: true,
                    // Or query everything
                    //  ...e.User["*"],
                    filter: e.op(user.id, '=', ctx.user.id),
                });

            return await db.run(db_query);
      }
    }),
  }),
});

builder.queryType({
  fields: (t) => ({
    // Define a field that issues an edgedb query
    me: t.edgeDBField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) => {
            const db_query = await e
                .select(e.User, (user) => {
                    // automatically resolving queried fields
                    ...query,
                    filter: e.op(user.id, '=', ctx.user.id),
                });

            return await db.run(db_query);
      }
    }),
  }),
});

Example

// Create an object type based on a edgedb type
// without providing any custom type information
builder.edgeDBObject('User', {
  fields: (t) => ({
    // expose fields from the database schema
    id: t.exposeID('id'),
    email: t.exposeString('email'),
  }),
});

builder.queryType({
  fields: (t) => ({
    // Define a field that issues an edgedb query
    me: t.edgeDBField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) => {
            const db_query = await e
                .select(e.User, (user) => {
                    // the `query` argument will add in the `select`s fields to
                    // resolve as much of the request in a single query as possible
                    ...query,
                    filter: e.op(user.id, '=', ctx.user.id),
                });

            return await db.run(db_query);
      }
    }),
  }),
});

@hayes Let me know what you think. Also got something wip currently, can share updates soon.

@hayes
Copy link
Owner

hayes commented Aug 15, 2022

EdgeDB definitely looks interesting!

I'm not against the idea, but I also don't have enough time right now to build this out. I have a lot of work left on the Prisma plugin to cover some existing use cases. The prisma plugin is one of the most complex things I've worked on for Pothos, and mapping graphQL queries to database queries can get pretty complicated. What you are proposing here looks like it has a lot in common with how the existing prisma plugin works, so there may be opportunity to share/resuse some logic, or at least copy some of the patterns.

I think edgedb also has an existing GraphQL engine/query interface. I'm not sure if it would make sense, but another potential apporach here might be to write a plugin to interface with external GraphQL APIs using a similar pattern (I built something like that at Airbnb, but I don't have access to that code anymore). Building a plugin for using GraphQL APIs as a source would probably support a wider range of use cases, and would be easier to prioritize for me.

If this is something you are interested in building, I am happy to help out, and review code. I am not against the idea of adding something like this to Pothos directly in the future, but I don't really have the time to build or maintain it myself right now.

@baristikir
Copy link
Contributor Author

Appreciate it taking your time for sharing your thoughts on this topic!

I have just started with the basic level of this plugin, with reusing a lot of the prisma plugin logic and patterns. I would love to work on this, might take some time and open for any code reviews :)
I would like to share my updates and progress here then.

Interesting haven't thought about the GraphQL API directly, will take a look into that and also how building a plugin for that would look like.

@hayes
Copy link
Owner

hayes commented Aug 15, 2022

Awesome, excited to see how this goes! Feel free to ask here or on discord if you have any questions about how the plugin system works, or how to build/design specific pieces (or if anything in the prisma plugin doesn't make sense)

@seanaye
Copy link

seanaye commented Aug 17, 2022

I'm using edgedb with pothos, although im wiring the resolvers manually. It might be worth noting here that edgedb queries can be arbitrarily nested, unlike prisma, due to its graph structure. I was thinking about this problem the other day and I think specifically for the case of edgedb the most efficient way to resolve a query is to compile the info resolver argument at the root to a single edgedb query.

This would allow all non-root resolvers to simply pull the data from their parent instead of having to query, completely eliminating N+1 problems.

I don't have the bandwidth to work on this either but I think this would be the best-case-scenario for an edgedb specific plugin. The cool part is that it would work with any graphql server not just pothos because it relies on the graphql spec for info

@hayes
Copy link
Owner

hayes commented Aug 17, 2022

@seanaye this is a simplified version of what the prisma plugin does.

There are some issues with the naive implementation of mapping info object to a query though:

  • we should NOT assume that fields will be named the same in graphql as they are in the db
  • things like relay connections (there are a bunch of other similar patterns in pothos) make this harder, because the relevant data is nested through one or more other objects
  • Same thing applies in the opposite direction, often a many to many would be modeled with a join table. We probably wouldn't want a graphql type for that join table
  • arguments (limits, filters, orderby) are not always directly on the relation they need to be applied to, and may not be named/formatted the same in the graphql layer as they are in the db layer
  • graphql aliases are a thing, this means the same relation can be selected multiple times with different arguments in a single graphql query

This prisma plugin has solutions for all of these problems, but it gets very complicated, and requires a LOT more information than what you can get from the info object. We could come up with standardized representation of metadata for defining meaningful relationships, arguments, and mappings that are attached as extensions on types. This would allow you to still get the data from the info object (this is how its done in the prisma plugin, but the way things are defined is far from standardized or reusable at the moment).

@emmanuelbuah
Copy link

emmanuelbuah commented Apr 5, 2023

I'd be very interested in this as well. @baristikir @hayes Is this still active and of interest? EdgeDB has come a long ways and made a ton of improvements and features. I have been using Prisma but planning on migrating to EdgeDB.

@hayes
Copy link
Owner

hayes commented Apr 5, 2023

I don't think this is actively being developed. I want to eventually build something more generic to support things like drizzle, kysely, and edgeDB without having to build everything from scratch for each orm. No concrete plans though, and will be a while before I have time to even start investigating how this might work

@jmolivas
Copy link

Drizzle++ the latest 0.26 release includes some interesting features

https://medium.com/@aleksandrblokh/best-typescript-orm-just-got-better-5a33688b8d2e

Relational Queries

@hayes
Copy link
Owner

hayes commented May 21, 2023

Definitely been on my radar, have some other high priority work (mostly v4 release) I need to get done first, but a drizzle plugin would be really cool, and those new features should make it much better/easier to build correctly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants