prisma-appsync icon indicating copy to clipboard operation
prisma-appsync copied to clipboard

Prisma-AppSync Client API rewrite

Open maoosi opened this issue 4 years ago • 3 comments

TLDR

After using Prisma-AppSync in various projects for about a year, I’ve decided it was time to rewrite Prisma-AppSync Client API from the ground (this will not affect the generated GraphQL schema or CRUD operations, only change the way the Client API is being used as part of the Lambda Resolver).

Why?

Here’s what I’m hoping to achieve from this re-write:

  • [ ] Simplify usage by streamlining API and adopting a more opinionated approach. Provide a better TypeScript DX with cleaner naming conventions and closer to Prisma Client.
  • [ ] Make fine-grained access control and custom resolvers easier to use and more flexible across all scopes, from small to larger size projects (see shield in the preview below).
  • [ ] Adopt a TDD approach with full CI/CD integration. This will help to cover more edge cases and bring Prisma-AppSync to a stable version quicker.
  • [ ] Refactor internal code structure, reduce external dependencies and improve execution performances. This should also make contributions to the project much easier for others.

Preview

Work in progress, very likely to evolve:

Minimal example

/**
 * Instantiate Prisma-AppSync Client
 */
const prismaAppSync = new PrismaAppSync()

/**
 * Lambda handler (AppSync Direct Lambda Resolver)
 */
export const resolver = async (event: any, context: any) => {
    return await prismaAppSync.resolve({ event })
}

Advanced example

/**
 * Instantiate Prisma-AppSync Client
 */
const prismaAppSync = new PrismaAppSync()

/**
 * Lambda handler (AppSync Direct Lambda Resolver)
 */
export const main = async (event: any, context: any) => {

    return await prismaAppSync.resolve<'listPosts' | 'notify'>({
        event,
        resolvers: {
            // Extend the generated CRUD API with a custom Query
            notify: async ({ args }: QueryParams) => {
                return {
                    message: `${args.message} from notify`,
                }
            },

            // Disable, or override, a generated CRUD operation
            listPosts: false,
        },
        shield: ({ authorization, identity }: QueryParams) => {
            const isAdmin = identity?.groups?.includes('admin')
            const isOwner = { owner: { cognitoSub: identity?.sub } }
            const isCognitoAuth = authorization === Authorizations.AMAZON_COGNITO_USER_POOLS

            return {
                // By default, access is limited to logged-in Cognito users
                '**': isCognitoAuth,

                // Posts and Comments can only be modified by their owner
                'modify/{post,comment}{,/**}': {
                    rule: isOwner,
                    reason: ({ model }) => `${model} can only be modified by their owner.`,
                },

                // Password field is protected
                '**/*password{,/**}': {
                    rule: false,
                    reason: () => 'Field password is not accessible.',
                },

                // Custom resolver `notify` is restricted to admins
                'notify{,/**}': {
                    rule: isAdmin,
                },
            }
        },
        hooks: () => {
            return {
                // Triggered after a Post is modified
                'after:modify/post': async ({ prismaClient, prismaArgs, result }: AfterHookParams) => {
                    await prismaClient.hiddenModel.create({
                        data: prismaArgs.data,
                    })
                    return result
                },
            }
        },
    })

}

Timeline

The work on this has already started, though I'm not able to commit at this stage. I’ll update this issue as soon as I have a clearer view of the timeline.

Feedback

Please feel free to respond to this issue with any suggestions you might have around the Client API itself, and the preview code from above.

maoosi avatar Jun 24 '21 02:06 maoosi

This looks like a good structure, would the generated revolvers be similar ?

DregondRahl avatar Jun 24 '21 02:06 DregondRahl

This looks like a good structure, would the generated revolvers be similar ?

@DregondRahl Yes! Keeping the same generated resolvers, GraphQL schema and docs. Only changing the "developer-facing" API.

maoosi avatar Jun 24 '21 02:06 maoosi

@maoosi image

it gives error for version "prisma-appsync": "^1.0.0-beta.58.2"

boroco avatar Jun 29 '22 03:06 boroco