Skip to content
On this page

Parse 🔎 ​

The last step of the whole process is to parse the transpiled query string, to an efficient data structure. The result object (ParseOutput) can contain an output for each Parameter/URLParameter.

TIP

Check out the API-Reference of each parameter for output formats and examples.

Example ​

The following example is based on the assumption, that the following packages are installed:

For explanation purposes, three simple entities with relations between them are declared to demonstrate the usage on the backend side.

entities.ts

typescript
import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    OneToMany,
    JoinColumn,
    ManyToOne
} from "typeorm";

@Entity()
export class User {
    @PrimaryGeneratedColumn({unsigned: true})
    id: number;

    @Column({type: 'varchar', length: 30})
    @Index({unique: true})
    name: string;

    @Column({type: 'varchar', length: 255, default: null, nullable: true})
    email: string;
    
    @Column({type: 'int', nullable: true})
    age: number

    @ManyToOne(() => Realm, { onDelete: 'CASCADE' })
    realm: Realm;

    @OneToMany(() => User, { onDelete: 'CASCADE' })
    items: Item[];
}

@Entity()
export class Realm {
    @PrimaryColumn({ type: 'varchar', length: 36 })
    id: string;

    @Column({ type: 'varchar', length: 128, unique: true })
    name: string;

    @Column({ type: 'text', nullable: true, default: null })
    description: string | null;
}

@Entity()
export class Item {
    @PrimaryGeneratedColumn({unsigned: true})
    id: number;

    @ManyToOne(() => Realm, { onDelete: 'CASCADE' })
    realm: Realm;

    @ManyToOne(() => User, { onDelete: 'CASCADE' })
    user: User;
}

Separate ​

The following variant will parse and apply the query parameters in two steps.

After the ParseOutput is generated using the parseQuery function, the output can be applied on the typeorm QueryBuilder using the applyQueryParseOutput function of the typeorm-extension.

typescript
import { Request, Response } from 'express';

import {
    parseQuery,
    ParseOutput
} from 'rapiq';

import {
    applyQueryParseOutput,
    useDataSource
} from 'typeorm-extension';

/**
 * Get many users.
 *
 * Request example
 * - url: /users?page[limit]=10&page[offset]=0&include=realm&filter[id]=1&fields=id,name
 *
 * @param req
 * @param res
 */
export async function getUsers(req: Request, res: Response) {
    const output: ParseOutput = parseQuery<User>(req.query, {
        defaultPath: 'user',
        fields: {
            allowed: ['id', 'name', 'realm.id', 'realm.name'],
        },
        filters: {
            allowed: ['id', 'name', 'realm.id'],
        },
        relations: {
            allowed: ['items', 'realm']
        },
        pagination: {
            maxLimit: 20
        },
        sort: {
            allowed: ['id', 'name', 'realm.id'],
        }
    });

    const dataSource = await useDataSource();
    const repository = dataSource.getRepository(User);
    const query = repository.createQueryBuilder('user');

    // -----------------------------------------------------

    // apply parsed data on the db query.
    applyQueryParseOutput(query, output);

    // -----------------------------------------------------

    const [entities, total] = await query.getManyAndCount();

    return res.json({
        data: {
            data: entities,
            meta: {
                total,
                ...output.pagination
            }
        }
    });
}

Compact ​

Another way is to use the applyQuery function which will parse and apply the query parameters in one step.

This is much shorter than the previous example and has less explicit dependencies âš¡.

typescript
import {Request, Response} from 'express';

import {
    applyQuery,
    useDataSource
} from 'typeorm-extension';

import { User } from './entities';

/**
 * Get many users.
 *
 * Request example
 * - url: /users?page[limit]=10&page[offset]=0&include=realm&filter[id]=1&fields=id,name
 *
 * @param req
 * @param res
 */
export async function getUsers(req: Request, res: Response) {
    const dataSource = await useDataSource();
    const repository = dataSource.getRepository(User);
    const query = repository.createQueryBuilder('user');

    // -----------------------------------------------------
    
    const { pagination } = applyQuery(query, req.query, {
        // defaultAlais is an alias for the defaultPath option
        defaultAlias: 'user',
        fields: {
            // The fields id & name of the realm entity can only be used, 
            // if the relation 'realm' is included.
            allowed: ['id', 'name', 'realm.id', 'realm.name']
        },
        // only allow filtering users by id & name
        filters: {
            // realm.id can only be used as filter key, 
            // if the relation 'realm' is included.
            allowed: ['id', 'name', 'realm.id']
        },
        relations: {
            allowed: ['items', 'realm']
        },
        // only allow to select 20 items at maximum.
        pagination: {
            maxLimit: 20
        },
        sort: {
            // profile.id can only be used as sorting key,
            // if the relation 'realm' is included.
            allowed: ['id', 'name', 'realm.id']
        }
    });

    // -----------------------------------------------------

    const [entities, total] = await query.getManyAndCount();

    return res.json({
        data: {
            data: entities,
            meta: {
                total,
                ...pagination
            }
        }
    });
}