visit
{YOUR_DOMAIN}/[PATH]?[QUERY_PARAMETERS]
e.g.
//test.com/users?userName=test_user&isBlocked=true
In the new way of query annotation, each of the query parameters will have a prefix - the name of the object or unique id in other words. And the properties will be surrounded by square brackets [QUERY_PARAMTER]
. I can rewrite the previous URL to the new one with this approach:
//test.com/users?uq[userName]=test_user&uq[isBlocked]=true
In the example above uq
representing the object with fields userName
and isBlocked
. It can be written as a typical JSON like:
{
uq: {
userName: 'test_user',
isBlocked: true
}
}
import qs from 'qs';
qs.stringify(params, {
arrayFormat: 'brackets',
encodeValuesOnly: true,
skipNulls: true,
});
const paginationQuery = { take: 100, skip: 4000 };
const params = { pq: paginationQuery };
const queryString = qs.stringify(params, {
arrayFormat: 'brackets',
encodeValuesOnly: true,
skipNulls: true,
});
// It eventually will be translated to something like:
// //test.com/users?pq[take]=100&pq[skip]=4000
So it is pretty much done on the FE side with the deepObject
. Depending on what program interface you want to use, the standard Fetch API or some library you will need to add the result query string to the URL you want to reach. It seems more complex but it adds advanced maintainability to the codebase.
@QueryParams('take', Number)
@QueryParams('skip', Number)
@QueryParams('searchText', String)
// e.t.c
@Description('Get run results')
@Get(endpoints.REPORT_DETAILS)
async getRunResults(
@PathParams(WS_ID, Number) workspaceId: number,
@PathParams(MODEL_ID, Number) modelId: number,
@PathParams(RUN_ID, String) runId: string,
@QueryParams(PAGINATION_QUERY, PaginationQuery)
@GenericOf(RunTestView) paginationQuery: PaginationQuery<RunTestView>
): Promise<ApiResponse<PaginationResult<RunTestView>>> {
const paginationResult = await this.reportService.findRunResults(
workspaceId,
modelId,
runId,
paginationQuery
);
return ApiResponse.ok(paginationResult);
}
@Generics('T')
export class PaginationQuery<T> {
@Minimum(1)
@Default(50)
public take?: number;
@Minimum(0)
@Default(0)
public skip?: number;
@Property()
@GenericOf('T')
public order?: Order<T>;
@Property()
public searchText?: string;
@Property()
@GenericOf('T')
public filters?: Filters<T>;
}
Also, it is useful when you have to use query parameters that actually will be used for different logical operations like pagination query and query params to include additional fields in the response like: { pq: paginationQuery, fq: fieldsQuery }
.
I believe it’s a good addition to what we all use in query parameters. It gives more maintainability to the codebase and decreases redundant code duplication in particular scenarios. The deepObject
also allows for maintaining model validation in the class and not spreading duplication of common query parameters constraints. It provides the ability to logically combine parameters.