npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sequelize-to-openapi

v0.1.0

Published

Convert Sequelize models to OpenAPI 3.1 schemas automatically

Readme

sequelize-to-openapi

Convert Sequelize models to OpenAPI 3.1 schemas automatically

npm version License: MIT TypeScript Node.js >=16

Define your models once in Sequelize. Derive your API documentation automatically.

sequelize-to-openapi is the Sequelize counterpart of the popular mongoose-to-swagger package — but targeting OpenAPI 3.1 (not Swagger 2.0) and built for modern Sequelize v6+.


Table of Contents


Why?

When you use Sequelize, you already describe your data model thoroughly: field types, nullability, defaults, validations, and comments. Your API schema is already there — why write it twice?

sequelize-to-openapi reads your Model.rawAttributes and produces a fully-formed OpenAPI 3.1 Schema Object, including:

  • All Sequelize DataType → OpenAPI type/format mappings
  • Required field detection (based on allowNull: false + no defaultValue)
  • Validation constraints (validate: {}) mapped to OpenAPI keywords
  • Column commentdescription
  • defaultValuedefault
  • Auto-increment PKs marked as readOnly: true

Installation

npm install sequelize-to-openapi
# or
yarn add sequelize-to-openapi
# or
pnpm add sequelize-to-openapi

sequelize is a peer dependency — make sure you have it installed.


Quick Start

import s2o from 'sequelize-to-openapi';
import { Sequelize, DataTypes, Model } from 'sequelize';

const sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:' });

class User extends Model {}
User.init(
  {
    username: {
      type: DataTypes.STRING(64),
      allowNull: false,
      comment: 'The login username',
      validate: { isAlphanumeric: true, notEmpty: true, len: [3, 64] },
    },
    email: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: { isEmail: true },
    },
    role: {
      type: DataTypes.ENUM('admin', 'user', 'moderator'),
      allowNull: false,
      defaultValue: 'user',
    },
    lastLogin: DataTypes.DATE,
  },
  { sequelize, modelName: 'User' },
);

const schema = s2o(User);

Output:

{
  "title": "User",
  "type": "object",
  "properties": {
    "id": { "type": "integer", "format": "int32", "readOnly": true },
    "username": {
      "type": "string",
      "maxLength": 64,
      "description": "The login username",
      "pattern": "^[a-zA-Z0-9]+$",
      "minLength": 3
    },
    "email": { "type": "string", "format": "email" },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "moderator"],
      "default": "user"
    },
    "lastLogin": { "type": "string", "format": "date-time" },
    "createdAt": { "type": "string", "format": "date-time" },
    "updatedAt": { "type": "string", "format": "date-time" }
  },
  "required": ["username", "email", "role"]
}

Comparison to mongoose-to-swagger

If you're coming from Mongoose + mongoose-to-swagger, the migration is intentional:

| Feature | mongoose-to-swagger | sequelize-to-openapi | |---|---|---| | Schema source | Mongoose Model | Sequelize Model | | Output format | Swagger 2.0 | OpenAPI 3.1 | | API | m2s(Model) | s2o(Model) | | Options | omitFields, props | omitFields, props, omitSequelizeInternals, includeRequired, title, includeAssociations | | Type mapping | Mongoose types | All Sequelize v6 DataTypes | | Validations | Basic | Full validate:{} extraction | | Zero runtime deps | ✅ | ✅ | | TypeScript | ✅ | ✅ |

// mongoose-to-swagger
const m2s = require('mongoose-to-swagger');
const schema = m2s(MyMongooseModel);

// sequelize-to-openapi — same ergonomics
const s2o = require('sequelize-to-openapi');
const schema = s2o(MySequelizeModel);

API Reference

s2o(Model, options?)

import s2o from 'sequelize-to-openapi';

const schema = s2o(MyModel, {
  omitFields: ['password', 'secretToken'],
  omitSequelizeInternals: true,
  title: 'PublicUserDTO',
});

Parameters:

  • Model — A Sequelize Model class (must be initialized with .init())
  • options — Optional Options object (see below)

Returns: An OpenAPI 3.1 Schema Object


Options

interface Options {
  /**
   * Fields to exclude from output
   * @example ['password', 'internalNote']
   */
  omitFields?: string[];

  /**
   * Extra model-level attribute props to include in schema properties.
   * Use this to whitelist non-standard metadata props you've added to
   * your attribute definitions (e.g. 'description', 'example').
   * @example ['description', 'example']
   */
  props?: string[];

  /**
   * Whether to omit Sequelize-managed fields: id, createdAt, updatedAt, deletedAt
   * @default false
   */
  omitSequelizeInternals?: boolean;

  /**
   * Whether to include the 'required' array
   * @default true
   */
  includeRequired?: boolean;

  /**
   * Title override (default: model class name)
   */
  title?: string;

  /**
   * Whether to include association references as $ref / array schemas
   * @default false
   */
  includeAssociations?: boolean;
}

DataType Mapping

All Sequelize v6 DataTypes are supported:

| Sequelize DataType | OpenAPI 3.1 | |---|---| | STRING | { type: 'string' } | | STRING(n) | { type: 'string', maxLength: n } | | CHAR(n) | { type: 'string', minLength: n, maxLength: n } | | TEXT / TEXT('tiny') | { type: 'string' } | | CITEXT | { type: 'string' } | | INTEGER | { type: 'integer', format: 'int32' } | | SMALLINT / TINYINT / MEDIUMINT | { type: 'integer', format: 'int32' } | | BIGINT | { type: 'integer', format: 'int64' } | | FLOAT / REAL | { type: 'number', format: 'float' } | | DOUBLE | { type: 'number', format: 'double' } | | DECIMAL / DECIMAL(p, s) | { type: 'number' } | | BOOLEAN | { type: 'boolean' } | | DATE | { type: 'string', format: 'date-time' } | | DATEONLY | { type: 'string', format: 'date' } | | TIME | { type: 'string', format: 'time' } | | UUID / UUIDV1 / UUIDV4 | { type: 'string', format: 'uuid' } | | ENUM('a', 'b') | { type: 'string', enum: ['a', 'b'] } | | ARRAY(T) (PostgreSQL) | { type: 'array', items: <mapped T> } | | RANGE(T) (PostgreSQL) | { type: 'array', items: <mapped T>, minItems: 2, maxItems: 2 } | | JSON / JSONB | { type: 'object' } | | BLOB | { type: 'string', format: 'binary' } | | HSTORE (PostgreSQL) | { type: 'object' } | | GEOMETRY / GEOGRAPHY | { type: 'object' } | | INET / CIDR / MACADDR | { type: 'string' } | | VIRTUAL | (skipped) |


Validation Extraction

Sequelize's validate: {} block is automatically extracted and mapped to OpenAPI constraint keywords:

| Sequelize validate rule | OpenAPI constraint | |---|---| | len: [min, max] | minLength, maxLength | | min: n | minimum: n | | max: n | maximum: n | | isEmail: true | format: 'email' | | isUrl: true / isURL: true | format: 'uri' | | isUUID: true | format: 'uuid' | | isAlpha: true | pattern: '^[a-zA-Z]+$' | | isAlphanumeric: true | pattern: '^[a-zA-Z0-9]+$' | | isNumeric: true | pattern: '^[0-9]+$' | | isLowercase: true | pattern: '^[a-z]+$' | | isUppercase: true | pattern: '^[A-Z]+$' | | notEmpty: true | minLength: 1 | | isIn: [[values]] | enum: values | | isIPv4: true | format: 'ipv4' | | isIPv6: true | format: 'ipv6' | | is: /regex/ | pattern: 'regex source' |

Example:

class BlogPost extends Model {}
BlogPost.init(
  {
    title: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        len: [10, 120],
        notEmpty: true,
      },
    },
    slug: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        is: /^[a-z0-9-]+$/,
        len: [3, 80],
      },
    },
    status: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        isIn: [['draft', 'published', 'archived']],
      },
    },
  },
  { sequelize, modelName: 'BlogPost' },
);

const schema = s2o(BlogPost);
// properties.title  → { type: 'string', minLength: 10, maxLength: 120 }
// properties.slug   → { type: 'string', pattern: '^[a-z0-9-]+$', minLength: 3, maxLength: 80 }
// properties.status → { type: 'string', enum: ['draft', 'published', 'archived'] }

Express + swagger-ui-express Integration

import express from 'express';
import swaggerUi from 'swagger-ui-express';
import s2o from 'sequelize-to-openapi';
import { User, Post, Comment } from './models';

const app = express();

// Build the OpenAPI document from your Sequelize models
const openApiDocument = {
  openapi: '3.1.0',
  info: {
    title: 'My API',
    version: '1.0.0',
    description: 'Auto-generated from Sequelize models',
  },
  components: {
    schemas: {
      User: s2o(User, { omitFields: ['password', 'resetToken'] }),
      Post: s2o(Post),
      Comment: s2o(Comment, { omitSequelizeInternals: true }),
    },
  },
  paths: {
    '/users/{id}': {
      get: {
        operationId: 'getUser',
        summary: 'Get a user by ID',
        parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'integer' } }],
        responses: {
          200: {
            description: 'The user',
            content: {
              'application/json': {
                schema: { $ref: '#/components/schemas/User' },
              },
            },
          },
        },
      },
    },
  },
};

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiDocument));

app.listen(3000, () => {
  console.log('API docs available at http://localhost:3000/api-docs');
});

TypeScript Usage

The library is written in TypeScript and ships full type declarations:

import s2o, { Options, ModelSchemaObject, OpenAPISchemaObject } from 'sequelize-to-openapi';

const options: Options = {
  omitFields: ['password'],
  omitSequelizeInternals: true,
};

const schema: ModelSchemaObject = s2o(User, options);

// Low-level access
import { mapDataType, applyValidations } from 'sequelize-to-openapi';

const typeSchema: OpenAPISchemaObject | null = mapDataType(DataTypes.STRING(100));
// => { type: 'string', maxLength: 100 }

const withConstraints: OpenAPISchemaObject = applyValidations(
  { type: 'string' },
  { isEmail: true, len: [5, 254] }
);
// => { type: 'string', format: 'email', minLength: 5, maxLength: 254 }

Adding Custom Metadata via props

If you annotate your attribute definitions with non-standard props (a common pattern for docs-first teams), use the props whitelist to include them in the output:

class Product extends Model {}
Product.init(
  {
    price: {
      type: DataTypes.DECIMAL(10, 2),
      allowNull: false,
      // Non-standard OpenAPI metadata — add anything you want
      description: 'Price in USD',
      example: 9.99,
    },
  },
  { sequelize, modelName: 'Product' },
);

const schema = s2o(Product, { props: ['description', 'example'] });
// properties.price → { type: 'number', description: 'Price in USD', example: 9.99 }

Philosophy

Zero runtime dependencies. This library does pure data transformation — reading from Model.rawAttributes and producing a plain JavaScript object. No extra packages needed.

Colocation. Your data model and your API schema are the same thing. Define it once in Sequelize and let the documentation derive from it automatically.

Familiar API. If you've used mongoose-to-swagger, you can switch with minimal changes. The function signature and option names are intentionally compatible.

Correctness over completeness. Where a precise mapping isn't possible (e.g. Sequelize VIRTUAL fields), we skip rather than guess.


Contributing

Contributions, issues and feature requests are welcome!

See CONTRIBUTING.md for details on how to get started.


License

MIT © sequelize-to-openapi contributors


Inspired by mongoose-to-swagger by Ben Lugavere / giddyinc. Thank you for the original idea.