devoxa / prisma-relay-cursor-connection Goto Github PK
View Code? Open in Web Editor NEWExtend Prisma's `findMany` method to support Relay Cursor Connections.
Extend Prisma's `findMany` method to support Relay Cursor Connections.
The addition of the nodes
field in #432 is a super useful change when working on the frontend side, but unfortunately it doesn't work great when using custom edges.
With custom edges, the nodes
field contains an array of the records
instead of an array of the node
specified in recordToEdge
:
{
recordToEdge: (record) => ({
node: record.foo,
extraEdgeField: 'Bar',
}),
}
The typing of nodes
is correct, but not the actual value inside.
This use case is bit more complex so I wasn't able to find the change necessary to make things work ๐
In the meantime, I use this workaround:
const result = await findManyCursorConnection...
result.nodes = result.edges.map((edge) => edge.node);
return result;
Also, I notice that the hasRequestedField
when requesting all records wasn't updated to check for nodes
. Right now, it only checks for edges
and totalCount
.
prisma-relay-cursor-connection/src/index.ts
Lines 78 to 86 in 7372ed8
GraphQL Cursor Connections Specification allows additional fields in Edges. Currently it is not possible to have custom edge fields since the package directly returns
edges: nodes.map((node) => ({ cursor: encodeCursor(node, options), node })),
in findManyCursorConnection
function.
I have custom edge fields in some connections and I basically duplicated your implementation to implement those connections.
I think this could be a useful feature for this package. What do you think?
Thank you for your work, it really helped me.
Hey team, how is it going?
Great job on this library! I've been playing around with it and it will make my life easy peasy! :)
I have a question though: I have a chat project and we have a connection for chats that user is currently a member, and for performance issues I need to query over a relationship table instead of the chat table itself... model would be something like:
chat <-> chat_user <-> user
I didn't fully understand what would be the right implementation if I want to query over chat_user
table but returning a connection of chats instead, can you help me with that?
Here's a simplified version of my schema.prisma
: (hiding a bunch of fields just for demonstration purposes)
model chat {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
name String?
image_url String?
}
model chat_user {
chat_id String @db.Uuid
user_id String @db.Uuid
last_opened DateTime @default(now()) @db.Timestamptz(6)
created_at DateTime? @default(now()) @db.Timestamptz(6)
is_admin Boolean @default(false)
}
model user {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
first_name String
last_name String
}
Thanks so much!
Hi, team
I wonder about the function findManyCursorConnection
that doesn't support handling outside the try catch
block that I want to use with custom exception handling when the user passes any malformed query to the backend. Here is an example of my statement that not working with try catch
below:
Thank you!
It would be nice to be able to opt out of the nodes
return value for use-cases where it's not needed. Requires a different function return type based on an options property.
Workaround
const result = await findManyCursorConnection(/* ... */)
delete result.nodes
Original conversion #432 (comment) and following.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These problems occurred while renovating this repository. View logs.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.
.github/workflows/push.yml
actions/checkout v4
actions/setup-node v4
buildjet/cache v4
codecov/codecov-action v4
package.json
graphql-fields 2.0.3
@devoxa/eslint-config 3.0.11
@devoxa/prettier-config 2.0.3
@prisma/client 5.13.0
@swc/core 1.5.0
@swc/jest 0.2.36
@types/graphql-fields 1.3.9
@types/jest 29.5.12
eslint 8.57.0
jest 29.7.0
prettier 3.2.5
prisma 5.13.0
typescript 5.4.5
@prisma/client ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
I use prisma-relay-cursor-connection
with my NestJS backend, I created a function
async findCategories(): Promise<StoryCategoryConnection> {
return await findManyCursorConnection(
(x) =>
this.prisma.storyCategory.findMany({
...x,
include: {
stories: { take: 1, include: { media: true }, orderBy: { updatedAt: 'desc' } },
},
orderBy: { updatedAt: 'desc' },
}),
() => this.prisma.storyCategory.count(),
null,
{
recordToEdge: ({ stories, ...record }) => ({ node: { ...record, thumbnail: stories[0]?.media } }),
},
);
}
The problem is I get an error in recordToEdge
function where I add thumbnail that is a media of the last updated relation story
. The error says
Object literal may only specify known properties, and 'thumbnail' does not exist in type '{ stories: ({ media: { id: string; createdAt: Date; updatedAt: Date; name: string; mimeType: string; url: string; frontId: string; backId: string; proofOfAddressId: string; proofOfFundsId: string; storyId: string; }; } & { ...; })[]; } & { ...; }'.ts(2353)
interfaces.d.ts(28, 5): The expected type comes from property 'node' which is declared here on type 'Omit<Edge<{ stories: ({ media: { id: string; createdAt: Date; updatedAt: Date; name: string; mimeType: string; url: string; frontId: string; backId: string; proofOfAddressId: string; proofOfFundsId: string; storyId: string; }; } & { ...; })[]; } & { ...; }>, "cursor">'
Error disappears when I return the list of stories
in recordToEdge
function, but I don't want to do it, I can also fix that passing
<any, StoryCategory>
as an TS argument to findManyCursorConnection
but I don't want to use any
and Prisma doesn't generate relations as types in e.g. StoryCategory
type from prisma schema so I can't pass PrismaStoryCategory as argument instead of any because generic function doesn't see relation and I don't want to create more types than I must
It would be lovely if I could pass type I want as a first argument in findManyCursorConnection
or pass it anywhere it would be possible to strictly type my edge type
btw. this problem is weird because I also did it in another function
return await findManyCursorConnection(
(x) =>
this.prisma.game.findMany({
...x,
where,
orderBy: { [field]: direction },
include: { gamesOnCountries: { include: { country: true } }, category: true },
}),
() => this.prisma.game.count({ where }),
{ after, first, before, last },
{
recordToEdge: (record) => ({
node: { ...record, countries: record.gamesOnCountries.map(({ country }) => country) },
}),
},
);
And I don't return gamesOnCountries relation and typescript doesn't scream about it's not present in returned object
Another thing I spotted is that when I use recordToEdge
function, type returned from findManyCursorConnection
doesn't modify type returned as node, it's still a type returned from prisma query
Please delete.
Hi!
Just wanted to say thank you for this library! As a heavy user of apollo-client, I wanted to try Relay for a while, and the thought of implementing relay-style pagination was a big mental drain... Until I found your library! :)
Thanks again
Would it be possible to only bring back the count and not have to grab any records? I currently have to pass either first or last with a positive value. I was wondering if it would be possible to pass 0 so I could only bring back count?
import { findManyCursorConnection } from '@devoxa/prisma-relay-cursor-connection'
interface TestObject {
id: number;
}
const result = await findManyCursorConnection<TestObject, TestObject>(
(args) => tests.findMany(args),
() => tests.count(),
{ first: {first}, after: {after} }
)
I want use numeric value for unique value
For that, I set my own TestObject
type when using findManyCursorConnection
But, after is always needed string value
Can I use numeric unique?
Is there a sample on how to deal with N+1 problem? Can library be used together with https://github.com/graphql/dataloader? If yes then how?
2.1.0:
Starting compilation in watch mode...
node_modules/@devoxa/prisma-relay-cursor-connection/dist/src/interfaces.d.ts:1:36 - error TS2307: Cannot find module 'graphql' or its corresponding type declarations.
1 import { GraphQLResolveInfo } from 'graphql';
This doesn't happen in 2.0.2.
Hi there,
I am new to cursor-based pagination, especially relay-style. However, as I understand, it is mandatory to order by the cursor to get predictable results, and this is by default not added to the sorting of the prisma query. Would it be an idea to add order by default, as I feel like it is assumable you want this when using this library?
Hi, thanks for the library!
I have an auto generated ObjectType like the following:
@ObjectType()
export class Product {
@Field()
id!: string;
@Field(() => Price[])
prices!: Price[]
}
I would like to make prices
a connection, but if I do the following TypeScript complains with Property 'prices' in type 'ProductNode' is not assignable to the same property in base type 'Product'.
@ObjectType()
export class ProductNode extends Product {
@Field(() => ProductPriceConnection)
prices!: ProductPriceConnection;
}
I also get the same error when I define the ResolverInterface.
Is there a canonical way to address this class of issues or the only way is to use implements
rather than extends
and re-define the whole object type manually?
Thanks.
Is there a way to customize the Connection object?
I would like to add a pageCursors
property with cursors resolved to page numbers to assist in instances of traditional pagination, similar to the approach described in this article.
...or at least play with it, anyway. I suspect a hybrid approach which could alternately use offsets instead of cursors when a page number is supplied would probably be the most efficient in the short term :)
Hello ๐
Thanks for this awesome lib, I was looking into implement this functionality by hand when I discovered it.
While playing with it I realised I was missing the _count
and _avg
from the response.
Here's the normal query which works fine:
course.findMany({
orderBy: {
createdAt: "desc",
},
where,
include: {
_count: {
select: {
lesson: true,
},
},
},
});
Here's the query using the plugin:
await findManyCursorConnection<
Course,
Pick<Prisma.CourseWhereUniqueInput, "eid">
>(
(args) => {
return client.course.findMany({
...args,
orderBy: {
createdAt: "desc",
},
where,
include: {
_count: {
select: {
lesson: true,
},
},
},
});
},
() => client.course.count({ where }),
{ first, after },
{
getCursor: (record) => ({ eid: record.eid }),
encodeCursor,
decodeCursor,
}
);
Is this something you're looking to add in the future? I can also have a look into it when I get some time.
Thanks ๐
This was originally from https://github.com/devoxa/prisma-relay-cursor-connection/pull/102/files, which I closed for non-activity. If someone else would like that feature feel free to open a fresh PR.
Hey thanks for that library. Seems that I found an bug: when using with orderBy
I only get a small set if data (16 from 101 entries). How can that be? How to fix that?
const a = await findManyCursorConnection(
(args) => {
return this.prisma.story.findMany({
...args,
where,
orderBy: orderBy ? { [orderBy.field]: orderBy.direction } : undefined,
include: { authors: true, sources: true, topics: true, mood: true },
});
},
() => this.prisma.story.count({ where }),
{ first, last, before, after }
);
I want to create pagination using prisma-relay-cursor-connection I'm using Postgresql with prisma in nestjs app
The id is number how we can handle pagination in this case
async GetPage() {
return await findManyCursorConnection(
args => this.Prisma.rue.findMany(args),
() => this.Prisma.rue.count(),
{},
);
}
TS Erorro :
Argument of type 'PrismaFindManyArguments<{ id: string; }>' is not assignable to parameter of type '{ select?: RueSelect; include?: RueInclude; where?: RueWhereInput; orderBy?: Enumerable; cursor?: RueWhereUniqueInput; take?: number; skip?: number; distinct?: Enumerable<...>; }'.
The types of 'cursor.id' are incompatible between these types.
Type 'string' is not assignable to type 'number'.ts(2345)
We recently added ability to support custom edge fields. However, return type of findManyCursorConnection
function is still Promise<Connection<Node>>
. I think we should update it to reflect those changes. I will open a PR soon but wanted to ask if this was intended.
Right now the model has to have { id: string }
, because I could not get the generic types working for { id: string | number }
Contributions welcome
I want to use the library like the documentation instructs me to, but the types don't match.
Is the library up to date with the latest Prisma Client? (or am I doing something wrong?)
I'm using NestJS.
{
"dependencies": {
"@devoxa/prisma-relay-cursor-connection": "^2.0.3"
"@prisma/client": "3.7.0"
},
"devDependencies": {
"prisma": "^3.7.0"
},
}
src/apps/apps.service.ts:30:9 - error TS2322: Type 'PrismaPromise<App[]>' is not assignable to type 'Promise<{ id: string; }[]>'.
Types of property 'then' are incompatible.
Type '<TResult1 = App[], TResult2 = never>(onfulfilled?: (value: App[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<...>' is not assignable to type '<TResult1 = { id: string; }[], TResult2 = never>(onfulfilled?: (value: { id: string; }[]) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<...>'.
Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
Types of parameters 'value' and 'value' are incompatible.
Type 'App[]' is not assignable to type '{ id: string; }[]'.
Type 'App' is not assignable to type '{ id: string; }'.
Types of property 'id' are incompatible.
Type 'number' is not assignable to type 'string'.
30 this.prisma.app.findMany({
~~~~~~~~~~~~~~~~~~~~~~~~~~
31 ...args,
~~~~~~~~~~~~~~~~~~
32 where
~~~~~~~~~~~~~~~
33 }),
~~~~~~~~~~
node_modules/@devoxa/prisma-relay-cursor-connection/dist/src/index.d.ts:7:73
7 }, Node = Record, CustomEdge extends Edge<Node> = Edge<Node>>(findMany: (args: PrismaFindManyArguments<Cursor>) => Promise<Record[]>, aggregate: () => Promise<number>, args?: ConnectionArguments, pOptions?: Options<Record, Cursor, Node, CustomEdge>): Promise<Connection<Node, CustomEdge>>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The expected type comes from the return type of this signature.
src/apps/apps.service.ts:30:34 - error TS2345: Argument of type '{ where: Prisma.AppWhereInput; cursor?: { id: string; }; take?: number; skip?: number; }' is not assignable to parameter of type '{ select?: AppSelect; include?: AppInclude; where?: AppWhereInput; orderBy?: Enumerable<AppOrderByWithRelationInput>; cursor?: AppWhereUniqueInput; take?: number; skip?: number; distinct?: Enumerable<...>; }'.
The types of 'cursor.id' are incompatible between these types.
Type 'string' is not assignable to type 'number'.
30 this.prisma.app.findMany({
~
31 ...args,
~~~~~~~~~~~~~~~~~~
32 where
~~~~~~~~~~~~~~~
33 }),
~~~~~~~~~
[10:04:17 AM] Found 2 errors. Watching for file changes.
async findAll(filterAppsDto: FilterAppsDto, baseFilterDto: BaseFilterDto) {
const { name, bundleIdentifier } = filterAppsDto;
const where: Prisma.AppWhereInput = {
AND: [{ name: { contains: name } }, { bundleIdentifier: { contains: bundleIdentifier } }]
};
return findManyCursorConnection(
args =>
this.prisma.app.findMany({
...args,
where
}),
() =>
this.prisma.app.count({
where
}),
baseFilterDto,
{
recordToEdge: app => ({
node: app
})
}
);
}
I was trying to query all records but it was returning an empty object
findPage() {
return findManyCursorConnection(
(args) =>
this.prisma.product.findMany({ ...args, where: { published: true } }),
() => this.prisma.product.count(),
{},
);
}
That was the SQL request automatically generated by Prisma client
prisma:query
SELECT `main`.`Product`.`id`, `main`.`Product`.`createdAt`, `main`.`Product`.`updatedAt`, `main`.`Product`.`name`, `main`.`Product`.`description`, `main`.`Product`.`price`, `main`.`Product`.`sku`, `main`.`Product`.`published` FROM `main`.`Product` WHERE `main`.`Product`.`id` = ? LIMIT ? OFFSET ?
@all-contributors add @ctrlplusb for code and tests
@all-contributors add @marcjulian for code
From the end-user's code it is not possible to implement a user-interface like:
Without effectively re-creating this library (or drastically over-fetching).
For authors interested in adding this to the library, please first have a look at the conversation and review comments in #366.
interface Doctors {
Id: number;
Name: string;
Rating: number;
}
async findCursorByRatingAsync(
after?: string,
before?: string,
first?: number,
last?: number,
): Promise<Connection<Doctors, Edge<Doctors>>> {
const baseArgs: any = {
where: {
Name: '11',
},
orderBy: {
Rating: 'asc',
},
};
const results = await findManyCursorConnection<Doctors, { id: number }>(
() => this.prismaSvc.doctors.findMany(baseArgs),
() => this.prismaSvc.doctors.count({ where: baseArgs.where }),
{ first, last, before, after },
{
getCursor: (record) => ({ id: record.Id }),
encodeCursor: (cursor) => Buffer.from(JSON.stringify(cursor)).toString('base64'),
decodeCursor: (cursor) => JSON.parse(Buffer.from(cursor, 'base64').toString('ascii')),
recordToEdge: (record): DoctorPreviewCursorPage => ({
node: toDoctorPreviewDTO(record),
}),
},
);
return results;
}
Here is my code
I have Doctors
object with numeric unique Id
for that, I used custom cursor
features and
For aggregating some variables, I used recordToEdge
for making DTO
In this situations,
When I have items more than 10 and set first: 3,
All 10 items are displayed in edges
I expected just 3 items displayed
What is wrong?
Hello,
Prisma v3 is out and I think this plugin should work fine with it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.