Skip to content

Commit f9a4967

Browse files
authored
Merge pull request #17 from rhyek/traditional-imports-also-endpoint-invoke
traditional controller imports + endpoint invoke for tests
2 parents d669163 + 5ad1ce3 commit f9a4967

21 files changed

+1259
-260
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 1.3.0 (2025-04-12)
4+
5+
### Features
6+
7+
- Added support for traditional controller imports + explicit HTTP paths
8+
- Can now call `invoke()` on endpoint instances. Useful for integration testing. [Example](https://github.com/rhyek/nestjs-endpoints/blob/1b1242348ebc77abad5ad0c67ab372690102d736/packages/test/test-app-express-cjs/test/app.e2e-spec.ts#L467).
9+
310
## 1.2.0 (2025-04-05)
411

512
### Features

README.md

Lines changed: 104 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,24 @@ const { data: msg, error, status } = useHelloWorld();
3333

3434
## Features
3535

36-
- **Easy setup:** Automatically scans your project for endpoint files.
3736
- **Stable:** Produces regular **NestJS Controllers** under the hood.
38-
- **File-based routing:** Endpoints' HTTP paths are based on their path on disk.
37+
- **Automatic setup**:
38+
- Scans your project for endpoint files.
39+
- **File-based routing:** Endpoints' HTTP paths are based on their path on disk.
40+
- **Traditional setup**: Explicitly set HTTP paths and import endpoints like regular controlllers.
3941
- **Schema validation:** Compile and run-time validation of input and output values using Zod schemas.
4042
- **End-to-end type safety:** Auto-generates `axios` and `@tanstack/react-query` client libraries. Internally uses `@nestjs/swagger`, [nestjs-zod](https://github.com/BenLorantfy/nestjs-zod), and [orval](https://orval.dev/).
4143
- **HTTP adapter agnostic:** Works with both Express and Fastify NestJS applications.
4244

43-
## Getting Started
44-
45-
### Installation
45+
## Installation
4646

4747
```bash
4848
npm install nestjs-endpoints @nestjs/swagger zod
4949
```
5050

51-
### Setup
51+
## Usage
52+
53+
### Option 1. Scanned endpoints/file-based routing
5254

5355
`src/app.module.ts`
5456

@@ -66,8 +68,6 @@ import { EndpointsRouterModule } from 'nestjs-endpoints';
6668
export class AppModule {}
6769
```
6870

69-
## Basic Usage
70-
7171
`src/endpoints/user/find.endpoint.ts`
7272

7373
```typescript
@@ -156,7 +156,7 @@ null%
156156
{"id":1}%
157157
```
158158

159-
## File-based routing
159+
### File-based routing
160160

161161
HTTP paths for endpoints are derived from the file's path on disk:
162162

@@ -174,9 +174,36 @@ Examples (assume `rootDirectory` is `./endpoints`):
174174

175175
> _**Note:**_ Bundled projects via Webpack or similar are not supported.
176176
177+
### Option 2. Traditional imports and paths
178+
179+
You can import endpoints like regular NestJS controllers. No need for `EndpointsRouterModule` in this case.
180+
181+
`src/app.module.ts`
182+
183+
```typescript
184+
import { Module } from '@nestjs/common';
185+
import { healthCheck } from './health-check.ts';
186+
187+
@Module({
188+
controllers: [healthCheck],
189+
})
190+
class AppModule {}
191+
```
192+
193+
`src/health-check.ts`
194+
195+
```typescript
196+
import { endpoint, z } from 'nestjs-endpoints';
197+
198+
export const healthCheck = endpoint({
199+
path: '/status/health',
200+
handler: () => 'ok',
201+
});
202+
```
203+
177204
## Codegen (optional)
178205

179-
You can automatically generate a client SDK for your API that can be used in other backend or frontend projects with the benefit of end-to-end type safety. It uses [orval](https://orval.dev/) internally.
206+
You can automatically generate a client SDK for your API that can be used in other backend or frontend projects with the benefit of end-to-end type safety. It uses [orval](https://orval.dev/) internally. It works with both scanned and manually imported endpoints.
180207

181208
### Using `setupCodegen`
182209

@@ -273,7 +300,7 @@ More examples:
273300
- [axios](https://github.com/rhyek/nestjs-endpoints/blob/f9fc77c0af9439e35e2ed3f26aa3e645795ed44f/packages/test/test-app-express-cjs/test/client.e2e-spec.ts#L15)
274301
- [react-query](https://github.com/rhyek/nestjs-endpoints/tree/main/packages/test/test-react-query-client)
275302

276-
### Manual
303+
### Manual codegen with OpenAPI spec file
277304

278305
If you just need the OpenAPI spec file or prefer to configure orval or some other tool yourself, you can do the following:
279306

@@ -429,3 +456,69 @@ To call this endpoint:
429456
-d '{"userId": 1, "date": "2021-11-03"}'
430457
{"id":1,"date":"2021-11-03T00:00:00.000Z","address":"::1"}%
431458
```
459+
460+
## Testing
461+
462+
You can write end-to-end or integration tests for your endpoints.
463+
464+
### End-to-end tests
465+
466+
Use either the generated client libraries, or regular HTTP requests using `supertest`.
467+
468+
```ts
469+
test('client library', async () => {
470+
const moduleFixture: TestingModule = await Test.createTestingModule({
471+
imports: [AppModule],
472+
}).compile();
473+
const app = moduleFixture.createNestApplication();
474+
await app.init();
475+
await app.listen(0);
476+
const client = createApiClient({
477+
baseURL: await app.getUrl(),
478+
});
479+
await expect(client.userFind({ id: 1 })).resolves.toMatchObject({
480+
data: {
481+
id: 1,
482+
483+
},
484+
});
485+
});
486+
487+
test('supertest', async () => {
488+
const moduleFixture: TestingModule = await Test.createTestingModule({
489+
imports: [AppModule],
490+
}).compile();
491+
const app = moduleFixture.createNestApplication();
492+
await request(app.getHttpServer())
493+
.get('/user/find?id=1')
494+
.expect(200)
495+
.then((resp) => {
496+
expect(resp.body).toMatchObject({
497+
id: 1,
498+
499+
});
500+
});
501+
});
502+
```
503+
504+
### Integration tests
505+
506+
You can also load individual endpoints without having to import your entire application
507+
508+
```ts
509+
import userFindEndpoint from 'src/endpoints/user/find.endpoint.ts';
510+
511+
test('integration', async () => {
512+
const moduleFixture: TestingModule = await Test.createTestingModule({
513+
controllers: [userFindEndpoint],
514+
providers: [DbService],
515+
}).compile();
516+
const app = moduleFixture.createNestApplication();
517+
await app.init();
518+
const userFind = app.get(userFindEndpoint);
519+
await expect(userFind.invoke({ id: 1 })).resolves.toMatchObject({
520+
id: 1,
521+
522+
});
523+
});
524+
```

0 commit comments

Comments
 (0)