simonw / datasette-graphql

Datasette plugin providing an automatic GraphQL API for your SQLite databases
https://datasette-graphql-demo.datasette.io/
Apache License 2.0
100 stars 6 forks source link

Plugin hook to allow other plugins to define GraphQL APIs #69

Closed simonw closed 2 years ago

simonw commented 3 years ago

Bonus idea: if https://github.com/simonw/datasette-graphql provided its own plugin hook then plugins like this one could expose a GraphQL API as well.

Originally posted by @simonw in https://github.com/simonw/datasette-ripgrep/issues/18#issuecomment-737847561

simonw commented 3 years ago
from datasette_graphql import hookimpl

def extra_fields(datasette):
    return [
        ("hello", graphene.String(), lambda root, info: "Hello world"),
    ]

Returns a name, a field type and a resolve function - these are then added to the query at the root of the Schema.

simonw commented 3 years ago

Would use the same optional async def inner() pattern as other hooks.

simonw commented 3 years ago

Open question: what if the name from a plugin collides with the name of a SQLite table?

Could solve that by automatically renaming the GraphQL field for that table.

markhalonen commented 3 years ago

The Postgraphile project handles many of the same types of issues, you may want to look there for inspiration. It has awesome plugins

simonw commented 2 years ago

I proved in https://github.com/simonw/datasette-low-disk-space-hook that you can add new plugin hooks as part of another plugin, see also this TIL: https://til.simonwillison.net/datasette/register-new-plugin-hooks

simonw commented 2 years ago

I'm going to experiment with this hook using https://datasette.io/plugins/datasette-packages since it's a really simple plugin (much simpler than datasette-ripgrep).

simonw commented 2 years ago

Here's the hookspec:

from pluggy import HookspecMarker

hookspec = HookspecMarker("datasette")

@hookspec
def graphql_extra_fields(datasette):
    "A list of (name, field_type) tuples to include in the GraphQL schema"

And an example plugin implementation:

@hookimpl
def graphql_extra_fields():
    class Package(graphene.ObjectType):
        "An installed package"
        name = graphene.String()
        version = graphene.String()

    return [
        (
            "packages",
            graphene.Field(
                graphene.List(Package),
                description="List of installed packages",
                resolver=lambda root, info: [
                {"name": d.project_name, "version": d.version}
                for d in sorted(
                    pkg_resources.working_set, key=lambda d: d.project_name.lower()
                )
            ])
        ),
    ]
simonw commented 2 years ago

Documentation: https://github.com/simonw/datasette-graphql/blob/860e27c9c628b2e052007f8fc1bf85263173340e/README.md#adding-custom-fields-with-plugins

simonw commented 2 years ago

Demo: https://latest-with-plugins.datasette.io/graphql?query=%7B%0A%20%20%20%20packages%20%7B%0A%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20version%0A%20%20%20%20%7D%0A%7D