Skip to content

Commit

Permalink
fix: bring back MongoDB id property to API response (#309)
Browse files Browse the repository at this point in the history
  • Loading branch information
fityannugroho authored Feb 26, 2024
1 parent 2948a21 commit 678c88e
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 208 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ API that provides information on the **administrative areas of Indonesia**, from

Built with [NestJS framework](https://nestjs.com) and writen in TypeScript. [Prisma](https://www.prisma.io) is used as the ORM to interact with any kind of databases (MySQL, PostgreSQL, and MongoDB).

> **⚠️ Upgrading to v3.0.0 ⚠️**
> **Note!**
>
> Since [v3.0.0](https://github.com/fityannugroho/idn-area/releases/tag/v3.0.0), **Node.js v18** or higher is required.
> If you choose MongoDB as the [database provider](/docs/installation.md#prerequisite), **the `id` property will be added to the response**. See [issue #308](https://github.com/fityannugroho/idn-area/issues/308).
>
> _This `id` property is **not present** if you use MySQL or PostgreSQL as the database provider._
## Getting Started

Expand Down
34 changes: 18 additions & 16 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- [Prerequisite](#prerequisite)
- [Installation Steps](#installation-steps)
- [Installation with Docker](#installation-with-docker)
- [Run the Test](#run-the-test)

---
Expand All @@ -18,45 +19,46 @@

1. Clone the repository

Use [`git clone`](https://www.git-scm.com/docs/git-clone) command, to clone this repository using HTTPS or SSH.
Use [`git clone`](https://www.git-scm.com/docs/git-clone) command, to clone this repository using HTTPS or SSH.

1. Install the dependencies

Use `npm install` command, to install all the dependencies.
Use `npm install` command, to install all the dependencies.

1. Configure the environment variables

- Create `.env` file by simply copying the **`.env.example` file** and rename it.
- Create `.env` file by simply copying the **`.env.example` file** and rename it.

- Set the `APP_HOST` and `APP_PORT`.
- Set the `APP_HOST` and `APP_PORT`.

- You can enable [rate limiting](https://docs.nestjs.com/security/rate-limiting) by setting the **`APP_ENABLE_THROTTLE`** to be `true`. You also can customize the `APP_THROTTLE_TTL` and `APP_THROTTLE_LIMIT` as desired.
- You can enable [rate limiting](https://docs.nestjs.com/security/rate-limiting) by setting the **`APP_ENABLE_THROTTLE`** to be `true`. You also can customize the `APP_THROTTLE_TTL` and `APP_THROTTLE_LIMIT` as desired.

- You can also customize the pagination feature by setting the **`APP_PAGINATION_MAX_PAGE_SIZE`** and **`APP_PAGINATION_DEFAULT_PAGE_SIZE`**.
- You can also customize the pagination feature by setting the **`APP_PAGINATION_MAX_PAGE_SIZE`** and **`APP_PAGINATION_DEFAULT_PAGE_SIZE`**.

> [!NOTE]
> Set the `APP_PAGINATION_MAX_PAGE_SIZE` value wisely, as it will determine the amount of resource usage (the size of queries to the database).
> **Note!**
>
> Set the `APP_PAGINATION_MAX_PAGE_SIZE` value wisely, as it will determine the amount of resource usage (the size of queries to the database).
- Set the `DB_PROVIDER` with the data source provider you want to use. Current supported providers: 'mongodb', 'postgresql', and 'mysql'. See the details [here](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields).
- Set the `DB_PROVIDER` with the data source provider you want to use. Current supported providers: 'mongodb', 'postgresql', and 'mysql'. See the details [here](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields).

- Set the `DB_HOST`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD`, `DB_USERNAME`, and `DB_PASSWORD`. See the [connection string](https://pris.ly/d/connection-strings) documentation.
- Set the `DB_HOST`, `DB_PORT`, `DB_USERNAME`, `DB_PASSWORD`, `DB_USERNAME`, and `DB_PASSWORD`. See the [connection string](https://pris.ly/d/connection-strings) documentation.

> If you use local database, you must grant **read-write access** to the database.
> If you use local database, you must grant **read-write access** to the database.
1. Generate the database

Run **`npm run db:migrate`** command to generate the database.
Run **`npm run db:migrate`** command to generate the database.

> You can use `npx prisma migrate deploy` command to run migration in **non-development environments** and if you are using any database providers **other than MongoDB**.
> See the details [here](https://www.prisma.io/docs/reference/api-reference/command-reference#migrate-deploy).
> You can use `npx prisma migrate deploy` command to run migration in **non-development environments** and if you are using any database providers **other than MongoDB**.
> See the details [here](https://www.prisma.io/docs/reference/api-reference/command-reference#migrate-deploy).
1. Seed the data

Run **`npm run db:seed`** command to seed the data.
Run **`npm run db:seed`** command to seed the data.

1. Run the app

Use `npm run start` command, to run the app.
Use `npm run start` command, to run the app.

### Installation with Docker

Expand Down
9 changes: 0 additions & 9 deletions src/common/interceptor/paginate.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Injectable } from '@nestjs/common';
import { isDBProvider } from '../../../common/utils/db/provider';
import {
TransformInterceptor,
TransformedResponse,
Expand Down Expand Up @@ -28,14 +27,6 @@ export class PaginateInterceptor<T> extends TransformInterceptor<
PaginatedResponse<T>
> {
protected transformValue({ data, meta }: PaginatedReturn<T>) {
// Remove the `id` property from the data if the database provider is MongoDB.
if (isDBProvider('mongodb')) {
data = (data as any[]).map(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
({ id, ...item }) => ({ id: undefined, ...item }) as T,
);
}

return { data, meta: { pagination: meta } };
}
}
18 changes: 1 addition & 17 deletions src/common/interceptor/transform.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { isDBProvider } from '../../../common/utils/db/provider';

export type WrappedData<
Data,
Expand Down Expand Up @@ -41,22 +40,7 @@ export class TransformInterceptor<T, R extends TransformedResponse<T>>
constructor(private reflector: Reflector) {}

protected transformValue(val: any): WrappedData<T> {
const res: WrappedData<T> = isDataWrapped<T>(val) ? val : { data: val };

// Remove the `id` property from the data if the database provider is MongoDB.
if (isDBProvider('mongodb')) {
if (Array.isArray(res.data)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
res.data = res.data.map(({ id, ...item }) => ({
id: undefined,
...item,
})) as T;
} else {
delete (res.data as { id: string }).id;
}
}

return res;
return isDataWrapped<T>(val) ? val : { data: val };
}

intercept(context: ExecutionContext, next: CallHandler<T>): Observable<R> {
Expand Down
70 changes: 39 additions & 31 deletions test/district.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
provinceRegex,
regencyRegex,
} from './helper/data-regex';
import { getEncodedSymbols } from './helper/utils';
import { expectIdFromMongo, getEncodedSymbols } from './helper/utils';

describe('District (e2e)', () => {
const baseUrl = '/districts';
Expand All @@ -23,11 +23,13 @@ describe('District (e2e)', () => {
const districts = await tester.expectData<District[]>(baseUrl);

districts.forEach((district) => {
expect(district).toEqual({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(districtRegex.name),
regencyCode: district.code.slice(0, 4),
});
expect(district).toEqual(
expectIdFromMongo({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(districtRegex.name),
regencyCode: district.code.slice(0, 4),
}),
);
});
});

Expand Down Expand Up @@ -64,11 +66,13 @@ describe('District (e2e)', () => {
);

districts.forEach((district) => {
expect(district).toEqual({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(new RegExp(testName, 'i')),
regencyCode: district.code.slice(0, 4),
});
expect(district).toEqual(
expectIdFromMongo({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(new RegExp(testName, 'i')),
regencyCode: district.code.slice(0, 4),
}),
);
});
});

Expand All @@ -79,11 +83,13 @@ describe('District (e2e)', () => {
);

districts.forEach((district) => {
expect(district).toEqual({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(districtRegex.name),
regencyCode,
});
expect(district).toEqual(
expectIdFromMongo({
code: expect.stringMatching(districtRegex.code),
name: expect.stringMatching(districtRegex.name),
regencyCode,
}),
);
});
});
});
Expand All @@ -105,22 +111,24 @@ describe('District (e2e)', () => {
`${baseUrl}/${testCode}`,
);

expect(district).toEqual({
code: testCode,
name: expect.stringMatching(districtRegex.name),
regencyCode: testCode.slice(0, 4),
parent: {
regency: {
code: testCode.slice(0, 4),
name: expect.stringMatching(regencyRegex.name),
provinceCode: testCode.slice(0, 2),
},
province: {
code: testCode.slice(0, 2),
name: expect.stringMatching(provinceRegex.name),
expect(district).toEqual(
expectIdFromMongo({
code: testCode,
name: expect.stringMatching(districtRegex.name),
regencyCode: testCode.slice(0, 4),
parent: {
regency: expectIdFromMongo({
code: testCode.slice(0, 4),
name: expect.stringMatching(regencyRegex.name),
provinceCode: testCode.slice(0, 2),
}),
province: expectIdFromMongo({
code: testCode.slice(0, 2),
name: expect.stringMatching(provinceRegex.name),
}),
},
},
});
}),
);
});
});

Expand Down
14 changes: 14 additions & 0 deletions test/helper/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isDBProvider } from '@common/utils/db/provider';

/**
* All symbol characters.
*/
Expand All @@ -24,3 +26,15 @@ export function getEncodedSymbols(
.filter((char) => !exclude.includes(char))
.map((char) => encodeURIComponent(char));
}

/**
* Expect the data contains the `id` property if the database provider is MongoDB.
*/
export function expectIdFromMongo(data: Record<keyof any, any>) {
return {
...data,
id: isDBProvider('mongodb')
? expect.stringMatching(/^[0-9a-fA-F]{24}$/)
: undefined,
};
}
Loading

0 comments on commit 678c88e

Please sign in to comment.