<!-- Markdown mirror of https://justscale.sh/migrate/from-nestjs -->

Migration guide

# Migrating from NestJS to JustScale

Migrating from NestJS to JustScale is mostly a one-to-one translation: NestJS providers become JustScale services, controllers stay controllers, and modules become explicit app composition. The biggest change is conceptual rather than mechanical — decorators and reflect-metadata are replaced by function-based dependency injection, and background jobs you ran on a queue typically become durable processes.

This guide walks through each piece in the order you will hit it: services, controllers and routes, app wiring, validation, guards, and background work.

- ## 1.Providers become services

A NestJS @Injectable() provider becomes a defineService with an inject map and a factory. Constructor injection is replaced by the injected dependencies passed to the factory.NestJS

```
@Injectable()
export class UserService {
  constructor(private readonly users: UserRepository) {}

  findByEmail(email: string) {
    return this.users.findOne({ where: { email } });
  }
}
```

JustScale

```
export class UserService extends defineService({
  inject: { users: UserRepository },
  factory: ({ users }) => ({
    findByEmail: (email: string) =>
      users.findOne(User.fields.email.eq(email)),
  }),
}) {}
```

- ## 2.Controllers and routes

Controllers stay controllers, but routes are defined with route-builder factories instead of method decorators. Path params can be turned into typed model references with .types().NestJS

```
@Controller('users')
export class UsersController {
  constructor(private readonly svc: UserService) {}

  @Get(':id')
  get(@Param('id') id: string) {
    return this.svc.findById(id);
  }
}
```

JustScale

```
import { createController } from '@justscale/core';
import { Get } from '@justscale/http';

export const UsersController = createController({
  inject: { svc: UserService },
  routes: () => ({
    get: Get('/users/:user').types({ user: User }).handle(({ user }) => user),
  }),
});
```

- ## 3.Modules become app composition

Instead of @Module metadata, you compose the app explicitly. Add services and controllers to a JustScale() app; there is no module graph to maintain.NestJS

```
@Module({
  providers: [UserService],
  controllers: [UsersController],
})
export class AppModule {}
```

JustScale

```
import JustScale from '@justscale/core';

export const app = JustScale()
  .add(UserService)
  .add(UsersController);
```

- ## 4.DTOs and pipes become schema validation

Validation pipes and class-validator DTOs become a schema on the route. The handler receives a typed, validated body.NestJS

```
export class CreateUserDto {
  @IsEmail() email: string;
}

@Post()
create(@Body() dto: CreateUserDto) { /* ... */ }
```

JustScale

```
import { Post } from '@justscale/http';
import { z } from 'zod';

create: Post('/users')
  .body(z.object({ email: z.string().email() }))
  .handle(({ body }) => { /* body is typed + validated */ }),
```

- ## 5.Background jobs become durable processes

Queue-based background work (for example a BullMQ processor) usually becomes a durable process: plain async code that suspends on signals or timeouts and resumes after a restart, with no separate worker or queue.NestJS + BullMQ

```
@Processor('orders')
export class OrderProcessor {
  @Process()
  async handle(job: Job) {
    await chargeCard(job.data.orderId);
  }
}
```

JustScale

```
import { createProcess } from '@justscale/core/process';

export const orderFulfillment = createProcess({
  path: '/order/:order/fulfillment',
  inject: { payments: PaymentService },
  async handler({ payments }, { order }) {
    await payments.charge(order); // survives restarts
  },
});
```

## Frequently asked questions

### How long does migrating from NestJS take?

Most of the work is mechanical: providers to services and controllers to route-builder controllers. The conceptual shift is dropping decorators for function-based DI and moving queue jobs to durable processes.

### Do I have to rewrite my NestJS guards?

No rewrite of the logic is needed; NestJS guards map to JustScale guards and interceptors map to middleware.

### Can I keep BullMQ during migration?

Yes. You can migrate incrementally and convert queue jobs to durable processes one at a time.

## Learn more

[JustScale vs NestJS](https://justscale.sh/compare/justscale-vs-nestjs)[Services](https://justscale.sh/docs/fundamentals/services)[Controllers](https://justscale.sh/docs/fundamentals/controllers)[Durable Processes](https://justscale.sh/docs/processes/overview)[Quick Start →](https://justscale.sh/docs/overview/quick-start)
