> ## Documentation Index
> Fetch the complete documentation index at: https://forest-chore-open-api.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating datasources

> Move your datasource configuration from a legacy agent to the current generation.

In legacy agents, the datasource was tied to a single ORM (Sequelize, Mongoose, ActiveRecord, Mongoid). The new agent introduces an explicit datasource layer that supports multiple databases and APIs in the same agent, and decouples the connection from the framework.

## API cheatsheet

| Legacy agent                                       | New agent                                                                                                     |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `forest-express-sequelize`                         | `@forestadmin/agent` + `@forestadmin/datasource-sql`                                                          |
| `forest-express-sequelize` (with Sequelize models) | `@forestadmin/agent` + `@forestadmin/datasource-sequelize`                                                    |
| `forest-express-mongoose`                          | `@forestadmin/agent` + `@forestadmin/datasource-mongoose`                                                     |
| `forest-rails` (ActiveRecord)                      | `forest_admin_agent` + `forest_admin_rails` + `forest_admin_datasource_active_record` (+ toolkit, customizer) |
| `forest-rails` (Mongoid)                           | `forest_admin_agent` + `forest_admin_rails` + `forest_admin_datasource_mongoid` (+ toolkit, customizer)       |

<Note>
  Pick `datasource-sql` or `datasource-mongo` if you want Forest to introspect the database directly. Pick `datasource-sequelize`, `datasource-mongoose`, or `datasource-active-record` if you want to reuse your existing ORM models (recommended when your application already defines them).
</Note>

## Before (Node.js, forest-express-sequelize)

```javascript theme={null}
const Liana = require('forest-express-sequelize');
const models = require('./models');

app.use(Liana.init({
  modelsDir: __dirname + '/models',
  envSecret: process.env.FOREST_ENV_SECRET,
  authSecret: process.env.FOREST_AUTH_SECRET,
  sequelize: models.sequelize,
}));
```

## After (Node.js, reusing Sequelize models)

```javascript theme={null}
import { createAgent } from '@forestadmin/agent';
import { createSequelizeDataSource } from '@forestadmin/datasource-sequelize';
import { sequelize } from './models';

const agent = createAgent({
  authSecret: process.env.FOREST_AUTH_SECRET,
  envSecret: process.env.FOREST_ENV_SECRET,
  isProduction: process.env.NODE_ENV === 'production',
});

agent.addDataSource(createSequelizeDataSource(sequelize));

agent.mountOnExpress(app).start();
```

## After (Node.js, direct SQL connection)

If you don't want to reuse your ORM models, connect directly to the database. Forest introspects the schema automatically.

```javascript theme={null}
import { createAgent } from '@forestadmin/agent';
import { createSqlDataSource } from '@forestadmin/datasource-sql';

const agent = createAgent({
  authSecret: process.env.FOREST_AUTH_SECRET,
  envSecret: process.env.FOREST_ENV_SECRET,
  isProduction: process.env.NODE_ENV === 'production',
});

agent.addDataSource(
  createSqlDataSource({
    uri: process.env.DATABASE_URL,
    sslMode: 'preferred',
  })
);

agent.mountOnExpress(app).start();
```

## Before (Ruby, forest-rails with ActiveRecord)

```ruby theme={null}
# Gemfile
gem 'forest_liana'

# config/initializers/forest_liana.rb
ForestLiana.env_secret = ENV['FOREST_ENV_SECRET']
ForestLiana.auth_secret = ENV['FOREST_AUTH_SECRET']
```

## After (Ruby, ActiveRecord)

```ruby theme={null}
# Gemfile
gem 'forest_admin_agent'
gem 'forest_admin_rails'
gem 'forest_admin_datasource_toolkit'
gem 'forest_admin_datasource_customizer'
gem 'forest_admin_datasource_active_record'
```

Run `rails generate forest_admin_rails:install` to scaffold the two configuration files.

```ruby theme={null}
# config/initializers/forest_admin_rails.rb
ForestAdminRails.configure do |config|
  config.auth_secret = ENV.fetch('FOREST_AUTH_SECRET')
  config.env_secret = ENV.fetch('FOREST_ENV_SECRET')
end
```

```ruby theme={null}
# app/lib/forest_admin_rails/create_agent.rb
module ForestAdminRails
  class CreateAgent
    def self.setup!
      database_configuration = Rails.configuration.database_configuration
      datasource = ForestAdminDatasourceActiveRecord::Datasource.new(database_configuration[Rails.env])

      @create_agent = ForestAdminAgent::Builder::AgentFactory.instance.add_datasource(datasource)
      customize
      @create_agent.build
    end

    def self.customize
      # Collection customizations (Smart Actions, computed fields, segments) go here.
    end
  end
end
```

## Multi-datasource

The biggest payoff of migrating: the new agent can connect to multiple data sources at once.

```javascript theme={null}
const agent = createAgent({ /* ... */ });

// Primary database (Sequelize models reused)
agent.addDataSource(createSequelizeDataSource(sequelize));

// Analytics database (direct SQL)
agent.addDataSource(
  createSqlDataSource(process.env.ANALYTICS_DATABASE_URL),
  { name: 'analytics' }
);

// Stripe data
agent.addDataSource(
  createStripeDataSource({ secretKey: process.env.STRIPE_SECRET_KEY })
);
```

Cross-datasource relationships are first-class. See [Relationships](/product/process/relationships/overview).

## Configuration changes

| Concept                      | Legacy                 | New                                                                    |
| ---------------------------- | ---------------------- | ---------------------------------------------------------------------- |
| `envSecret`                  | `FOREST_ENV_SECRET`    | `FOREST_ENV_SECRET` (unchanged)                                        |
| `authSecret`                 | `FOREST_AUTH_SECRET`   | `FOREST_AUTH_SECRET` (unchanged)                                       |
| Models directory             | `modelsDir` option     | Provided implicitly via the datasource (Sequelize / ActiveRecord)      |
| Including / excluding tables | Manual model filtering | `{ include: [...], exclude: [...] }` on `addDataSource`                |
| Schema generation            | Forest CLI / runtime   | `.forestadmin-schema.json` written on agent start in development       |
| Custom routes                | Express routes         | Hooks (see [Hooks](/product/process/advanced-concepts/hooks/overview)) |

## Including or excluding collections

In v1, you'd manually filter the models passed in. In v2:

```javascript theme={null}
agent.addDataSource(
  createSequelizeDataSource(sequelize),
  { exclude: ['internal_logs', 'session_data'] }
);

// Or only include specific collections
agent.addDataSource(
  createSequelizeDataSource(sequelize),
  { include: ['users', 'orders', 'products'] }
);
```

## Validate the migration

After swapping the datasource:

<Steps>
  <Step title="Start the new agent">
    Run on a different port from the legacy agent (e.g. `3001`).
  </Step>

  <Step title="Check schema generation">
    On first start in development, the agent writes a `.forestadmin-schema.json` file. Confirm every collection you expect is listed.
  </Step>

  <Step title="Smoke-test the API">
    Hit `http://localhost:3001/forest`, which should return Forest metadata.
  </Step>

  <Step title="Point a test environment at the new agent">
    Update the agent URL in your Forest test environment. Browse collections. Every collection should appear with the same fields.
  </Step>

  <Step title="Continue with customizations">
    Once data flows correctly, move on to migrating Smart Actions, Smart Fields, and the rest.
  </Step>
</Steps>

## Common issues

<AccordionGroup>
  <Accordion title="A column shows up with a different name">
    Some legacy agents auto-converted snake\_case to camelCase. The new agent preserves database column names by default. Use the `rename` option or the field-rename customization API to align names.
  </Accordion>

  <Accordion title="A relationship I had isn't appearing automatically">
    Foreign keys defined in the database are detected automatically. Relationships defined only in your ORM models (`belongsTo`, `hasMany`) are detected when you use the ORM-backed datasources (`datasource-sequelize`, `datasource-mongoose`, `datasource-active-record`). Relationships that exist only in code paths the agent doesn't see won't be detected. Declare them explicitly with `addManyToOneRelation`, `addOneToManyRelation`, etc.
  </Accordion>

  <Accordion title="The agent fails to connect to the database">
    Verify that `DATABASE_URL` is set on the agent server, that the user has read permissions, and that SSL is configured if your database requires it. For SQL datasources, check `sslMode`.
  </Accordion>
</AccordionGroup>

## Next step

<Card title="Migrate Smart Actions" icon="arrow-right" href="/guides/migration/from-v1/steps/smart-actions">
  Convert each Smart Action to the new `addAction` API.
</Card>
