What is SQL to Sequelize Model Converter?
Sequelize is one of the oldest and most established promise-based ORMs for Node.js, supporting PostgreSQL, MySQL, MariaDB, SQLite, and Microsoft SQL Server through a single API. A Sequelize model maps a JavaScript/TypeScript class (or a plain object definition) to a database table, and Sequelize handles building SQL, running migrations, and managing associations between tables. Sequelize gives you two ways to declare a model, and understanding the difference matters when reading converted output. The classic style is sequelize.define("User", { ...attributes }), which returns a model and is concise for JavaScript projects. The modern style — recommended for TypeScript — is class User extends Model { } followed by User.init({ ...attributes }, { sequelize, modelName: "User" }). The class-extension form lets you declare instance properties with TypeScript types (using InferAttributes and InferCreationAttributes) so you get type-checked field access, whereas define() returns a loosely typed model. Both produce the same runtime behaviour; they differ in type safety and where you can attach instance methods. Every attribute in a Sequelize model is described with a DataTypes value — DataTypes.STRING, DataTypes.INTEGER, DataTypes.BOOLEAN, DataTypes.DATE, DataTypes.JSONB, and so on — plus options like allowNull, defaultValue, primaryKey, autoIncrement, and unique. Relationships are NOT declared inside the attributes; instead you call association methods after defining the models: User.hasMany(Post) and Post.belongsTo(User). This converter maps your SQL columns to the correct DataTypes and constraint options, and turns REFERENCES into the references config plus the matching belongsTo/hasMany association so you can wire up your models quickly when migrating an existing SQL database to Sequelize.
How to Use
- Select your database dialect (PostgreSQL, MySQL, or SQLite). The dialect affects defaults like how SERIAL vs AUTO_INCREMENT maps to autoIncrement: true and which DataTypes variants are emitted.
- Choose output language: TypeScript (class extends Model + init) or JavaScript (sequelize.define). Pick TypeScript if you want typed attributes via InferAttributes.
- Paste your SQL CREATE TABLE statements. Each table becomes one model definition.
- Click "Generate Sequelize Model" to produce the model code with DataTypes mappings and association hints.
- Save each model and import them into your app, then call the generated belongsTo/hasMany associations after all models are defined (associations must run once every model class exists).
Why Use This Tool?
Tips & Best Practices
- Define associations separately, after every model is loaded. Sequelize associations like User.hasMany(Post) must run when both model classes already exist. Putting them inside a define() call or before the other model is imported causes "X is not associated to Y" errors at query time.
- Sequelize adds createdAt and updatedAt automatically unless you set timestamps: false in the model options. If your SQL table already has created_at/updated_at columns, either map them with field: "created_at" and set timestamps: true, or you will get duplicate/conflicting columns.
- DataTypes.STRING is VARCHAR(255) by default — always pass the length from your SQL (DataTypes.STRING(100)) so a migration does not silently widen or truncate the column. Use DataTypes.TEXT for unbounded text.
- For booleans on MySQL, Sequelize uses TINYINT(1); on PostgreSQL it uses BOOLEAN. The converter handles this, but be aware reading a MySQL TINYINT(1) back gives true/false, while a raw query gives 0/1.
- underscored: true in model options auto-converts camelCase attribute names to snake_case column names. This is the cleanest way to keep JS-style userId in code while storing user_id in the database, instead of adding field: "user_id" on every column.
Frequently Asked Questions
What is the complete SQL type to Sequelize DataTypes mapping?
VARCHAR(n)/CHAR(n) → DataTypes.STRING(n), TEXT/TINYTEXT/MEDIUMTEXT/LONGTEXT → DataTypes.TEXT, INTEGER/INT/SMALLINT → DataTypes.INTEGER, BIGINT → DataTypes.BIGINT, TINYINT(1)/BOOLEAN/BOOL → DataTypes.BOOLEAN, FLOAT/REAL → DataTypes.FLOAT, DOUBLE/DOUBLE PRECISION → DataTypes.DOUBLE, DECIMAL/NUMERIC(p,s) → DataTypes.DECIMAL(p,s), DATE/DATETIME → DataTypes.DATEONLY (for DATE) and DataTypes.DATE (for DATETIME/TIMESTAMP), TIMESTAMP/TIMESTAMPTZ → DataTypes.DATE, TIME → DataTypes.TIME, JSON → DataTypes.JSON, JSONB → DataTypes.JSONB (PostgreSQL only), UUID → DataTypes.UUID, BLOB/BYTEA/VARBINARY → DataTypes.BLOB, ENUM(...) → DataTypes.ENUM(...).
What is the difference between sequelize.define() and class extends Model?
sequelize.define("User", {...}) is the classic factory form: short, untyped, and returns a model you use directly. class User extends Model {} with User.init({...}, { sequelize }) is the modern form that supports declaring typed instance properties (declare id: number) using InferAttributes<User> and InferCreationAttributes<User>, giving full TypeScript type checking on field access and on User.create(). Both create equivalent models at runtime — choose class+init for TypeScript projects and define() for quick JavaScript scripts.
How do I set up associations like belongsTo and hasMany?
A SQL foreign key user_id INTEGER REFERENCES users(id) maps to two association calls: Post.belongsTo(User, { foreignKey: "user_id" }) on the child, and User.hasMany(Post, { foreignKey: "user_id" }) on the parent. belongsTo means the FK column lives on this model (Post); hasMany/hasOne means the FK lives on the other model. Use hasOne instead of hasMany when the relationship is one-to-one. These calls also add helper methods like post.getUser() and user.getPosts().
How does Sequelize compare to TypeORM and Prisma?
Sequelize is the most battle-tested and dialect-flexible of the three, with a mature migration CLI, but its TypeScript types were retrofitted and feel less ergonomic than newer tools. TypeORM is decorator-based and integrates with NestJS. Prisma is schema-first with the strongest type safety and a generated client. Choose Sequelize for legacy support, multi-dialect needs, or projects already invested in it; choose Prisma for greenfield TypeScript projects wanting top type safety.
Why are createdAt and updatedAt appearing that I did not define?
Sequelize enables automatic timestamps by default, adding createdAt and updatedAt columns and managing them on create/update. To disable, set timestamps: false in the model options. To map them to existing snake_case columns, keep timestamps: true and set createdAt: "created_at", updatedAt: "updated_at" (or use underscored: true). To add soft deletes, set paranoid: true, which adds a deletedAt column and filters soft-deleted rows automatically.
Is my SQL data sent to a server?
No. All conversion runs entirely in your browser. Your SQL schema never leaves your device.
Real-world Examples
Single table: a products table as a typed Sequelize model
A products table with an auto-increment id, a unique SKU, a decimal price, and a JSONB attributes column. This shows DataTypes mapping, allowNull/unique options, and the class + init TypeScript form with typed attributes.
CREATE TABLE products ( id SERIAL PRIMARY KEY, sku VARCHAR(50) UNIQUE NOT NULL, name VARCHAR(200) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0, in_stock BOOLEAN NOT NULL DEFAULT true, attributes JSONB );
import { DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize";
import { sequelize } from "./db";
class Product extends Model<InferAttributes<Product>, InferCreationAttributes<Product>> {
declare id: CreationOptional<number>;
declare sku: string;
declare name: string;
declare price: number;
declare inStock: boolean;
declare attributes: object | null;
}
Product.init(
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
sku: { type: DataTypes.STRING(50), allowNull: false, unique: true },
name: { type: DataTypes.STRING(200), allowNull: false },
price: { type: DataTypes.DECIMAL(10, 2), allowNull: false, defaultValue: 0 },
inStock: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true, field: "in_stock" },
attributes: { type: DataTypes.JSONB, allowNull: true },
},
{ sequelize, tableName: "products", timestamps: false }
);
export default Product;Related tables: comments belonging to posts with associations
A comments table referencing posts. This demonstrates the references config inside the attribute plus the belongsTo / hasMany association pair you must add after both models are defined.
CREATE TABLE comments ( id SERIAL PRIMARY KEY, post_id INTEGER NOT NULL REFERENCES posts(id), author VARCHAR(100) NOT NULL, body TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW() );
import { DataTypes, Model } from "sequelize";
import { sequelize } from "./db";
import Post from "./Post";
class Comment extends Model {}
Comment.init(
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
postId: {
type: DataTypes.INTEGER,
allowNull: false,
field: "post_id",
references: { model: "posts", key: "id" },
},
author: { type: DataTypes.STRING(100), allowNull: false },
body: { type: DataTypes.TEXT, allowNull: false },
createdAt: { type: DataTypes.DATE, allowNull: false, defaultValue: DataTypes.NOW, field: "created_at" },
},
{ sequelize, tableName: "comments", timestamps: false }
);
// Wire up associations after all models are loaded:
Comment.belongsTo(Post, { foreignKey: "post_id" });
Post.hasMany(Comment, { foreignKey: "post_id" });
export default Comment;