Skip to content

Commit

Permalink
feat(user): add the ability to query paginated users
Browse files Browse the repository at this point in the history
  • Loading branch information
NansD committed Feb 24, 2023
1 parent 3f391d3 commit 3c67499
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { APP_INTERCEPTOR } from '@nestjs/core';
import { ContextInterceptor } from './libs/application/context/ContextInterceptor';
import { ExceptionInterceptor } from '@libs/application/interceptors/exception.interceptor';
import { postgresConnectionUri } from './configs/database.config';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

const interceptors = [
{
Expand All @@ -29,6 +31,10 @@ const interceptors = [
connectionUri: postgresConnectionUri,
}),
CqrsModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
}),

// Modules
UserModule,
Expand Down
36 changes: 36 additions & 0 deletions src/libs/api/graphql/paginated.graphql-response.base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Field, ObjectType, Int } from '@nestjs/graphql';
import { Type } from '@nestjs/common';
// count limit page

export interface IPaginatedType<T> {
data: T[];
count: number;
limit: number;
page: number;
}

export function PaginatedGraphqlResponse<T>(
classRef: Type<T>,
): Type<IPaginatedType<T>> {
@ObjectType({ isAbstract: true })
abstract class PaginatedType implements IPaginatedType<T> {
constructor(props: IPaginatedType<T>) {
this.count = props.count;
this.limit = props.limit;
this.page = props.page;
this.data = props.data;
}
@Field(() => Int)
page: number;

@Field(() => Int)
count: number;

@Field()
limit: number;

@Field(() => [classRef])
readonly data: T[];
}
return PaginatedType as Type<IPaginatedType<T>>;
}
30 changes: 30 additions & 0 deletions src/modules/user/dtos/graphql/user.graphql-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ResponseBase } from '@libs/api/response.base';
import { Field, ObjectType } from '@nestjs/graphql';

@ObjectType()
export class UserGraphqlResponseDto extends ResponseBase {
@Field({
description: "User's identifier",
})
id: string;

@Field({
description: "User's email address",
})
email: string;

@Field({
description: "User's country of residence",
})
country: string;

@Field({
description: 'Postal code',
})
postalCode: string;

@Field({
description: 'Street where the user is registered',
})
street: string;
}
12 changes: 12 additions & 0 deletions src/modules/user/dtos/graphql/user.paginated-gql-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { PaginatedGraphqlResponse } from '../../../../libs/api/graphql/paginated.graphql-response.base';

import { UserGraphqlResponseDto } from './user.graphql-response.dto';

@ObjectType()
export class UserPaginatedGraphqlResponseDto extends PaginatedGraphqlResponse(
UserGraphqlResponseDto,
) {
@Field(() => [UserGraphqlResponseDto])
data: UserGraphqlResponseDto[];
}
38 changes: 38 additions & 0 deletions src/modules/user/queries/find-users/find-users.graphql-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { QueryBus } from '@nestjs/cqrs';
import { Args, Query, Resolver } from '@nestjs/graphql';
import { Result } from 'oxide.ts/dist';
import { ResponseBase } from '../../../../libs/api/response.base';
import { Paginated } from '../../../../libs/ddd';
import { PaginatedParams } from '../../../../libs/ddd/query.base';
import { UserModel } from '../../database/user.repository';
import { UserPaginatedGraphqlResponseDto } from '../../dtos/graphql/user.paginated-gql-response.dto';
import { FindUsersQuery } from './find-users.query-handler';

@Resolver()
export class FindUsersGraphqlResolver {
constructor(private readonly queryBus: QueryBus) {}
@Query(() => UserPaginatedGraphqlResponseDto)
async findUsers(
@Args('options', { type: () => String })
options: PaginatedParams<FindUsersQuery>,
): Promise<UserPaginatedGraphqlResponseDto> {
const query = new FindUsersQuery(options);
const result: Result<
Paginated<UserModel>,
Error
> = await this.queryBus.execute(query);

const paginated = result.unwrap();
const response = new UserPaginatedGraphqlResponseDto({
...paginated,
data: paginated.data.map((user) => ({
...new ResponseBase(user),
email: user.email,
country: user.country,
street: user.street,
postalCode: user.postalCode,
})),
});
return response;
}
}
6 changes: 5 additions & 1 deletion src/modules/user/user.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FindUsersQueryHandler } from './queries/find-users/find-users.query-han
import { UserMapper } from './user.mapper';
import { CqrsModule } from '@nestjs/cqrs';
import { USER_REPOSITORY } from './user.di-tokens';
import { FindUsersGraphqlResolver } from './queries/find-users/find-users.graphql-resolver';

const httpControllers = [
CreateUserHttpController,
Expand All @@ -23,7 +24,10 @@ const messageControllers = [CreateUserMessageController];

const cliControllers: Provider[] = [CreateUserCliController];

const graphqlResolvers: Provider[] = [CreateUserGraphqlResolver];
const graphqlResolvers: Provider[] = [
CreateUserGraphqlResolver,
FindUsersGraphqlResolver,
];

const commandHandlers: Provider[] = [CreateUserService, DeleteUserService];

Expand Down

0 comments on commit 3c67499

Please sign in to comment.