Hi all, in my phoenix project I have an Ecto schema and simple controller for providing REST API. Now I want to provide swagger API for third-party users of this API. According to Open API Spex documents I should define schema for each request parameters and responses. The REST API is just for fetching some instance and creating new instance. Is it possible to reuse my Instance schema instead of defining new schema for Open API Spex?
Here is the Instance schema and the related controller:
# instance.ex
defmodule ZYR.Sys.Instance do
use Ecto.Schema
import Ecto.Changeset
@derive {Jason.Encoder, except: [:__meta__]}
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "instance" do
field(:code, :string)
field(:family, :string)
field(:name, :string)
timestamps()
end
@doc false
def changeset(instance, attrs) do
instance
|> cast(attrs, [:name, :code, :family])
|> validate_required([:name, :code, :family])
end
end
def index(conn, _params) do
render(conn, "index.json", %{instances: Sys.list_instances()})
end
Exactly this code won’t compile.
You can start from this snippet
defmodule MyOpenApiSchemaBuilder do
defmacro defschema(module) do
quote bind_quoted: [module: module] do
require OpenApiSpex
OpenApiSpex.schema(%{
title: inspect(module),
type: :object,
properties:
module.__schema__(:fields)
|> Map.new(fn field_name ->
type = Ecto.Type.type(module.__schema__(:type, field_name))
{field_name, %OpenApiSpex.Schema{type: type}}
end)
})
end
end
end
defmodule MyUserSchema do
import MyOpenApiSchemaBuilder
defschema(MyApp.User)
end
I’m certain that it’s possible, but I’d argue it’s not a good idea.
An API used by third-parties is a commitment; changing it will require coordination. Tightly coupling that to the underlying schema through generated code means that now the database schema can’t easily change either.
I’ve done something like this and I think it’s preferable to maintaining a separate, very similar list of attributes/types. It seems to me that coupling is desired in this case. If I’ve removed a column I’ve documented in my API I don’t want the documentation to stay the same.
I do think at least there needs to be a way to specify which db fields should be added to the schema, since not all are relevant. Maybe something like that is what you were thinking of when you said “tightly” coupled?