⚠️
This release comes with a major rewrite of the Prisma-AppSync Client API and several breaking changes. Please make sure to read details below before upgrading.

🪓 Breaking changes

Prisma-AppSync Client API rewritten from the ground up

BREAKING
  • Usage simplified by adopting a more opinionated approach. Also provides a better TypeScript DX closer to Prisma Client.
  • Fine-gained access control and hooks have been re-engineered entirely to make them easier to use and more flexible for all project sizes.
  • Adopted a full TDD approach with both unit and integration tests. This is to help bring Prisma-AppSync to a stable version quicker.
Before:
typescript
// init prisma-appsync client const app = new PrismaAppSync({ connectionUrl: process.env.CONNECTION_URL }) // direct lambda resolver for appsync export const main = async (event) => { // parse the `event` from your Lambda function app.parseEvent(event) // handle CRUD operations / resolve query const result = await app.resolve() // close database connection await app.prisma.$disconnect() // return query result return Promise.resolve(result) }
After:
typescript
const prismaAppSync = new PrismaAppSync() // direct lambda resolver for appsync export const resolver = async (event) => { return await prismaAppSync.resolve({ event }) }

Client API custom resolvers rewrite

BREAKING
Before:
typescript
app.registerCustomResolvers({ notify: async ({ args }: CustomResolverProps) => { return { message: `${args.message} from notify` } } })
After:
typescript
return await prismaAppSync.resolve<'notify'>({ event, resolvers: { notify: async ({ args }: QueryParams) => { return { message: `${args.message} from notify`, } }, } })

Client API Hooks rewrite

BREAKING
Before:
typescript
// execute before resolve app.beforeResolve(async (props) => {}) // execute after resolve app.afterResolve(async (props) => {})
After:
typescript
return await prismaAppSync.resolve<'likePost'>({ event, // AppSync event hooks: { // execute before any query 'before:**': async (props) => {}, // execute after any query 'after:**': async (props) => {}, // execute after custom resolver query `likePost` // (e.g. `query { likePost(postId: 3) }`) 'after:likePost': async ({ prismaClient, authIdentity, args }) => { await prismaClient.notification.create({ data: { event: 'POST_LIKED', targetId: args.postId, userId: authIdentity.sub, } }) }, } })

Fine-Grained Access Control rewrite

BREAKING
Before:
typescript
// before resolving any query app.beforeResolve(async ({ authIdentity }: BeforeResolveProps) => { // rules only apply to Cognito authorization type if (authIdentity.authorization !== AuthModes.AMAZON_COGNITO_USER_POOLS) { return false } // get current user from database, using Prisma // we only need to know the user ID const currentUser = await prisma.user.findUnique({ select: { id: true }, where: { cognitoSub: authIdentity.sub } }) || { id: null } // everyone can access (get + list) or create Posts app.allow({ action: AuthActions.access, subject: 'Post' }) app.allow({ action: AuthActions.create, subject: 'Post' }) // only the author is allowed to modify a given Post app.deny({ action: AuthActions.modify, subject: 'Post', // IF `Post.ownerId` NOT_EQUAL_TO `currentUser.id` THEN DENY_QUERY condition: { ownerId: { $ne: currentUser.id } } }) })
After:
typescript
return await prismaAppSync.resolve({ event, shield: ({ authorization, identity }: QueryParams) => { const isCognitoAuth = authorization === Authorizations.AMAZON_COGNITO_USER_POOLS const isOwner = { owner: { cognitoSub: identity?.sub } } // Prisma syntax return { '**': { rule: isCognitoAuth, reason: ({ model }) => `${model} access is restricted to logged-in users.`, }, 'modify/post{,/**}': { rule: isOwner, reason: ({ model }) => `${model} can only be modified by their owner.`, }, } }, })

New Prisma-AppSync Client options

BREAKING
  • connectionUrl parameter renamed into connectionString. This parameter is optional and leverage Prisma Client naming convention and defaults.
  • New maxDepth parameter that improve security and prevent clients from abusing query depth. Defaults to 3.
typescript
const prismaAppSync = new PrismaAppSync({ // optional, DB connection string, default to env var `DATABASE_URL` connectionString, // optional, enable data sanitizer for DB storage (incl. XSS parser), default to true sanitize, // optional, enable debug server logs, default to false debug, // optional, pagination for listQueries, default to 50 defaultPagination, // optional, allowed graphql query depth, default to 3 maxDepth, })

New Prisma-AppSync Generator options

BREAKING
  • customSchema parameter renamed into extendSchema.
  • customResolvers parameter renamed into extendResolvers.
  • new parameter defaultDirective, to globally apply default directives.
typescript
generator appsync { provider = "prisma-appsync" // optional params output = "./generated/prisma-appsync" extendSchema = "./custom-schema.gql" extendResolvers = "./custom-resolvers.yaml" defaultDirective = "@auth(model: [{ allow: apiKey }])" }

New syntax for AppSync Authorizations modes

BREAKING
Before:
json
/// @PrismaAppSync.type: '@aws_api_key @aws_cognito_user_pools(cognito_groups: [\"admins\"])' model Post { id Int @id @default(autoincrement()) title String }
After:
json
/// @auth(model: [{ allow: apiKey }, { allow: userPools, groups: ["admins"] }]) model Post { id Int @id @default(autoincrement()) title String }
👆 Output: @aws_api_key @aws_cognito_user_pools(cognito_groups: ["admins"])

New data sanitizer behaviour

BREAKING
Enabling the data sanitizer will now automatically "clarify/decode" the data before sending it back to the client. Meaning client-side decoding is not anymore necessary, while your data will still be parsed for XSS before storage.

Auto-generated documentation

BREAKING
To reduce maintenance burden, auto-generated API docs have been removed from Prisma-AppSync in favour of better description of the data (accessible via the native GraphQL documentation from your favourite GraphQL IDE).
In case this is problematic for you and you’d like auto-generated docs to make a come back, please consider supporting the project and opening a new issue.

🎉 New features and improvements

New installer (scaffolding tool)

NEW FEATURE
New installer that can be run via npx create prisma-appsync-app. Nicely plug with existing Prisma projects (non-destroying), while also allows to scaffold new projects from scratch.
bash
___ _ _ __ / _ \_ __()___ _ __ ___ __ _ /_\ _ __ _ __ / _\_ _ _ __ ___ / /)/ '__| / __| '_ ` _ \ / _` |_____ //◭\\| '_ \| '_ \\ \| | | | '_ \ / __| / ___/| | | \__ \ | | | | | (| |_____/ _ \ |) | |) |\ \ |_| | | | | (__ \/ |_| |_|___/_| |_| |_|\__,_| \_/ \_/ .__/| .__/\__/\__, |_| |_|\___| |_| |_| |___/ ◭ Prisma-AppSync Installer v1.0.0

Customise GraphQL Schema output

NEW FEATURE
json
/// @gql(queries: { list: 'posts' }, subscriptions: null) model Post { id Int @id @default(autoincrement()) title String }
👆 Output: Default listPosts query renamed to posts / No subscriptions for the Post model

Support for Atomic Operations

NEW FEATURE
graphql
mutation { updatePost( where: { id: 1 }, operation: { views: { increment: 1 } } ) { views } }

Support for Case Sensitivity (PostgreSQL and MongoDB connectors only)

NEW FEATURE
graphql
query { listPosts( where: { title: { contains: "prisma", mode: "insensitive" } } ) { title } }

CDK boilerplate upgraded to v2+

IMPROVEMENT
CDK boilerplate was entirely rewritten to offer a more easy to use, simpler syntax making it more approachable for people who are new to AWS CDK.

TypeScript types improved for better DX

IMPROVEMENT
Hovering on Prisma-AppSync types and Client API methods using VSCode is now displaying docs and examples across the entire codebase.

Bundle size and performances

IMPROVEMENT
Noticeable gains in bundle size and runtime performances. Lots of dependencies removed from the previous version, in favour of custom utils functions.

Errors handling and server logs improved

IMPROVEMENT
Both errors handling and server logs (accessible from CloudWatch) have been improved to includes more details and be easily readable.
typescript
// Example object returned from the API { "error": "Query has depth of 4, which exceeds max depth of 3.", "type": "FORBIDDEN", "code": 401 } // Error codes const errorCodes = { FORBIDDEN: 401, BAD_USER_INPUT: 400, INTERNAL_SERVER_ERROR: 500, }

🐞 Bug fixes

FIXES

🐙 Issue #26: Issue using nullable relation fields with mutations

🐙 Issue #32: Issue performing tests because of JEST_WORKER_ID

👋 Github Sponsors

CONTRIBUTION
Enjoy using Prisma-AppSync? Please consider sponsoring me at 🐙 maoosi so I can spend more time working on the project.

Helpful?