Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import type {
ApolloServerPlugin,
GraphQLRequestListener,
} from '@apollo/server';
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateNextHandler } from '@as-integrations/next';
import { GraphQLError } from 'graphql';
import type { NextRequest } from 'next/server';
import { auth } from '@/lib/auth/auth';
import { canUserPerformOperation } from '@/lib/graphql/operationsConfig';
import { resolvers } from '@/lib/graphql/resolvers';
import { resolvers as scalarResolvers, typeDefs } from '@/lib/graphql/schema';
import { prisma } from '@/lib/prisma/prisma';
import type { GraphQLContext } from '../../../types/graphql/context';
/**
* Plugin to log GraphQL operations (development only)
*/
const loggingPlugin: ApolloServerPlugin<GraphQLContext> = {
async requestDidStart(): Promise<GraphQLRequestListener<GraphQLContext>> {
return {
async didResolveOperation(requestContext) {
const { operationName } = requestContext.request;
console.log(`[GraphQL] Operation: ${operationName || 'anonymous'}`);
},
};
},
};
/**
* Plugin to validate operation permissions
*/
const authPlugin: ApolloServerPlugin<GraphQLContext> = {
async requestDidStart(): Promise<GraphQLRequestListener<GraphQLContext>> {
return {
async didResolveOperation(requestContext) {
const { operationName } = requestContext.request;
const { role } = requestContext.contextValue;
// Check operation permissions
if (operationName && !canUserPerformOperation(operationName, role)) {
throw new GraphQLError(
`Unauthorized: You don't have permission to perform '${operationName}'`,
{
extensions: {
code: 'FORBIDDEN',
http: { status: 403 },
requiredRole: role || 'Authenticated user',
},
},
);
}
},
};
},
};
// Create Apollo Server instance
const server = new ApolloServer<GraphQLContext>({
typeDefs,
resolvers: { ...scalarResolvers, ...resolvers },
plugins: [loggingPlugin, authPlugin],
introspection: true,
});
/**
* Create GraphQL context for each request
*/
const handler = startServerAndCreateNextHandler<NextRequest, GraphQLContext>(
server,
{
context: async (): Promise<GraphQLContext> => {
// Get NextAuth v5 session
const session = await auth();
return {
userId: session?.user?.id,
role: session?.user?.role,
operationName: null,
prisma,
};
},
},
);
const wrappedHandler = async (
request: NextRequest,
_context: { params: Promise<Record<string, never>> },
): Promise<Response> => {
if (request.method === 'GET') {
return new Response(
JSON.stringify({
message: 'GraphQL API endpoint. Use POST requests.',
endpoint: '/api/graphql',
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
);
}
const requestBody = await request.clone().text();
if (!requestBody.trim()) {
return new Response(
JSON.stringify({
error: 'Empty request body',
message: 'GraphQL POST requests must include a JSON body.',
}),
{
status: 400,
headers: { 'Content-Type': 'application/json' },
},
);
}
return handler(request);
};
// Export Next.js route handlers
export const POST = wrappedHandler;
|