Advanced Practices for Nest.js + Next.js Projects
Tags: nestjs, nextjs, webdev, tutorial
Published: 2022-01-07 11:22:38

This is the second and final part in a series of articles about combining nest.js and NEXT.js. In the first part we created a project and discovered a few basic SSR techniques for this stack. At this point the project can already be used as a base for developing a real website. However there are a few more things we can do: enable Hot Module Replacement for `nest-next`, discover more reusable SSR techniques and learn of subdirectory deployments.

> This post is a translation of [the original articles by me on Habr](https://habr.com/ru/post/570886/). The translation had no input from experienced tech writers or editors. So any feedback towards correcting any mistakes is greatly appreciated.

## Table of Contents

-   [Table of Contents](#table-of-contents)
-   [Introduction](#introduction)
-   [CDN deployment](#cdn-deployment)
-   [HMR and NEXT.js instance caching](#hmr-and-nextjs-instance-caching)
    -   [Hot Reload in nest.js](#hot-reload-in-nestjs)
    -   [Caching NEXT.js instance](#caching-nextjs-instance)
-   [More SSR techniques](#more-ssr-techniques)
    -   [Creating a configuration](#creating-a-configuration)
    -   [AOP for GSSP](#aop-for-gssp)
    -   [Accessing application context](#accessing-application-context)
    -   [Client-side navigation](#client-side-navigation)
-   [Subdirectory deployment](#subdirectory-deployment)
    -   [Development proxy](#development-proxy)
    -   [Adding basePath](#adding-basepath)
    -   [Proxy and basePath](#proxy-and-basepath)
    -   [Fetch wrapper](#fetch-wrapper)
    -   [Proxy without rewrites](#proxy-without-rewrites)
-   [Conclusion](#conclusion)

## Introduction

I should remind you that the final application is available at my GitHub - https://github.com/yakovlev-alexey/nest-next-example - commit history mostly follows the flow of the article. I assume that you are already finished with the first part of series and have the project at the relevant [revision (commit 5139ad554f)](https://github.com/yakovlev-alexey/nest-next-example/tree/5139ad554f7ceabbc18498a04fc764fee6120aeb).

> NEXT.js and nest.js are very similar in naming so I will be always typing NEXT.js (the React framework) in uppercase and  nest.js (the MVC backend framework) in lowercase to differenciate between them more easily.

## CDN deployment

Let's start simple. NEXT.js [supports CDN (Content Delivery Network) deployments out of the box](https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix). Simply put your path to statics as `assetPrefix` in your `next.config.js` when building for production. Then upload `./.next/static` to your CDN of choice. It is important to note that `assetPrefix` also has to be set in the config when launching production nest.js server.

## HMR and NEXT.js instance caching

At the moment the application works really fast in dev mode. Reboots should not take more than 3 seconds and pages load up instantly. However this might not be always that way - large projects tend to have significantly crippled build procedures. It is especially noticable for large frontends. Every change to nest.js server would reboot it and create a new NEXT.js instance that would start recompiling your client (almost) from scratch.

This greatly degrades developer experience significantly increasing feedback cycles when developing complex solutions. Let's fix this issue by implementing [Hot Module Replacement](https://webpack.js.org/guides/hot-module-replacement/) in nest.js and caching NEXT.js instances between reloads.

### Hot Reload in nest.js

We will first follow [official documentation on Hot Reload in nest.js](https://docs.nestjs.com/recipes/hot-reload). I will not duplicate most of information from there, just the more important parts.

For example, we should use our own `start:dev` script to accept a different `tsconfig.json`.

```json
// ./package.json
"start:dev": "nest build --webpack --webpackPath webpack-hmr.config.js --path tsconfig.server.json --watch"
```

> It is important to pass `tsconfig.server.json` outside of a relative path: `ts-loader` will look up paths relative to the server executable. If supply it just with the file name it will start searching in parent folders until it finds a matching file.

Next we should tackle Hot Reload in `main.ts`

```typescript
// ./src/server/main.ts
import { NestFactory } from  '@nestjs/core';
import { PORT } from  'src/shared/constants/env';
import { AppModule } from  './app.module';

declare const module: any;

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(PORT);

  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => app.close());
  }
}
bootstrap();
```

This implementation will works for us. However make notice of old module disposal: `app.close()` is called. It causes all modules, services and controller to cease.

Let's check if our server even works after adding HMR. Boot the server and make changes to `app.controller.ts`. It is important that the server will not reboot at all if you save the file without making any actual changes. If you did indeed make changes to a file it should take ~1-2 seconds to reboot in small projects and no more than ~4-5 seconds in large ones. It is a huge difference that significantly improve developer experience.

> If any nest.js modules were using dynamic `import/require` they may not work after adding Webpack HMR due to bundling. My solution was to boot `tsc` along with Webpack nest.js executable since dynamically imported files were not frequently changed. You may also need NODE_PATH environment variable to properly resolve dynamic import paths (prepend `NODE_PATH=<path> ...` to `start:dev` script).

### Caching NEXT.js instance

Still we have not solved the bottleneck we had with new NEXT.js instance for each reboot. This may take up to a few seconds in large projects during initialization and page loads. 

We will cache `RenderModule` from `nest-next` between module reloads in `app.module.ts` For this we need [nest.js dynamic module initialization](https://docs.nestjs.com/fundamentals/dynamic-modules).

```typescript
// ./src/server/app.module.ts
import { DynamicModule, Module } from '@nestjs/common';
import Next from 'next';
import { RenderModule } from 'nest-next';
import { NODE_ENV } from 'src/shared/constants/env';
import { AppController } from './app.controller';
import { AppService } from './app.service';

declare const module: any;

@Module({})
export class AppModule {
    public static initialize(): DynamicModule {
        /* during initialization attempt pulling cached RenderModule
            from persisted data */
        const renderModule =
            module.hot?.data?.renderModule ??
            RenderModule.forRootAsync(Next({ dev: NODE_ENV === 'development' }), {
                viewsDir: null,
            });
        
        if (module.hot) {
            /* add a handler to cache RenderModule
                before disposing existing module */
            module.hot.dispose((data: any) => {
                data.renderModule = renderModule;
            });
        }
        
        return {
            module: AppModule,
            imports: [renderModule],
            controllers: [AppController],
            providers: [AppService],
        };
    }
}
```

Update `main.ts` to dynamically initialize `AppModule`.

```typescript
// ./src/server/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

const app = await NestFactory.create(AppModule.initialize());
```

To make sure everything works we may observe terminal outputs during nest.js reboot. If we succeeded we will not see `compiling...` message produced when creating a new instance of NEXT.js server. This way we potentially saved _hours_ of developers time.

## More SSR techniques

Our original problem was that we did not want to have separate services for nest.js and NEXT.js. It seems that at this point we removed all things that were blocking us from having a decent developer experience with our solution. So how about leveraging our solution to have some advantages?

Earlier we declined the strategy of returning SSR data from controller handlers. We also discovered how we can employ nest.js interceptors to pass data to GSSP in a reusable way. Why don't we use interceptors to pass some common initial data to our client? This data may be user data or tokens/translations/configurations/feature flags and so on.

### Creating a configuration

In order to pass some configuration to our client we first need to create some configuration. I will not create a separate module and service for accessing configuration in nest.js (but that is how you would do it in a proper application). A simple file will suffice.

Our configuration will be populated with [feature flags](https://en.wikipedia.org/wiki/Feature_toggle). As an example we will use `blog_link` flag toggling between two link display options.

```typescript
// ./src/server/config.ts
const CONFIG = {
    features: {
        blog_link: true,
    },
};

export { CONFIG };
```

Let's create `ConfigInterceptor` that would put configuration to returned value and include it in `@UseInterceptors` decorator.

> Obviously in a real application you might pull feature flags from a separate service or website.  Luckily it's fairly easy to access request information in interceptors: `req` and `res` HTTP contexts are available in them so you may execute some middlewares prior to executing interceptors.

```typescript
// ./src/server/config.interceptor.ts
import {
    Injectable,
    NestInterceptor,
    ExecutionContext,
    CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { CONFIG } from './config';

@Injectable()
export class ConfigInterceptor implements NestInterceptor {
    intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
        return next.handle().pipe(
            map((data) => ({
                ...data,
                config: CONFIG,
            })),
        );
    }
}
```

```typescript
// ./src/server/app.controller.ts
import { ConfigInterceptor } from  './config.interceptor';

// ...

@Get('/')
@Render('index')
@UseInterceptors(ParamsInterceptor, ConfigInterceptor)
public home() {
    return {};
}

@Get(':id')
@Render('[id]')
@UseInterceptors(ParamsInterceptor, ConfigInterceptor)
public blogPost() {
    return {};
}
```

To make sure our interceptor works we may use `console.log(ctx.query)` in GSSP.

> It is important not to include sensitive information in `ctx.query`: it is also serialized when generating HTML. Therefore it is also not recommended to use `ctx.query` to pass large items that are unused on the client. As a workaround use `ctx.req` to access such information: during SSR it will be populated with your Express (or fastify for that matter) `req` from nest.js.

### AOP for GSSP

Now we have reusable code to pass information to the client: we should also add some reusable code to parse this information and do something with it. Let's create `buildServerSideProps` wrapper for GSSP.

```typescript
// ./src/client/ssr/buildServerSideProps.ts
import { ParsedUrlQuery } from  'querystring';
import { Config } from  'src/shared/types/config';
import {
    GetServerSideProps,
    GetServerSidePropsContext,
} from  'src/shared/types/next';
  
type StaticProps = {
    features: Config['features'];
};
  
type StaticQuery = {
    config: Config;
};
  
const buildServerSideProps = <P, Q extends ParsedUrlQuery = ParsedUrlQuery>(
    getServerSideProps: (ctx: GetServerSidePropsContext<Q>) => Promise<P>,
): GetServerSideProps<Partial<StaticProps> & P, Partial<StaticQuery> & Q> => {
    return async (ctx) => {
        const { features } = ctx.query.config || {};
          
        const props = await getServerSideProps(ctx);
          
        return {
            props: {
                ...props,
                features,
            },
        };
    };
};

export { buildServerSideProps };
```

> If you are paying attention you may notice that `buildServerSideProps` recieves not a real GSSP but a simplified version that return just the `props`. This stops you from using `redirect` and other properties in individual GSSPs. To avoid that use a real GSSP as the param to `buildServerSideProps`.

In order to have helpful typings we should have a type for `Config`.

```typescript
// ./src/shared/types/config.ts
export type Config = {
    features: Record<string, boolean>;
};

// ./src/server/config.ts
import type { Config } from 'src/shared/types/config';

const CONFIG: Config = {
    features: {
        blog_link: true,
    },
};
```

Also NEXT.js `GetServerSideProps` does not quite suit us - we can't really override `Query` type. We will add types of our own.

```typescript
// ./src/shared/types/next.ts
import {
    GetServerSidePropsResult,
    GetServerSidePropsContext as GetServerSidePropsContextBase,
} from  'next';
import { ParsedUrlQuery } from  'querystring';
  
export type GetServerSidePropsContext<Q = ParsedUrlQuery> = Omit<
    GetServerSidePropsContextBase,
    'query'
> & { query: Q };
  
export type GetServerSideProps<P, Q = ParsedUrlQuery> = (
    ctx: GetServerSidePropsContext<Q>,
) => Promise<GetServerSidePropsResult<P>>;
```

Update pages to use this new wrapper.

```typescript
// ./src/pages/index.tsx
export const getServerSideProps = buildServerSideProps<THomeProps>(async () => {
    const blogPosts = await fetch('/api/blog-posts');
     
    return { blogPosts };
});

// ./src/pages/[id].tsx
export const getServerSideProps = buildServerSideProps<TBlogProps, TBlogQuery>(
    async (ctx) => {
        const id = ctx.query.id;
          
        const post = await fetch(`/api/blog-posts/${id}`);
          
        return { post };
    },
);
```

> It is once again important not to pass the entire config to your client. It might contain sensitive information like tokens, internal URLs and credentials or just make your payload larger.

### Accessing application context

We already can access `features` property on our pages. But this might not be a very comfortable way of doing so. We have to utilize prop-drilling to pass it to deeply nested components. To avoid that, use global application context.

```typescript
// ./src/shared/types/app-data.ts
import { Config } from './config';

export type AppData = {
    features: Config['features'];
};

// ./src/client/ssr/appData.ts
import { createContext } from 'react';
import { AppData } from 'src/shared/types/app-data';

const AppDataContext = createContext<AppData>({} as AppData);

export { AppDataContext };
```

To not repeat application context mounting on every page put this logic in `_app.tsx`. I will implement it using a class but it is not a requirement.

```typescript
// ./src/pages/_app.tsx
import NextApp, { AppProps } from 'next/app';
import { AppDataContext } from 'src/client/ssr/appData';
import { AppData } from 'src/shared/types/app-data';

class App extends NextApp<AppProps> {
    appData: AppData;
    
    constructor(props: AppProps) {
        super(props);
        
        this.appData = props.pageProps.appData || {};
    }

    render() {
        const { Component, pageProps } = this.props;
    
        return (
            <AppDataContext.Provider value={this.appData}>
                <Component {...pageProps} />
            </AppDataContext.Provider>
        );
    }
}

export default App;
```

Update `buildServerSideProps` slightly.

```typescript
// ./src/client/ssr/buildServerSideProps.ts
import { AppData } from 'src/shared/types/app-data';

// ...

type StaticProps = {
    appData: Partial<AppData>;
};

// ...

return {
    props: {
        ...props,
        appData: {
            features,
        },
    },
};
```

And create a hook to use `AppDataContext` easily.

```typescript
// ./src/client/ssr/useAppData.ts
import { useContext } from 'react';
import { AppDataContext } from './appData';

const useAppData = () => {
    return useContext(AppDataContext);
};

export { useAppData };
```

Finally, implement the `useFeature` hook and utilize it on the `index` page.

```typescript
// ./src/client/hooks/useFeature.ts
import { useAppData } from 'src/client/ssr/useAppData';

const useFeature = (feature: string, defaultValue = false) => {
    return useAppData().features[feature] ?? defaultValue;
};

export { useFeature };

// ./src/pages/index.tsx
const Home: FC<THomeProps> = ({ blogPosts }) => {
    const linkFeature = useFeature('blog_link');
    
    return (
        <div>
            <h1>Home</h1>
            {blogPosts.map(({ title, id }) => (
                <div key={id}>
                    {linkFeature ? (
                        <>
                            {title}
                            <Link href={`/${id}`}> Link</Link>
                        </>
                    ) : (
                        <Link href={`/${id}`}>{title}</Link>
                    )}
                </div>
            ))}
        </div>
    );
};
```

Validate the changes in the browser. `localhost:3000` should display links according to the feature flag we set in the server configuration. Update the configuration and check if after page refresh displayed link changes.

### Client-side navigation

After makes such significant changes to our SSR pipelines and GSSP we surely have to check if client-side navigations still work.

Our concerns are justified: browsers will reload the page when making client-side transitions. In the terminal we would see an error: we can't quite serialize `appData.features` field. It expectedly is `undefined` when making client-side navigation. Our nest.js controller handlers do not get called when making client-side transitions, remember?

> Only apply interceptors or `req` properties to pass actual initial data. And never expect them to be available in GSSP functions. Such data may be translations, feature flags, CSRF tokens and other configurations.

To solve our current issue let's sanitize `buildServerSideProps` return values.

```typescript
// ./src/client/ssr/filterUnserializable.ts
const filterUnserializable = (
    obj: Record<string, unknown>,
    filteredValues: unknown[] = [undefined],
): Record<string, unknown> => {
    return Object.keys(obj).reduce<Record<string, unknown>>((ret, key) => {
        if (typeof obj[key] === 'object' && obj[key] !== null) {
            return {
                ...ret,
                [key]: filterUnserializable(obj[key] as Record<string, unknown>),
            };
        } else if (!filteredValues.includes(obj[key])) {
            return { ...ret, [key]: obj[key] };
        }
        
        return ret;
    }, {});
};

export { filterUnserializable };

// ./src/client/ssr/buildServerSideProps
import  {  filterUnserializable  }  from  './filterUnserializable';

// ...

return {
    props: {
        ...(await getServerSideProps(ctx)),
        appData: filterUnserializable({ features }) as StaticProps['appData'],
    },
};
```

> This `filterUnserializable` implementation has some flaws. It will show poor perfomance for large and nested objects. For completeness and consistency between article versions I will leave the code as is to later return in a separate article on how to diagnose and profile such issues.

Let's test client-side transitions once again - should work now.

> Having the ability to easily pass arbitary data to your client is one of the primary advantages `nest-next` offers in my opinion. Data may come from any source including middlewares mutating `req` properties. You may leverage existing Express solutions in your `nest-next` project and deliver changes to clients almost immediately.

## Subdirectory deployment

In some cases you might want your production application to be deployed in a subdirectory. For example we are working on documentation and our service should be available at `/docs`. Or with our current application - a blog section on a website with `/blog` prefix.

What should we do to support such functionality? It seems the only thing blocking us is API requests and client links. Statics will be deployed on a CDN so it will not be a problem. Seems like a fairly simple task at first.

But then we remember that NEXT.js makes requests to an internal endpoint when making client-side transitions. Request handlers executes GSSP on the server and return JSON with data. We are rather helpless when trying to change this. And if we do not use a CDN the entire static will break. This will not do for us.

Luckily [in NEXT.js docs we find `basePath` parameter](https://nextjs.org/docs/api-reference/next.config.js/basepath). It allows NEXT.js to support subdirectory deployments out of the box, adding it to every NEXT.js server endpoint. Great, now we have a plan, let's start.

### Development proxy

Before we start coding anything we should provide a way to reproduce our production environment with subdirectory deployment. For this we will use a simple [proxy server](https://en.wikipedia.org/wiki/Proxy_server). Use [Docker](https://www.docker.com/) and [nginx](https://nginx.org/) to implement and start a proxy. Let's add proxy configuration.

```nginx
# ./nginx.conf
server {
    listen 8080;

    location /blog/ {
        proxy_pass http://localnode:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

> It is important to understand that nginx will boot in a Docker container with a net of its own. Therefore to properly proxy the request to the nest.js server we would need an address for the host machine. We will use `localnode` name for it. I have not tested `start:proxy` script on Windows machines. Unless you are using WSL you may have some issues supplying the container with host machine address.

This configuration will allow proxy to handle requests to `localhost:8080/blog/*` and proxy them to `localhost:3000/*`. In `package.json` add `start:proxy` script to boot a Docker container with our proxy.

```bash
docker run --name nest-next-example-proxy \
    -v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
    --add-host localnode:$(ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}') \
    -p 8080:8080 \
    -d nginx
```

### Adding basePath

Add `basePath` to your server configuration. Update typings and pull data from `BASE_PATH` environment variable.

```typescript
// ./src/shared/types/config.ts
export type Config = {
    features: Record<string, boolean>;
    basePath: string;
};

// ./src/server/config.ts
import { Config } from 'src/shared/types/config';

const CONFIG: Config = {
    features: {
        blog_link: true,
    },
    basePath: process.env.BASE_PATH || '',
};

export { CONFIG };
```

Create `next.config.js` to configure NEXT.js. Put `basePath` there as well.

```javascript
// ./next.config.js
module.exports = {
    basePath: process.env.BASE_PATH,
};
```

### Proxy and basePath

Check website on `localhost:8000/blog` after booting both server and proxy. The request reaches nest.js server but NEXT.js static requests are unsuccessful. NEXT.js expects that the request comes with the desired `basePath` in `req.url`. But in our nginx proxy we cut it off. Add a separate rule to proxy `/blog/_next` requests without replacing our `basePath`.

```nginx
server {
    listen 8080;
    
    location /blog/_next/ {
        proxy_pass http://localnode:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    location /blog/ {
        proxy_pass http://localnode:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

Reboot Docker container and check the website in your browser. Unfortunately we are out of luck again.

> There was (and still is to some extent) a problem with `nest-next`. This package applies a [filter](https://docs.nestjs.com/exception-filters) that forwards unhandled 404s to NEXT.js. But only if the request starts with `/_next`. Therefore NEXT.js server expects a request that starts with `basePath` and `nest-next` proxies only requests starting with `/_next`.

I submitted a [PR to fix this issue](https://github.com/kyle-mccarthy/nest-next/pull/79) and it was merged. However package maintainer Kyle has not published a new version with this fix since then. You may ask him to publish or use a compiled version from my GitHub.

```bash
yarn upgrade nest-next@https://github.com/yakovlev-alexey/nest-next/tarball/base-path-dist
```

> Tag `base-path-dist` only includes the required files.

After bumping npm package check `localhost:8080/blog` in your browsers. You should finally see a familiar Home page. Client-side navigations also work!

### Fetch wrapper

The only thing left is to add `basePath` to `fetch` requests. It seems that there is not need for that at the moment. However that is only due to the fact that we call `fetch` only on the server and never from the client. As soon as you start fetching from the client you start seeing errors.

Let's refactor `buildServerSideProps` a little. Update typings for `AppData` and extract `appData` parsing from `ctx.query` to a separate method.

```typescript
// ./src/shared/types/app-data.ts
import { Config } from './config';

export type AppData = Pick<Config, 'basePath' | 'features'>;

// ./src/client/ssr/extractAppData.ts
import { GetServerSidePropsContext } from 'src/shared/types/next';
import { AppData } from 'src/shared/types/app-data';
import { filterUnserializable } from './filterUnserializable';
import { StaticQuery } from './buildServerSideProps';
  
const extractAppData = (
    ctx: GetServerSidePropsContext<Partial<StaticQuery>>,
) => {
    const { features, basePath } = ctx.query.config || {};
      
    return filterUnserializable({ features, basePath }) as Partial<AppData>;
};
  
export { extractAppData };
```

Use new util in `buildServerSideProps`.

```typescript
// ./src/client/ssr/buildServerSideProps.ts
import { extractAppData } from './extractAppData';

// ...

const buildServerSideProps = <P, Q extends ParsedUrlQuery = ParsedUrlQuery>(
    getServerSideProps: (ctx: GetServerSidePropsContext<Q>) => Promise<P>,
): GetServerSideProps<Partial<StaticProps> & P, Partial<StaticQuery> & Q> => {
    return async (ctx) => {
        const props = await getServerSideProps(ctx);
        
        return {
            props: {
                ...props,
                appData: extractAppData(ctx),
            },
        };
    };
};

export { buildServerSideProps };
```

Finally we may access `basePath` on the client. The only thing left is to add this property to the actual `fetch` wrapper. I will not create an ingenious solution and turn `envAwareFetch` into a function with side effects.

```typescript
// ./src/shared/utils/fetch.ts
import { isServer, PORT } from '../constants/env';

type FetchContext = {
    basePath: string;
};

const context: FetchContext = {
    basePath: '',
};

const initializeFetch = (basePath: string) => {
    context.basePath = basePath;
};

const getFetchUrl = (url: string) => {
    if (isServer) {
        // на сервере не нужно добавлять basePath - запрос делается не через proxy
        return url.startsWith('/') ? `http://localhost:${PORT}${url}` : url;
    }
    
    return url.startsWith('/') ? context.basePath + url : url;
};

const envAwareFetch = (url: string, options?: Partial<RequestInit>) => {
    const fetchUrl = getFetchUrl(url);
    
    return fetch(fetchUrl, options).then((res) => res.json());
};

export { envAwareFetch as fetch, initializeFetch };
```

Finally initialize `fetch` using `initializeFetch`. It might seem that we should do that in GSSP however it is once again only run on the server. Therefore use `_app.tsx` as the place to initialize `fetch`.

```typescript
// ./src/pages/_app.tsx
constructor(props: AppProps) {
    super(props);

    this.appData = props.pageProps.appData || {};

    initializeFetch(this.appData.basePath);
}
```

To check that everything works properly you add a `fetch` request in `useEffect` on one of the pages.

> This way we can deploy `nest-next` application in subdirectories without loosing any of the features or advantages we had.

### Proxy without rewrites

There is also an option to use a proxy without rewrites. Then all requests have to be handled with `basePath` on the server as well. Proxy configuration would look like this.

```nginx
server {
    listen 8080;

    location /blog/ {
        proxy_pass http://localnode:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

To prepend `basePath` to every nest.js server endpoint you may employ something like [`nest-router`](https://github.com/nestjsx/nest-router) and use `basePath` as `path` parameter.

> It is also important to initialize `fetch` before making any requests in GSSP. And you would have to pull `basePath` from an environment variable or directly from config when running GSSP on client-side transition.

## Conclusion

![Articles summary as a miro board](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anxdgoq1l4qe8neh3kyj.jpg)

`nest-next` allows you to have very reusable SSR pipelines with all the features of NEXT.js and existing infrastructure you might have with nest.js and Express. Hopefully this series of articles helped you to better understand how you might leverage this technology to help you develop large applications. Skills you might get from completing this tutorial can also be reused for configuring nest.js with other purposes.