How to serialize SqlAlchemy result to JSON?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
SQLAlchemy model instances are not directly JSON-serializable because they are Python objects with internal state, relationships, and metadata. The common approaches are: a custom to_dict() method on models, __dict__ filtering, the marshmallow-sqlalchemy library for schema-based serialization, or Pydantic with sqlmodel for type-safe serialization. For simple cases, converting column values to a dictionary and passing it to json.dumps() works. For complex cases with relationships and nested objects, use a serialization library.
Method 1: Custom to_dict() Method
__table__.columns iterates over all column definitions. default=str in json.dumps() handles non-serializable types like datetime by converting them to strings.
Method 2: Reusable Mixin
A mixin class provides reusable serialization across all models.
Method 3: Using marshmallow-sqlalchemy
marshmallow-sqlalchemy auto-generates schema fields from the model definition and handles nested relationships, validation, and custom field transformations.
Method 4: Pydantic with SQLModel
SQLModel combines SQLAlchemy models with Pydantic schemas, giving you automatic serialization without additional schema classes.
Method 5: Flask-SQLAlchemy with jsonify
Serializing Query Results (Row Objects)
Common Pitfalls
- DateTime not JSON-serializable:
json.dumps()raisesTypeErrorondatetimeobjects. Usedefault=str,.isoformat(), or a custom encoder to convert dates to strings. - Lazy-loaded relationships cause N+1 queries: Accessing
user.ordersin a loop triggers a separate query per user. Usejoinedload()orselectinload()when serializing relationships to avoid performance issues. - Exposing sensitive fields: A generic
to_dict()that serializes all columns may expose passwords, tokens, or internal IDs. Always explicitly exclude sensitive fields or use a whitelist approach. - Detached instance errors: Accessing model attributes after the session is closed raises
DetachedInstanceError. Either serialize inside the session context or useexpire_on_commit=Falseon the session. - Circular references in relationships: Bidirectional relationships (User has Orders, Order has User) cause infinite recursion in recursive
to_dict(). Set a depth limit or exclude back-references.
Summary
- Custom
to_dict()method on models — simplest approach for basic serialization SerializerMixin— reusable mixin with relationship support and type handlingmarshmallow-sqlalchemy— full-featured schema-based serialization with validationSQLModel(Pydantic + SQLAlchemy) — automatic serialization without extra schema classes- Use
default=strinjson.dumps()to handledatetimeand other non-serializable types - Always exclude sensitive fields and handle lazy-loaded relationships explicitly
Related reading
- How to set a boolean field in Dynamo DB?
- How to set a default entity property value with Hibernate
- How to set a DynamoDB Map property value, when the map doesn''t exist yet
- how to SET and DELETE using a single UpdateExpression with Dynamo
- How to serve static files in Flask
- How to serve static files in Flask
- how to set cassandra read and write consistency
- How to set encrypt false in Camel Debezium SQL server connector for JDBC connection
System design interview problems on this topic
- Design a Distributed OLTP Database: solution and walkthrough
- Design a Wide Column Database: solution and walkthrough
- Design a Key Value Store: solution and walkthrough
- Design a Web Cache: solution and walkthrough

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseSystem Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.