Skip to main content

Implementing a New GraphQL Query or Mutation

When tasked with implementing a new GraphQL query or mutation:

  1. Define in GraphQL Schema:

    • Add the query/mutation in queries.graphqls or mutations.graphqls.
    • Define new types in appropriate .graphqls files (e.g., users.graphqls for User-related).
    • Add inputs if needed (e.g., UserInput).
  2. Create/Update DTOs:

    • In web/dto/, create EntityDto.java matching the GraphQL type.
    • Create EntityInput.java extending EntityDto for mutations.
    • Ensure fields match GraphQL schema.
  3. Implement Mapper:

    • In web/, create EntityMapper.java extending BaseMapper<Entity, EntityDto, EntityInput>.
    • Use MapStruct annotations.
    • Implement @AfterMapping to set bidirectional references (e.g., set entity on child objects).
  4. Update Controller:

    • In web/EntityController.java, add @QueryMapping or @MutationMapping method.
    • Inject service, mapper, CurrentUserContext.
    • Resolve currentUserId = currentUserContext.getCurrentUserId() in the controller and pass it to the service where needed.
    • For queries: Call service to get entity, map to DTO.
    • For mutations: Map input to entity, call service update/create, map result to DTO.
    • Add @PreAuthorize for permissions using evaluators.
  5. Update Service:

    • In EntityService.java, add methods for business logic.
    • Accept currentUserId as a method parameter — do NOT inject CurrentUserContext into services.
    • Use repositories for DB access.
    • Handle dependencies on other services (e.g., KeycloakService, SlugService).
  6. Update Repository:

    • In domain/EntityRepository.java, extend BaseRepository<Entity>.
    • Add custom query methods if needed (e.g., @Query for complex joins).
    • For permissions, see MemberRepository for examples of role-based queries.
    • For workspace subclass fields, always query the subclass: SELECT w FROM OrgWorkspace w WHERE ....
  7. Handle Permissions:

    • Use @PreAuthorize("@axveroRolePermissionEvaluator.hasUserAnyOrgRole(#id, 'OWNER')") for org-level.
    • For team/workspace, use hasUserAnyTeamRole, hasUserAnyRoleForTeam (cascading), etc.
    • Roles: OWNER, ADMIN, MEMBER, etc. (see MemberRole enum).
    • Automatic creator membership: Ensure creators are added as OWNER when implementing create methods.
    • Role-based result scoping: If a query must return different data depending on whether the caller is a client or staff (e.g., applications), check ClientService.findClientIdByUserIdAndWorkspaceId(currentUserId, workspaceId) in the service and filter accordingly. Clients always see only their own data; staff see all.
  8. Update Tests:

    • Add integration tests in test/integration/ using HTTP client files.
    • Ensure Testcontainers for DB/Keycloak.
  9. DB Migrations:

    • No DB migrations needed currently; using JPA create-update. Once the project is complete, migrate to Liquibase.
  10. Validate:

    • Run ./mvnw compile first
    • No need to run tests as part of development, but ensure code compiles and passes basic checks.
    • Tests are currently not fully implemented, so focus on production code correctness and structure.
    • Update schemas if needed.