Today we dive into the seamless initial setup of the NAP(P) stack: Angular, NestJs, PostgreSQL, and Prisma. This blog post serves as a guide to the straightforward configuration that lays the foundation for a powerful development environment for TypeScript.

What sets this stack apart is not just its technological strength but the symphony it creates through a shared language – both frontend and backend are written in TypeScript, fostering a unified development experience while maintaining clean interfaces between database, backend, and frontend.

 

How to setup PostgreSQL

Database Before we proceed, let's ensure you have a PostgreSQL database up and running. If not, don't worry - below you can find a simple Docker Compose for the setup.

Add the following docker-compose.yml to the root of your project 1

1 version: "3.9"

2

3 services:

4 napp-database:

5 image: postgres:latest

6 hostname: napp-database

7 container_name: napp-database

8 restart: always

9 environment:

10 POSTGRES_USER: napp-user

11 POSTGRES_PASSWORD: napp-pa$word

12 POSTGRES_DB: napp

13 ports:

14 - "5432:5432"

 

Now you just need to run

1 docker compose up

 

How to setup NestJS

Now, let's delve into the backend realm. Leveraging the power of NestJS and Prisma, we'll seamlessly configure our server and create a simple data model.

The setup cannot be simpler: Just install the NestJS CLI, create your first project, and start it.

1 npm install -g @nestjs/cli

2 nest new napp-api // this will create a new folder

3 cd napp-api

4 nmp run start // your project is now running

 

To ensure everything is running as intended, we simply do a GET

localhost:3000 

 and check if it is returning a 200 code, which should send "Hello world".

 

How to setup Prisma

Also, the Prisma setup is quite straightforward. It simply needs to be installed and initialized.

1 npm install prisma --save-dev

2 npx prisma init

 

To set the DATABASE_URL in the .env file, make sure your database is up and running.

1 DATABASE_URL="postgresql://napp-user:napppa$word@localhost:5432/napp?schema=public"

 

Exploring Prisma Schema The Prisma schema acts as the blueprint for your database, defining tables, fields, and relationships. In this guide, we will provide a simple example that shows some of the features, such as a many-to-many relationship and different data types.

For the setup, this schema is not needed and should just serve as an example. Feel free to create your own schema.

1 generator client {

2 provider = "prisma-client-js"

3 }

4

5 datasource db {

6 provider = "postgresql"

7 url = env("DATABASE_URL")

8 }

9

10 model Player

{ 11 id Int @id @default(autoincrement())

12 name String

13 PlayerStrengthInSeason PlayerStrengthInSeason[]

14 }

15

16 model Season {

17 id Int @id @default(autoincrement())

18 year Int

19 PlayerStrengthInSeason PlayerStrengthInSeason[]

20 }

21

22 model PlayerStrengthInSeason {

23 playerId Int

24 player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)

25 seasonId Int

26 season Season @relation(fields: [seasonId], references: [id], onDelete: Cascade)

27 strength Int

28

29 @@id([playerId, seasonId])

30 }

prisma.schema

Initialize your first migration. This will create all the tables with their relations as specified in your schema.prisma. Now, let's take the exciting step of initializing your first migration. This process, guided by your schema.prisma, will bring to life all the tables and their relations, setting the stage for a seamlessly orchestrated database tailored to your application's specifications.

 

1 npx prisma migrate dev --name init

 

Congrats! If you inspect your database, you should see the created tables and also your first migration in the Prisma/migrations folder.

Finally, we will enable CORS to allow requests from our local frontend.

 

1 async function bootstrap()

{ 2 const app = await NestFactory.create(AppModule);

3 app.enableCors();

4 await app.listen(3000);

5 }

 

How to setup Angular

We're now shifting our focus to the frontend where we will concentrate on Angular. In this section, we'll guide you through the fundamental steps of configuring your Angular project, establishing the HTTP client, and making the first call to our already running backend.

If not done already, install the Angular CLI.

1 npm install -g @angular/cli

 

Create and run your Angular application with the CLI

1 ng new napp-ui // use your app name

2 cd napp-ui

3 ng serve // start the application

 

You should now see the default Angular starter application running at localhost:4200.

 

How to setup HTTP Client

Add the HttpClientModule to your app configuration that is used in bootstrapApplication in main.ts

1 export const appConfig: ApplicationConfig = {

2 providers: [provideRouter(routes), importProvidersFrom(HttpClientModule)]

3 };

 app.config.ts

 

Now we just need to import the client by adding it to the constructor of our component. For testing purposes, we also added a helloMessage.

 

1 helloMessage = '';

2 constructor(private http: HttpClient) {

3 }

4

5 ngOnInit(): void {

6 this.http.get('http://localhost:3000', { responseType: 'text'}).subscribe((data) => {

7 this.helloMessage = data;

8 });

9 }

app.component.ts

 

Finally, we just need to replace the content in the app.component.html with our helloMessage .

 

1 <main class="main">
2   <h1> {{helloMeassage}}</h1>
3 </main>
4 <router-outlet></router-outlet>
5

 app.component.html

 

The NAPP Stack: the development enviroment for TypeScript is ready

Congrats! You finished the setup and should now see an unstyled "Hello World!" in your browser It is fairly straightforward and enough to build your first prototype with the stack from this point onwards.

However, we want to dive into real-world scenarios in which you want a monorepository, automated generation of interfaces, and a setup which allows developers to work with it even if they don't know the specifics of all parts supplied.

You might want to know how testing, especially end-to-end testing, can be done. Before we dive into these topics, let's recap what we saw so far:

 

1. Consistency in Architecture: Both Angular and NestJS follow a modular, componentbased architecture. This consistency streamlines development, as developers can apply similar patterns and practices across the frontend and backend, leading to a more cohesive codebase.

2. TypeScript Integration: Both Angular and NestJS are built with TypeScript, a superset of JavaScript that adds static typing. This common language between frontend and backend reduces friction in development, enhances code readability, and enables seamless data sharing between layers.

3. Code Reusability: With shared concepts like interceptors, guards, and pipes, developers can reuse code and logic across frontend and backend components. This not only saves time and effort but also promotes consistency and maintainability throughout the application. (We actually wrote a blog post about this)

4. Enhanced Developer Productivity: Angular's rich ecosystem of libraries, tools, and prebuilt components complements NestJS's robust framework, providing developers with a comprehensive toolkit for rapid development. Features like Angular CLI and NestJS CLI streamline common tasks, boosting productivity.

5. Community Support: Both Angular and NestJS have vibrant communities with active contributors, extensive documentation, and a wealth of resources, including tutorials, forums, and third-party libraries. Leveraging these communities can help developers troubleshoot issues, stay updated on best practices, and accelerate development.

6. Scalability and Maintainability: The modular architecture of both frameworks facilitates scalability and maintainability. Developers can easily add new features, refactor existing 6. code, and scale the application horizontally or vertically as needed, without introducing significant complexity.

 

Real-world Scenarios (What You Really Need)

Now we can dive into different questions you might be asking yourself when building a realworld application with NAPP.

 

API Interface Generation

In a real-world scenario, it is not feasible to call the endpoints we are about to create without an OpenAPI specification. Luckily, NestJS comes with a module to generate one:

1 npm install @nestjs/swagger With this package, you get a lot of annotations and convenience to provide an OpenAPI specification. Here is an example of an entity you could supply:

1 export class PlayerEntity {

2 @ApiProperty()

3 id: string

4 @ApiProperty()

5 name: string

6

7 constructor(partialPlayer: Partial) {

8 Object.assign(this, partialPlayer)

9 }

10 } PlayerEntity.ts

 

For a detailed overview, check out the documentation from NestJS. One quick example you should consider is writing the specification to a file on startup in dev mode, to be able to detect changes to the API easily:

 

1 const document = SwaggerModule.createDocument(app, config);

2 SwaggerModule.setup(`${pathPrefix}/docs`, app, document);

3 if (process.env.NODE_ENV !== 'production') {

4 fs.writeFileSync(

5 path.resolve(

6 __dirname +

7 'api-spec/api-swagger-spec.json',

8 ),

9 JSON.stringify(document),

10 );

11 }

 main.ts

 

Now you can easily use the specification as a source for generating any client you might need. Due to the popularity of OpenAPI and the various generators out there, the backend now provides a basically universally usable HTTP-based API. In the NAPP-stack, we usually work with ng-openapi-gen in our Angular applications.

So in your Angular project, you can install it:

 

1 npm install -D ng-openapi-gen

 

The easiest way to use it is by adding a new script to your package.json:

1 {

2 ...

3 "scripts:": {

4 ...

5 "generate-api-types": "ng-openapi-gen --input {pathToFile}/api-swagger-spec.json --output {projectRoot}/out/backend-api --enumStyle pascal -- ignoreUnusedModels false",

6 ...

7 }

8 ...

9 }

 package.json

 

Now you can easily use the types you generated by importing the resulting module in your application:

1 @NgModule({

2 declarations: [AppComponent],

3 imports: [

4 ApiModule.forRoot({rootUrl: 'http://localhost:3000'}),

5 HttpClientModule

6 ],

7 bootstrap: [AppComponent],

8 })

9 export class AppModule {

10 }

 AppModule.ts

 

With this setup, your backend provides a perfect foundation, and your frontend can easily use this foundation to work with all provided entities.

 

Nx for mono-repo management

Another topic in modern development is the question of how you should organize your codebase into git repositories, especially when building web applications that often include multiple services, UIs, and more, all of which ultimately form one large distributed application.

When using Nx mono-repos, this process can be simplified, and you can work with a single repository no matter how many services you need.

To do this, you can set up nestJS and Angular with a few simple steps:

 

1 npx create-nx-workspace my-workspace --preset=nest

2 npm add -D @nx/angular

3 nx g @nx/angular:app my-frontend

 

In the options for the first command, you already set up a NestJS application for your repository (name of the application, whether you want a Dockerfile, etc.).

The second step adds the plugin you need for Angular development in an Nx repository. This plugin is then used to generate a frontend for your application.

What is great about Nx is you will only need to maintain a single package.json for the entire project and have optimized build and dependency caching mechanisms included.

The features Nx provides are so versatile that only this glimpse can be given here, but in general, it simplifies a developer's life by allowing them to check out one git repo, run only two or three commands, and have a working development environment ready to go.

Authentication & Authorization

In our projects, we are using JWTs everywhere. To be precise, we use OAuth 2.0 and additionally often the OpenID Connect protocol. This allows us to utilize existing libraries for our implementation. A great example for NestJS would be passportJS with the correct strategy applied. An example of how to configure your Nest app for passport can be found in the docs.

While the strategy for our use case is also available as a default strategy for passport, many more are available (>500 to be precise), allowing this implementation to be a growing default for NestJS.

On the other hand, a great example for Angular would be the angular-oauth2-oidc by Manfred Steyer (one of the legends in the Angular community!). The library is very much self-explanatory and can be found on npm.

 

Smooth development environment for TypeScript

In the symphony of NestJS, Angular, PostgreSQL, and Prisma, our journey concludes with an application running after a few minutes. We are running a unified TypeScript stack, and Prisma simplifies our lives in the backend.

While we were focused on the fundamental setup at first, we also saw the strength of this stack truly shining when adapted to more advanced practices and tools. This can include automated code generation of our backend services to the frontend using OpenAPI or using a repository management tool like Nx.

This stack lays a solid foundation, inviting you to explore advanced mechanisms in some follow-up articles. Stay tuned!