---
layout: article
title: Sequelize
description: Use Sequelize with an Appwrite native MySQL database. Configure the mysql dialect, run migrations against the direct connection, and pool runtime traffic from serverless environments.
---

Appwrite's native MySQL database is a standard MySQL engine (default **8.4**), so [Sequelize](https://sequelize.org/) works against it with no Appwrite-specific configuration. Point Sequelize's `mysql` dialect and the `mysql2` driver at the connection string from the [Connections](/docs/products/databases/mysql/connections) page and use models, the query interface, and migrations as you would against any MySQL server.

**Before you start**

You'll need a native MySQL database in a `ready` state and its credentials. See [native MySQL databases](/docs/products/databases/mysql) to create one and [Connections](/docs/products/databases/mysql/connections) to retrieve the connection string. The username is `admin.<hash>`, and the database name is `default`.

**Sequelize 7**

Sequelize 7 (`@sequelize/core` + `@sequelize/mysql`) is still alpha. These examples use Sequelize 6 (`sequelize` + `mysql2`), the current stable line. Appwrite connection URLs and ports are unchanged on v7. Stay on v6 unless you are deliberately adopting the alpha: v7 uses a different dialect API, and its CLI is not ready yet.

# Set the connection string

Fetch the connection details with [`mysql.get()`](/docs/products/databases/mysql/connections#credentials). That call returns one `connectionString` on the engine port (`3306`). Use it as `DIRECT_URL`, copy it to `DATABASE_URL`, and change only the port to `6033` for the [connection pooler](/docs/products/databases/mysql/connection-pooling). Never commit them:

```env
DATABASE_URL="mysql://admin.<hash>:<password>@db-<hash>.<region>.appwrite.center:6033/default"
DIRECT_URL="mysql://admin.<hash>:<password>@db-<hash>.<region>.appwrite.center:3306/default"
```

Use the pooled `DATABASE_URL` (port `6033`) for runtime traffic and `DIRECT_URL` (port `3306`) for Sequelize CLI migrations. Connections on Appwrite Cloud are encrypted with TLS. Configure `dialectOptions.ssl` when Sequelize or `mysql2` does not infer TLS from the URL, and keep certificate verification enabled against Node's standard trust store. See [Network security](/docs/products/databases/mysql/network-security) for TLS and network controls.

# Install and configure Sequelize

Install Sequelize and `mysql2`:

```bash
npm install sequelize mysql2
npm install -D sequelize-cli typescript @types/node
```

Create a Sequelize instance for runtime traffic. Prefer the URL form and set SSL with verification:

```ts
import { Sequelize, DataTypes } from 'sequelize';

export const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: 'mysql',
  dialectOptions: {
    ssl: {
      require: true,
      rejectUnauthorized: true
    }
  },
  logging: false,
  pool: {
    max: 10,
    min: 0,
    idle: 10000
  }
});

export const User = sequelize.define(
  'User',
  {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    email: {
      type: DataTypes.STRING(255),
      allowNull: false,
      unique: true
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      field: 'created_at'
    }
  },
  {
    tableName: 'users',
    updatedAt: false
  }
);
```

`ssl: { require: true, rejectUnauthorized: true }` validates the server certificate against Node's built-in CA store. You can also pass discrete `host`, `port`, `database`, `username`, and `password` options instead of a URL; keep `dialect: 'mysql'` and the same `dialectOptions.ssl` settings.

# Run migrations

Use Sequelize CLI migrations against the **direct** engine port (`3306`). DDL needs a session-level connection, so do not point the CLI at the transaction-mode pooler.

`sequelize-cli init` generates localhost defaults. Replace them with a concrete `.sequelizerc` and config that read `DIRECT_URL`.

Create `.sequelizerc` at the project root:

```js
const path = require('path');

module.exports = {
  config: path.resolve('config', 'config.js'),
  'models-path': path.resolve('models'),
  'seeders-path': path.resolve('seeders'),
  'migrations-path': path.resolve('migrations')
};
```

Create `config/config.js` so every CLI environment uses `DIRECT_URL`:

```js
require('dotenv').config();

const shared = {
  url: process.env.DIRECT_URL,
  dialect: 'mysql',
  dialectOptions: {
    ssl: {
      require: true,
      rejectUnauthorized: true
    }
  },
  logging: false
};

module.exports = {
  development: shared,
  test: shared,
  production: shared
};
```

Then generate and apply migrations:

```bash
npx sequelize-cli migration:generate --name create-users
npx sequelize-cli db:migrate
```

Example migration:

```js
'use strict';

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('users', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      email: {
        type: Sequelize.STRING(255),
        allowNull: false,
        unique: true
      },
      created_at: {
        type: Sequelize.DATE,
        allowNull: false,
        defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
      }
    });
  },

  async down(queryInterface) {
    await queryInterface.dropTable('users');
  }
};
```

Prefer migrations over `sync({ alter: true })` in shared and production environments. The `admin.<hash>` user owns the `default` database and can run schema changes.

# Query with Sequelize

Once the schema is migrated, authenticate and use models:

```ts
import { sequelize, User } from './db';

await sequelize.authenticate();

const user = await User.create({ email: 'ada@example.com' });

const recent = await User.findAll({
  order: [['createdAt', 'DESC']],
  limit: 10
});
```

On long-running servers, create the Sequelize instance once at module scope and reuse it. On serverless, keep a single instance per module scope so warm invocations reuse the pool, and use the Appwrite pooler for runtime traffic. Call `sequelize.close()` on shutdown.

# Pool connections from serverless

Each running instance opens its own connections to the engine. On serverless and edge platforms such as Vercel, Netlify, and Cloudflare, short-lived instances can fan out into more backend connections than the engine allows. Route runtime traffic through the [connection pooler](/docs/products/databases/mysql/connection-pooling). The primary `DATABASE_URL` already uses the pooler port (`6033`) on the same hostname.

Keep Sequelize CLI migrations pointed at `DIRECT_URL`.

Keep the Sequelize `pool.max` small on serverless. Session-level features such as user variables and temporary tables need the direct port or a session-mode pooler. See the [pooler modes](/docs/products/databases/mysql/connection-pooling#modes) page for the trade-offs. Confirm the pooler port on the [Connections](/docs/products/databases/mysql/connections) page if your project shows a different value.

# Use a branch for previews and CI

[Branches](/docs/products/databases/mysql/branches) are isolated copies of a database with their own hostname and connection string. They're ideal for running migrations against throwaway data in a pull-request preview or an integration-test job:

1. Create a branch from the API and read its `connectionString`.
2. Export it as `DIRECT_URL` and `DATABASE_URL` for the preview or CI job.
3. Run `sequelize-cli db:migrate` and your test suite against the branch.
4. Delete the branch when the job finishes.

Because a branch starts from a storage snapshot, the schema and data match the source database at branch time, so migrations run against representative data without touching production.

# Related

- [Connections](/docs/products/databases/mysql/connections): Retrieve credentials and rotate the primary password.
- [Connection pooler](/docs/products/databases/mysql/connection-pooling): Pool modes, ports, and read/write splitting for serverless workloads.
- [Branches](/docs/products/databases/mysql/branches): Ephemeral database copies for preview environments and CI.
- [Network security](/docs/products/databases/mysql/network-security): TLS and network controls for native MySQL databases.
