Prisma-AppSync Client API rewrite
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
shieldin 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.
This looks like a good structure, would the generated revolvers be similar ?
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

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