***

title: "Stand-alone CLI Scripts"
metaTitle: "Stand-alone CLI Scripts with Vendure Worker Bootstrap"
metaDescription: "Create stand-alone Node.js scripts that access Vendure services via bootstrapWorker for cron jobs, data imports, and one-off administrative tasks."
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

It is possible to create stand-alone scripts that can be run from the command-line by using the [bootstrapWorker function](/current/core/reference/typescript-api/worker/bootstrap-worker/). This can be useful for a variety of use-cases such as running cron jobs or importing data.

## Minimal example

Here's a minimal example of a script which will bootstrap the Vendure Worker and then log the number of products in the database:

```ts title="src/get-product-count.ts"
import { bootstrapWorker, Logger, ProductService, RequestContextService } from '@vendure/core';

import { config } from './vendure-config';

if (require.main === module) {
    getProductCount()
        .then(() => process.exit(0))
        .catch(err => {
            Logger.error(err);
            process.exit(1);
        });
}

async function getProductCount() {
    // This will bootstrap an instance of the Vendure Worker, providing
    // us access to all of the services defined in the Vendure core.
    // (but without the unnecessary overhead of the API layer).
    const { app } = await bootstrapWorker(config);

    // Using `app.get()` we can grab an instance of _any_ provider defined in the
    // Vendure core as well as by our plugins.
    const productService = app.get(ProductService);

    // For most service methods, we'll need to pass a RequestContext object.
    // We can use the RequestContextService to create one.
    const ctx = await app.get(RequestContextService).create({
        apiType: 'admin',
    });

    // We use the `findAll()` method to get the total count. Since we aren't
    // interested in the actual product objects, we can set the `take` option to 0.
    const { totalItems } = await productService.findAll(ctx, {take: 0});

    Logger.info(
        [
            '\n-----------------------------',
            `There are ${totalItems} products`,
            '------------------------------',
        ].join('\n'),
    )
}
```

This script can then be run from the command-line:

```bash
npx ts-node src/get-product-count.ts

# or

yarn ts-node src/get-product-count.ts
```

resulting in the following output:

```bash
info 01/08/23, 11:50 - [Vendure Worker] Bootstrapping Vendure Worker (pid: 4428)...
info 01/08/23, 11:50 - [Vendure Worker] Vendure Worker is ready
info 01/08/23, 11:50 - [Vendure Worker]
-----------------------------------------
There are 56 products in the database
-----------------------------------------
```

## The `app` object

The `app` object returned by the `bootstrapWorker()` function is an instance of the [NestJS Application Context](https://docs.nestjs.com/standalone-applications). It has full access to the NestJS dependency injection container, which means that you can use the `app.get()` method to retrieve any of the services defined in the Vendure core or by any plugins.

```ts title="src/import-customer-data.ts"
import { bootstrapWorker, CustomerService } from '@vendure/core';
import { config } from './vendure-config';

// ...

async function importCustomerData() {
    const { app } = await bootstrapWorker(config);
    
    const customerService = app.get(CustomerService); // [!code highlight]
}
```

## Creating a RequestContext

Almost all the methods exposed by Vendure's core services take a `RequestContext` object as the first argument. Usually, this object is created in the [API Layer](/current/core/developer-guide/the-api-layer/#resolvers) by the `@Ctx()` decorator, and contains information related to the current API request.

When running a stand-alone script, we aren't making any API requests, so we need to create a `RequestContext` object manually. This can be done using the [`RequestContextService`](/current/core/reference/typescript-api/request/request-context-service/):

```ts title="src/get-product-count.ts"
// ...
import { RequestContextService } from '@vendure/core';

async function getProductCount() {
    const { app } = await bootstrapWorker(config);
    const productService = app.get(ProductService);
    
    const ctx = await app.get(RequestContextService).create({ // [!code highlight]
        apiType: 'admin', // [!code highlight]
    }); // [!code highlight]
    
    const { totalItems } = await productService.findAll(ctx, {take: 0});
}
```

### Acting as a specific user

The context above is anonymous — it carries no user, and therefore no permissions. That is fine for
services which do not check them, but services such as the [`RoleService`](/current/core/reference/typescript-api/services/role-service/)
will reject it.

To act as a particular user, pass that user in the `user` option. The example below loads the
superadmin, which is usually what you want for an administrative script:

```ts title="src/get-product-count.ts"
// ...
import { ConfigService, RequestContextService, TransactionalConnection, User } from '@vendure/core';

async function getProductCount() {
    const { app } = await bootstrapWorker(config);

    // Load the user the script should act as. Any User will do — the superadmin is used here
    // because an administrative script generally needs unrestricted permissions.
    const { superadminCredentials } = app.get(ConfigService).authOptions; // [!code highlight]
    const superAdminUser = await app // [!code highlight]
        .get(TransactionalConnection) // [!code highlight]
        .rawConnection.getRepository(User) // [!code highlight]
        .findOneOrFail({ // [!code highlight]
            where: { identifier: superadminCredentials.identifier }, // [!code highlight]
            // The roles (and their channels) must be loaded, or the resulting context // [!code highlight]
            // has the user's id but no permissions. // [!code highlight]
            relations: { roles: { channels: true } }, // [!code highlight]
        }); // [!code highlight]

    const ctx = await app.get(RequestContextService).create({
        apiType: 'admin',
        user: superAdminUser, // [!code highlight]
    });

    // `ctx` now carries the superadmin's permissions, so permission-checking
    // services will accept it.
}
```

:::note
Loading the `roles` relation (and its `channels`) is what gives the context its permissions. Omit
it and the context still has the user's id — enough for services that reload the user by id, but
its `channelPermissions` are empty, so any check that reads them directly (for example the entity
access-control checks behind `connection.getRepository(ctx, Entity)`) will deny access.
:::
