Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
23450c1
setup basic codegen for nestjs server
aryobenholzner Jun 28, 2025
d886d19
set up generated file structure
aryobenholzner Jun 29, 2025
eb834cd
adapted moustache files
aryobenholzner Jun 29, 2025
1844ae8
fixed imports
aryobenholzner Jun 29, 2025
a1ddef4
adapted templates
aryobenholzner Jun 29, 2025
387e96e
added module bootstrap
aryobenholzner Jun 29, 2025
d4f8807
added model generation
aryobenholzner Jun 29, 2025
07ad56f
fixed error with generic type
aryobenholzner Jun 29, 2025
ac039c5
added README
aryobenholzner Jun 29, 2025
cd4c0d6
added usage clarification to README, introduced module index.ts
aryobenholzner Jun 30, 2025
f55b21f
Update modules/openapi-generator/src/main/resources/typescript-nestjs…
aryobenholzner Jun 30, 2025
a81f776
cleaned up package.mustache, added parameters for versions
aryobenholzner Jun 30, 2025
b9c18c1
cleaned up unneeded boilerplate templates
aryobenholzner Jun 30, 2025
18ee6ac
fixed indentations from templates
aryobenholzner Jun 30, 2025
b769376
implemented useSingleRequestParameter
aryobenholzner Jul 11, 2025
fa06a62
fixed parameter handling
aryobenholzner Jul 11, 2025
dc89dcc
added samples with tests
aryobenholzner Jul 11, 2025
2f05e15
added docs
aryobenholzner Jul 11, 2025
d9903d1
fixed samples
aryobenholzner Jul 25, 2025
71aafbe
corrected docs
aryobenholzner Jul 25, 2025
1dde3d0
Merge branch 'master' into nestjs-server-codegen
aryobenholzner Jul 30, 2025
4d22dbe
updated docs after merge of master
aryobenholzner Jul 30, 2025
a54f6d6
fixed samples build
aryobenholzner Jul 30, 2025
547edec
fixed workflow
aryobenholzner Jul 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ org.openapitools.codegen.languages.TypeScriptFetchClientCodegen
org.openapitools.codegen.languages.TypeScriptInversifyClientCodegen
org.openapitools.codegen.languages.TypeScriptJqueryClientCodegen
org.openapitools.codegen.languages.TypeScriptNestjsClientCodegen
org.openapitools.codegen.languages.TypeScriptNestjsServerCodegen
org.openapitools.codegen.languages.TypeScriptNodeClientCodegen
org.openapitools.codegen.languages.TypeScriptReduxQueryClientCodegen
org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# OpenApi Generator _typescript-nestjs-server_

Usage: The generated output is intended to be its own module, that can be imported into your NestJS App Module. You do not need to change generated files, just import the module and implement the API

Example usage (with the openapi sample `petstore.yaml`):

1. Invoke openapi-generator
```
openapi-generator-cli.jar generate -i petstore.yaml -g typescript-nestjs-server -o api-module/
```
2. implement the contracts from `api-module/api`

`handlers/PetService.ts`:
```typescript
import { Pet, ApiResponse } from "models";
import { Observable } from "rxjs";
import { PetApi } from "../api";
import { Inject, Injectable } from "@nestjs/common";

@Injectable()
export class PetService implements PetApi {
addPet(pet: Pet, request: Request): Pet | Promise<Pet> | Observable<Pet> {
throw new Error("Method not implemented.");
}

deletePet(petId: number, apiKey: string, request: Request): void | Promise<void> | Observable<void> {
throw new Error("Method not implemented.");
}

...
```

3. Import the API Module with a reference to your implementation
`app.module.ts`
```typescript
import { Module } from "@nestjs/common";
import { ApiModule } from "api-module/api.module";
import { PetService } from "./handlers/PetService";
import { UserService } from "./handlers/UserService";
import { StoreService } from "./handlers/StoreService";

@Module({
imports: [
ApiModule.forRoot({
petApi: PetService,
userApi: UserService,
storeApi: StoreService
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
```

You now can regenerate the API module as often as you want without overriding your implementation.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Type } from '@nestjs/common';
{{#apiInfo}}
{{#apis}}
{{#operations}}
import { {{classname}} } from '{{apiPackage}}';
{{/operations}}
{{/apis}}

export type ApiImplementations = {
{{#apis}}
{{#operations}}
{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}: Type<{{classname}}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is the implementer supposed to use this? can you clarify that in the PR description?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the description as well as the committed README

{{/operations}}
{{/apis}}
};
{{/apiInfo}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { DynamicModule, Module, Provider } from '@nestjs/common';
import { ApiImplementations } from './api-implementations'
{{#apiInfo}}
{{#apis}}
import { {{classname}} } from './{{apiPackage}}';
import { {{classname}}Controller } from './controllers';
{{/apis}}
{{/apiInfo}}

@Module({})
export class ApiModule {
static forRoot(apiImplementations: ApiImplementations): DynamicModule {
const providers: Provider[] = [
{{#apiInfo}}
{{#apis}}
{
provide: {{classname}},
useClass: apiImplementations.{{#lambda.camelcase}}{{classname}}{{/lambda.camelcase}}
},

{{/apis}}
{{/apiInfo}}
];

return {
module: ApiModule,
controllers: [
{{#apiInfo}}
{{#apis}}
{{classname}}Controller,
{{/apis}}
{{/apiInfo}}
],
providers: [...providers],
exports: [...providers]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}}';

@Injectable()
export abstract class {{classname}} {
{{#operations}}
{{#operation}}
abstract {{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}, {{/allParams}} request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}>;

{{/operation}}
{{/operations}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
export * from './{{classFilename}}';
{{/operations}}
{{/apis}}
{{/apiInfo}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Controller{{#httpMethods}}, {{.}}{{/httpMethods}}, Req } from '@nestjs/common';
import { Observable } from 'rxjs';
import { {{classname}} } from '../{{apiPackage}}';
import { {{#tsImports}}{{classname}}, {{/tsImports}} } from '../{{modelPackage}}';

@Controller()
export class {{classname}}Controller {
constructor(private readonly {{classVarName}}: {{classname}}) {}

{{#operations}}
{{#operation}}
@{{#vendorExtensions.x-http-method}}{{.}}{{/vendorExtensions.x-http-method}}{{^vendorExtensions.x-http-method}}{{httpMethod}}{{/vendorExtensions.x-http-method}}('{{path}}')
{{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}, {{/allParams}}@Req() request: Request): {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}} | Promise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> | Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}void{{/returnType}}> {
return this.{{classVarName}}.{{operationId}}({{#allParams}}{{paramName}}, {{/allParams}} request);
}

{{/operation}}
{{/operations}}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{#apiInfo}}
{{#apis}}
{{#operations}}
export * from './{{classFilename}}.controller';
{{/operations}}
{{/apis}}
{{/apiInfo}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# Git push script for {{npmName}}

# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: Not in a git repository"
exit 1
fi

# Get the current branch
CURRENT_BRANCH=$(git branch --show-current)

echo "Pushing to git repository..."
echo "Current branch: $CURRENT_BRANCH"

# Add all changes
git add .

# Commit with a default message if no commit message provided
if [ -z "$1" ]; then
git commit -m "Update generated NestJS server code"
else
git commit -m "$1"
fi

# Push to the current branch
git push origin $CURRENT_BRANCH

echo "Successfully pushed to git repository!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# compiled output
/dist
/node_modules

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Storybook build outputs
.out
.storybook-out

# Temporary folders
tmp/
temp/
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{#models}}
{{#model}}
{{#tsImports}}
import { {{classname}} } from './{{filename}}';
{{/tsImports}}


{{#description}}
/**
* {{{.}}}
*/
{{/description}}
{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#isAlias}}{{>modelAlias}}{{/isAlias}}{{^isAlias}}{{#taggedUnions}}{{>modelTaggedUnion}}{{/taggedUnions}}{{^taggedUnions}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{^oneOf}}{{>modelGeneric}}{{/oneOf}}{{/taggedUnions}}{{/isAlias}}{{/isEnum}}
{{/model}}
{{/models}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type {{classname}} = {{dataType}};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{#stringEnums}}
export enum {{classname}} {
{{#allowableValues}}
{{#enumVars}}
{{#enumDescription}}

/**
* {{.}}
*/{{/enumDescription}}
{{name}} = {{{value}}}{{^-last}},{{/-last}}
{{/enumVars}}
{{/allowableValues}}
}
{{/stringEnums}}
{{^stringEnums}}
export const {{classname}} = {
{{#allowableValues}}
{{#enumVars}}
{{#enumDescription}}

/**
* {{.}}
*/
{{/enumDescription}}
{{name}}: {{{value}}}{{^-last}},{{/-last}}
{{/enumVars}}
{{/allowableValues}}
} as const;
export type {{classname}} = typeof {{classname}}[keyof typeof {{classname}}];
{{/stringEnums}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface {{classname}}{{#allParents}}{{#-first}} extends {{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/allParents}} { {{>modelGenericAdditionalProperties}}
{{#vars}}
{{#description}}
/**
* {{{description}}}
{{#deprecated}}
* @deprecated
{{/deprecated}}
*/
{{/description}}
{{^description}}
{{#deprecated}}
/** @deprecated */
{{/deprecated}}
{{/description}}
{{^modelPropertyNamingOriginal}}
{{#isReadOnly}}readonly {{/isReadOnly}}{{{name}}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}} | null{{/isNullable}};
{{/modelPropertyNamingOriginal}}
{{#modelPropertyNamingOriginal}}
{{#isReadOnly}}readonly {{/isReadOnly}}{{#hasSanitizedName}}'{{{baseName}}}'{{/hasSanitizedName}}{{^hasSanitizedName}}{{{name}}}{{/hasSanitizedName}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{#isNullable}} | null{{/isNullable}};
{{/modelPropertyNamingOriginal}}
{{/vars}}
}{{>modelGenericEnums}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{#additionalPropertiesType}}

[key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}};

{{/additionalPropertiesType}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{{#hasEnums}}

{{^stringEnums}}
export namespace {{classname}} {
{{/stringEnums}}
{{#vars}}
{{#isEnum}}
{{#stringEnums}}
export enum {{classname}}{{enumName}} {
{{#allowableValues}}
{{#enumVars}}
{{name}} = {{{value}}}{{^-last}},{{/-last}}
{{/enumVars}}
{{/allowableValues}}
};
{{/stringEnums}}
{{^stringEnums}}
export const {{enumName}} = {
{{#allowableValues}}
{{#enumVars}}
{{name}}: {{{value}}}{{^-last}},{{/-last}}
{{/enumVars}}
{{/allowableValues}}
} as const;
export type {{enumName}} = typeof {{enumName}}[keyof typeof {{enumName}}];
{{/stringEnums}}
{{/isEnum}}
{{/vars}}
{{^stringEnums}}}{{/stringEnums}}
{{/hasEnums}}
Loading