1.0.0-rc.1
BREAKING
🚨 This release comes with a major rewrite of the Prisma-AppSync Client API and breaking changes. Please make sure to take a moment and read through the details below before upgrading.
Highlights
- Codebase rewrite: Prisma-AppSync was rewritten from the ground to offer a simplified API with a more opinionated approach, end-to-end type safety for a better DX, advanced customisation options, as well as improved security and fine-grained access control.
- Installer CLI: New interactive scaffolding CLI to quickly start new Prisma-AppSync projects, accessible from a single
npx create-prisma-appsync-app@latest
command. It can also plug into existing projects already using Prisma. - Local AppSync Server: New local development environment built for Prisma-AppSync (local database, auto-reload, TS support, GraphQL IDE). Iterate faster by simulating a GraphQL API running on AWS AppSync from your local machine.
- Documentation website: New documentation website and contribution guide, built upon VitePress, and accessible from prisma-appsync.vercel.app.
- Lots of improvements: Implemented dozens of improvements, bug fixes and new Prisma features (atomic operations, order by relations, case sensitivity, etc).
🪓 Breaking changes
Prisma-AppSync Client usage
BREAKING
Usage is 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:
// 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:
const prismaAppSync = new PrismaAppSync()
// direct lambda resolver for appsync
export const resolver = async (event) => {
return await prismaAppSync.resolve({ event })
}
Prisma-AppSync Client options
BREAKING
connectionUrl
parameter renamed intoconnectionString
. This parameter is optional and leverages Prisma Client naming convention and defaults.debug
parameter renamed intologLevel
. This parameter is optional and allows specify server logging levels.- New
maxDepth
parameter that improves security and prevents clients from abusing query depth. Defaults to3
. - New
maxReqPerUserMinute
parameter that provides in-memory rate-limiting and prevents common DDoS attacks. Defaults to200
.
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, specify server logging level (`INFO`, `WARN`, `ERROR`), default to `INFO`
logLevel,
// optional, pagination for listQueries, default to 50
defaultPagination,
// optional, allowed graphql query depth, default to 3
maxDepth,
// optional, per user, in-memory rate limiting, default to 200
maxReqPerUserMinute
})
Custom Resolvers API
BREAKING
Before:
app.registerCustomResolvers({
notify: async ({ args }: CustomResolverProps) => {
return {
message: `${args.message} from notify`
}
}
})
After:
return await prismaAppSync.resolve<'notify'>({
event,
resolvers: {
notify: async ({ args }: QueryParams) => {
return {
message: `${args.message} from notify`,
}
},
}
})
Hooks before/after API
BREAKING
Before:
// execute before resolve
app.beforeResolve(async (props) => {})
// execute after resolve
app.afterResolve(async (props) => {})
After:
return await prismaAppSync.resolve<'likePost'>({
event,
hooks: {
// execute before any query
'before:**': async (params: BeforeHookParams) => params,
// execute after any query
'after:**': async (params: AfterHookParams) => params,
// execute after custom resolver query `likePost`
// (e.g. `query { likePost(postId: 3) }`)
'after:likePost': async (params: AfterHookParams) => {
await params.prismaClient.notification.create({
data: {
event: 'POST_LIKED',
targetId: params.args.postId,
userId: params.authIdentity.sub,
},
})
return params
},
},
})
Fine-Grained Access Control API
BREAKING
Before:
// 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:
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.`,
},
'{update,upsert,delete}Post{,/**}': {
rule: isOwner,
reason: ({ model }) => `${model} can only be modified by their owner.`,
},
}
},
})
Prisma-AppSync Generator options
BREAKING
customSchema
parameter renamed intoextendSchema
.customResolvers
parameter renamed intoextendResolvers
.- new parameter
defaultDirective
, to globally apply default directives.
generator appsync {
provider = "prisma-appsync"
// optional params
output = "./generated/prisma-appsync"
extendSchema = "./custom-schema.gql"
extendResolvers = "./custom-resolvers.yaml"
defaultDirective = "@auth(model: [{ allow: apiKey }])"
}
AppSync Authorizations modes
BREAKING
Before:
/// @PrismaAppSync.type: '@aws_api_key @aws_cognito_user_pools(cognito_groups: [\"admins\"])'
model Post {
id Int @id @default(autoincrement())
title String
}
After:
/// @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"])
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 the maintenance burden, auto-generated API docs have been removed from Prisma-AppSync in favour of a 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 comes back, please consider supporting the project and opening a new issue.
🎉 New features
New installer (scaffolding tool)
NEW FEATURE
New installer that can be run via npx create-prisma-appsync-app@latest
. Nicely plug with existing Prisma projects (non-destroying), while also allowing scaffolding of new projects from scratch.
___ _ _ __
/ _ \_ __(◭)___ _ __ ___ __ _ /_\ _ __ _ __ / _\_ _ _ __ ___
/ /◭)/ '__| / __| '_ ` _ \ / _` |_____ //◭\\| '_ \| '_ \\ \| | | | '_ \ / __|
/ ___/| | | \__ \ | | | | | (◭| |_____/ _ \ |◭) | |◭) |\ \ |_| | | | | (__
\/ |_| |_|___/_| |_| |_|\__,_| \_/ \_/ .__/| .__/\__/\__, |_| |_|\___|
|_| |_| |___/
◭ Prisma-AppSync Installer v1.0.0
Local development environment built for Prisma-AppSync
NEW FEATURE
New local development environment built for Prisma-AppSync (local database, auto-reload, TS support, GraphQL IDE). Simulate a GraphQL API running on AWS AppSync + AWS Lambda Resolver + Prisma ORM + Database.
Support added for Prisma 4.5.x
NEW FEATURE
Codebase adapted and fully tested for Prisma 4.5.x support.
Customise GraphQL Schema output
NEW FEATURE
/// @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
mutation {
updatePost(
where: { id: 1 },
operation: {
views: { increment: 1 }
}
) {
views
}
}
Support for order by relational fields
NEW FEATURE
query {
listPosts(
orderBy: {
author: {
name: ASC
}
}
) {
title
author {
name
}
}
}
Support for Case Sensitivity (PostgreSQL and MongoDB connectors only)
NEW FEATURE
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 were removed from the previous version, in favour of custom utils functions.
Errors handling and server logs improved
IMPROVEMENT
Both error handling and server logs (accessible from CloudWatch) have been improved to include more details and be easily readable.
// 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,
TOO_MANY_REQUESTS: 429,
}
🐞 Bug fixes
Added support for uniqueIndexes (🙏 contribution from @cipriancaba)
Fixed casing issue on GraphQL relation fields (🙏 contribution from @ryparker)
🐙 Issue #37 / PR #38: Generated GraphQL schema relation definitions using the incorrect case for type values
Fixed issue on nullable relation fields in mutations
🐙 Issue #26: Issue using nullable relation fields with mutations
Replaced env var JEST_WORKER_ID with PRISMA_APPSYNC_TESTING
🐙 Issue #32: Issue performing tests because of JEST_WORKER_ID
💛 Github Sponsors
Enjoy using Prisma-AppSync? Please consider sponsoring me at 🐙 maoosi ↗ so that I can spend more time working on the project.