---
title: Marten 0.3.0 release notes
pagination_prev: null
pagination_next: null
---

_June 19, 2023._

## Requirements and compatibility

Crystal 1.6, 1.7, and 1.8.

## New features

### Support for streaming responses

It is now possible to generate streaming responses from iterators of strings easily by leveraging the [`Marten::HTTP::Response::Streaming`](pathname:///api/0.3/Marten/HTTP/Response/Streaming.html) class or the [`#respond`](pathname:///api/0.3/Marten/Handlers/Base.html#respond(streamed_content%3AIterator(String)%2Ccontent_type%3DHTTP%3A%3AResponse%3A%3ADEFAULT_CONTENT_TYPE%2Cstatus%3D200)-instance-method) helper method. This can be beneficial if you intend to generate lengthy responses or responses that consume excessive memory (a classic example of this is the generation of large CSV files).

Please refer to [Streaming responses](../../handlers-and-http/introduction.md#streaming-responses) to learn more about this new capability.

### Caching

Marten now lets you interact with a global cache store that allows interacting with an underlying cache system and performing basic operations such as fetching cached entries, writing new entries, etc. By using caching, you can save the result of expensive operations so that you don't have to perform them for every request.

The global cache can be accessed by leveraging the [`Marten#cache`](pathname:///api/0.3/Marten.html#cache%3ACache%3A%3AStore%3A%3ABase-class-method) method. Here are a few examples on how to perform some basic caching operations:

```crystal
# Fetching an entry from the cache.
Marten.cache.fetch("mykey", expires_in: 4.hours) do
  "myvalue"
end

# Reading from the cache.
Marten.cache.read("unknown") # => nil
Marten.cache.read("mykey") # => "myvalue"
Marten.cache.exists?("mykey") => true

# Writing to the cache.
Marten.cache.write("foo", "bar", expires_in: 10.minutes) => true
```

Marten's caching leverages a [cache store mechanism](../../caching/introduction.md#configuration-and-cache-stores). By default, Marten uses an in-memory cache (instance of [`Marten::Cache::Store::Memory`](pathname:///api/0.3/Marten/Cache/Store/Memory.html)) and other [third-party stores](../../caching/reference/stores.md#other-stores) can be installed depending on your caching requirements (eg. Memcached, Redis).

Marten's new caching capabilities are not only limited to its standard cache functionality. They can also be effectively utilized via the newly introduced [template fragment caching](../../caching/introduction.md#template-fragment-caching) feature, made possible by the [`cache`](../../templates/reference/tags.md#cache) template tag. With this feature, specific parts of your [templates](../../templates.mdx) can now be cached with ease.

Please refer to the [Caching](../../caching.mdx) to learn more about these new capabilities.

### JSON field for models and schemas

Marten now provides the ability to define `json` fields in [models](../../models-and-databases/reference/fields.md#json) and [schemas](../../schemas/reference/fields.md#json). These fields allow you to easily persist and interact with valid JSON structures that are exposed as [`JSON::Any`](https://crystal-lang.org/api/JSON/Any.html) objects by default.

For example:

```crystal
class MyModel < Marten::Model
  # Other fields...
  field :metadata, :json
end

MyModel.last!.metadata # => JSON::Any object
```

Additionally, it is also possible to specify that JSON values must be deserialized using a class that makes use of [`JSON::Serializable`](https://crystal-lang.org/api/JSON/Serializable.html). This can be done by leveraging the `serializable` option in both [model fields](../../models-and-databases/reference/fields.md#json) and [schema fields](../../schemas/reference/fields.md#serializable).

For example:

```crystal
class MySerializable
  include JSON::Serializable

  property a : Int32 | Nil
  property b : String | Nil
end

class MyModel < Marten::Model
  # Other fields...
  field :metadata, :json, serializable: MySerializable
end

MyModel.last!.metadata # => MySerializable object
```

### Duration field for models and schemas

It is now possible to define `duration` fields in [models](../../models-and-databases/reference/fields.md#duration) and [schemas](../../schemas/reference/fields.md#duration). These allow you to easily persist valid durations (that map to [`Time::Span`](https://crystal-lang.org/api/Time/Span.html) objects in Crystal) in your models but also to expect valid durations in data validated through the use of schemas.

For example:

```crystal
class Recipe < Marten::Model
  field :id, :big_int, primary_key: true, auto: true
  # Other fields...
  field :fridge_time, :duration, blank: true, null: true
end
```

### Minor features

#### Models and databases

* New [`#get_or_create`](../../models-and-databases/reference/query-set.md#get_or_create) / [`#get_or_create!`](../../models-and-databases/reference/query-set.md#get_or_create-1) methods were added to query sets in order to allow easily retrieving a model record matching a given set of filters or creating a new one if no record is found.
* [`string`](../../models-and-databases/reference/fields.md#string) fields now support a `min_size` option allowing to validate the minimum size of persisted string field values.
* A new [`#includes?`](../../models-and-databases/reference/query-set.md#includes) method was added to query sets in order easily perform membership checks without loading the entire list of records targeted by a given query set.
* Alternative [`#exists?`](../../models-and-databases/reference/query-set.md#exists) methods were added to query sets in order to allow specifying additional filters to use as part of existence checks.
* An [`#any?`](pathname:///api/0.3/Marten/DB/Query/Set.html#any%3F-instance-method) method was added to query sets in order to short-circuit the default implementation of [`Enumerable#any?`](https://crystal-lang.org/api/Enumerable.html#any%3F%3ABool-instance-method) and to avoid loading the full list of records in memory (when called without arguments). This overridden method is technically an alias of [`#exists?`](../../models-and-databases/reference/query-set.md#exists).
* Marten migrations are now optimized to prevent possible issues with circular dependencies within added or deleted tables
* It is now possible to define arbitrary database options by using the new [`db.options`](../../development/reference/settings.md#options) database setting.
* It is now possible to define [`many_to_one`](../../models-and-databases/reference/fields.md#many_to_one) and [`one_to_one`](../../models-and-databases/reference/fields.md#one_to_one) fields that target models with non-integer primary key fields (such as UUID fields for example).

#### Handlers and HTTP

* The [`Marten::Handlers::RecordList`](../../handlers-and-http/reference/generic-handlers.md#listing-records) generic record now provides the ability to specify a custom [query set](../../models-and-databases/queries.md) instead of a model class. This can be achieved through the use of the [`#queryset`](pathname:///api/0.3/Marten/Handlers/RecordListing.html#queryset(queryset)-macro) macro.
* A new [`Marten::Middleware::AssetServing`](../../handlers-and-http/reference/middlewares.md#asset-serving-middleware) middleware was introduced to make it easy to serve collected assets in situations where it is not possible to easily configure a web server (such as [Nginx](https://nginx.org)) or a third-party service (like Amazon's S3 or GCS) to serve assets directly.
* A new [`Marten::Middleware::SSLRedirect`](../../handlers-and-http/reference/middlewares.md#ssl-redirect-middleware) middleware was introduced to allow redirecting non-HTTPS requests to HTTPS easily.
* A new [`Marten::Middleware::ContentSecurityPolicy`](../../handlers-and-http/reference/middlewares.md#content-security-policy-middleware) middleware was introduced to ensure the presence of the Content-Security-Policy header in the response's headers. Please refer to [Content Security Policy](../../security/content-security-policy.md) to learn more about the Content-Security-Policy header and how to configure it.
* The [`Marten::Middleware::I18n`](../../handlers-and-http/reference/middlewares.md#i18n-middleware) middleware can now automatically determine the current locale based on the value of a cookie whose name can be configured with the [`i18n.locale_cookie_name`](../../development/reference/settings.md#locale_cookie_name) setting.
* The [`Marten::Middleware::I18n`](../../handlers-and-http/reference/middlewares.md#i18n-middleware) middleware now automatically sets the Content-Language header based on the activated locale.

#### Templates

* A [`join`](../../templates/reference/filters.md#join) template filter was introduced to allow converting enumerable template values into a string separated by a separator value.
* A [`split`](../../templates/reference/filters.md#split) template filter was introduced to allow converting a string into an array of elements.

#### Schemas

* Type-safe getter methods (ie. `#<field>`, `#<field>!`, and `#<field>?`) are now automatically generated for schema fields. Please refer to [Accessing validated data](../../schemas/introduction.md#accessing-validated-data) in the schemas documentation to read more about these methods and how/why to use them.

#### Development

* [`Marten::HTTP::Errors::SuspiciousOperation`](pathname:///api/0.3/Marten/HTTP/Errors/SuspiciousOperation.html) exceptions are now showcased using the debug internal error page handler to make it easier to diagnose errors such as unexpected host errors (which result from a missing host value in the [`allowed_hosts`](../../development/reference/settings.md#allowed_hosts) setting).
* [`Marten#setup`](pathname:///api/0.3/Marten.html#setup-class-method) now raises.[`Marten::Conf::Errors::InvalidConfiguration`](pathname:///api/0.3/Marten/Conf/Errors/InvalidConfiguration.html) exceptions when a configured database involves a backend that is not installed (eg. a MySQL database configured without `crystal-lang/crystal-mysql` installed and required).
* The [`new`](../../development/reference/management-commands.md#new) management command now automatically creates a [`.editorconfig`](https://editorconfig.org) file for new projects.
* A new [`root_path`](../../development/reference/settings.md#root_path) setting was introduced to make it possible to configure the actual location of the project sources in your system. This is especially useful when deploying projects that have been compiled in a different location from their final destination, which can happen on platforms like Heroku. By setting the root path, you can ensure that your application can find all necessary project sources, as well as other files like locales, assets, and templates.
* A new `--plan` option was added to the [`migrate`](../../development/reference/management-commands.md#migrate) management command in order to provide a comprehensive overview of the operations that will be performed by the applied or unapplied migrations.
* An interactive mode was added to the [`new`](../../development/reference/management-commands.md#new) management command: if the `type` and `name` arguments are not provided, the command now prompts the user for inputting the structure type, the app or project name, and whether the auth app should be generated.
* It is now possible to specify command aliases when defining management commands by leveraging the [`#command_aliases`](pathname:///api/0.3/Marten/CLI/Manage/Command/Base.html#command_aliases(*aliases%3AString|Symbol)-class-method) helper method.

#### Security

* The ability to fully configure and customize the Content-Security-Policy header was added to the framework. Please refer to [Content Security Policy](../../security/content-security-policy.md) to learn more about the Content-Security-Policy header and how to configure it in Marten projects.

#### Deployment

* A new guide was added in order to document [how to deploy on Heroku](../../deployment/how-to/deploy-to-heroku).
* A new guide was added in order to document [how to deploy on Fly.io](../../deployment/how-to/deploy-to-fly-io).

## Backward incompatible changes

### Handlers and HTTP

* [Custom route parameter](../../handlers-and-http/how-to/create-custom-route-parameters.md) must now implement a [`#regex`](pathname:///api/0.3/Marten/Routing/Parameter/Base.html#regex%3ARegex-instance-method) method and can no longer rely on a `#regex` macro to generate such method.
* The generic handlers that used to require the use of a `#model` class method now leverage a dedicated macro instead. This is to make handlers that inherit from generic handler classes more type-safe when it comes to manipulating model records.
* The generic handlers that used to require the use of a `#schema` class method now leverage a dedicated macro instead. This is to make handlers that inherit from generic handler classes more type-safe when it comes to manipulating schema instances.
