165 lines
3.2 KiB
Markdown
165 lines
3.2 KiB
Markdown
# Complete API Implementation Examples
|
|
|
|
## Example 1: API Endpoint with Cursor Pagination
|
|
|
|
```typescript
|
|
import { prisma } from './prisma-client';
|
|
|
|
type GetPostsParams = {
|
|
cursor?: string;
|
|
limit?: number;
|
|
};
|
|
|
|
export async function GET(request: Request) {
|
|
const { searchParams } = new URL(request.url);
|
|
const cursor = searchParams.get('cursor') || undefined;
|
|
const limit = Number(searchParams.get('limit')) || 20;
|
|
|
|
if (limit > 100) {
|
|
return Response.json(
|
|
{ error: 'Limit cannot exceed 100' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const posts = await prisma.post.findMany({
|
|
take: limit,
|
|
skip: cursor ? 1 : 0,
|
|
cursor: cursor ? { id: cursor } : undefined,
|
|
orderBy: { createdAt: 'desc' },
|
|
include: {
|
|
author: {
|
|
select: { id: true, name: true, email: true },
|
|
},
|
|
},
|
|
});
|
|
|
|
const nextCursor = posts.length === limit
|
|
? posts[posts.length - 1].id
|
|
: null;
|
|
|
|
return Response.json({
|
|
data: posts,
|
|
nextCursor,
|
|
hasMore: nextCursor !== null,
|
|
});
|
|
}
|
|
```
|
|
|
|
**Client usage:**
|
|
|
|
```typescript
|
|
async function loadMorePosts() {
|
|
const response = await fetch(`/api/posts?cursor=${nextCursor}&limit=20`);
|
|
const { data, nextCursor: newCursor, hasMore } = await response.json();
|
|
|
|
setPosts(prev => [...prev, ...data]);
|
|
setNextCursor(newCursor);
|
|
setHasMore(hasMore);
|
|
}
|
|
```
|
|
|
|
## Example 2: Filtered Cursor Pagination
|
|
|
|
```typescript
|
|
type GetFilteredPostsParams = {
|
|
cursor?: string;
|
|
authorId?: string;
|
|
tag?: string;
|
|
limit?: number;
|
|
};
|
|
|
|
async function getFilteredPosts({
|
|
cursor,
|
|
authorId,
|
|
tag,
|
|
limit = 20,
|
|
}: GetFilteredPostsParams) {
|
|
const where = {
|
|
...(authorId && { authorId }),
|
|
...(tag && { tags: { some: { name: tag } } }),
|
|
};
|
|
|
|
const posts = await prisma.post.findMany({
|
|
where,
|
|
take: limit,
|
|
skip: cursor ? 1 : 0,
|
|
cursor: cursor ? { id: cursor } : undefined,
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
|
|
return {
|
|
data: posts,
|
|
nextCursor: posts.length === limit ? posts[posts.length - 1].id : null,
|
|
};
|
|
}
|
|
```
|
|
|
|
**Index requirement:**
|
|
|
|
```prisma
|
|
model Post {
|
|
id String @id @default(cuid())
|
|
authorId String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([authorId, createdAt, id])
|
|
}
|
|
```
|
|
|
|
## Example 3: Small Admin Table with Offset
|
|
|
|
```typescript
|
|
type GetAdminUsersParams = {
|
|
page?: number;
|
|
pageSize?: number;
|
|
search?: string;
|
|
};
|
|
|
|
async function getAdminUsers({
|
|
page = 1,
|
|
pageSize = 50,
|
|
search,
|
|
}: GetAdminUsersParams) {
|
|
const skip = (page - 1) * pageSize;
|
|
|
|
const where = search
|
|
? {
|
|
OR: [
|
|
{ email: { contains: search, mode: 'insensitive' as const } },
|
|
{ name: { contains: search, mode: 'insensitive' as const } },
|
|
],
|
|
}
|
|
: {};
|
|
|
|
const [users, total] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where,
|
|
skip,
|
|
take: pageSize,
|
|
orderBy: { createdAt: 'desc' },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
role: true,
|
|
createdAt: true,
|
|
},
|
|
}),
|
|
prisma.user.count({ where }),
|
|
]);
|
|
|
|
return {
|
|
data: users,
|
|
pagination: {
|
|
page,
|
|
pageSize,
|
|
totalPages: Math.ceil(total / pageSize),
|
|
totalRecords: total,
|
|
hasNext: page < Math.ceil(total / pageSize),
|
|
hasPrev: page > 1,
|
|
},
|
|
};
|
|
}
|
|
```
|