diff --git a/Changelog.md b/Changelog.md
new file mode 100644
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,3 @@
+# 0.1.0.0
+
+Initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Ilya Kostyuchenko
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/deriving-openapi3.cabal b/deriving-openapi3.cabal
new file mode 100644
--- /dev/null
+++ b/deriving-openapi3.cabal
@@ -0,0 +1,42 @@
+cabal-version:      1.12
+name:               deriving-openapi3
+version:            0.1.0.0
+license:            MIT
+license-file:       LICENSE
+maintainer:         Ilya Kostyuchenko <ilyakooo0@gmail.com>
+homepage:           https://github.com/ilyakooo0/deriving-openapi3#readme
+bug-reports:        https://github.com/ilyakooo0/deriving-openapi3/issues
+synopsis:           DerivingVia for OpenAPI 3
+description:        DerivingVia for OpenAPI 3
+category:           JSON, Generics, OpenAPI
+build-type:         Simple
+extra-source-files: Changelog.md
+
+source-repository head
+    type:     git
+    location: https://github.com/ilyakooo0/deriving-openapi3
+
+library
+    exposed-modules:    Deriving.OpenApi
+    hs-source-dirs:     src
+    other-modules:      Paths_deriving_openapi3
+    default-language:   Haskell2010
+    default-extensions:
+        AllowAmbiguousTypes ScopedTypeVariables KindSignatures DataKinds
+        TypeOperators FlexibleInstances TypeApplications
+        UndecidableInstances TypeFamilies PolyKinds OverloadedStrings
+        DerivingStrategies DerivingVia FlexibleContexts
+
+    ghc-options:
+        -Weverything -Wno-star-is-type -Wno-unsafe
+        -Wno-prepositive-qualified-module -Wno-safe
+        -Wno-missing-safe-haskell-mode -Wno-missing-import-lists
+        -Wno-implicit-prelude
+
+    build-depends:
+        aeson <1.6,
+        base <4.15,
+        deriving-aeson <0.3,
+        lens <4.20,
+        openapi3 <3.2,
+        text <1.3
diff --git a/src/Deriving/OpenApi.hs b/src/Deriving/OpenApi.hs
new file mode 100644
--- /dev/null
+++ b/src/Deriving/OpenApi.hs
@@ -0,0 +1,139 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+-- | __Example:__
+--
+-- >>> :set -XDerivingStrategies -XDerivingVia -XDeriveGeneric
+--
+-- >>> :{
+-- data User = User
+--   { userFirstName :: String,
+--     userAge :: Maybe Integer
+--   }
+--   deriving stock (Generic)
+--   deriving (FromJSON, ToJSON, ToSchema)
+--     via CustomJSON
+--          '[FieldLabelModifier '[StripPrefix "user", CamelToSnake], RejectUnknownFields]
+--          User
+-- showYaml = Data.ByteString.Char8.putStr . Data.Yaml.encode
+-- :}
+--
+-- >>> showYaml $ toSchema (Proxy :: Proxy User)
+-- type: object
+-- properties:
+--   first_name:
+--     type: string
+--   age:
+--     type: integer
+-- required:
+-- - first_name
+-- additionalProperties: false
+module Deriving.OpenApi
+  ( CustomOpenApi,
+    DatatypeNameModifier,
+    ToSchema,
+    module Deriving.Aeson,
+  )
+where
+
+import Control.Lens
+import qualified Data.Aeson.Types as A
+import Data.OpenApi
+import Data.OpenApi.Internal.ParamSchema
+import Data.OpenApi.Internal.Schema
+import Data.Proxy
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Typeable
+import Deriving.Aeson
+import GHC.Generics
+import GHC.TypeLits
+
+data Description (desc :: Symbol) = Description
+
+instance (AesonOptions xs) => AesonOptions (Description f ': xs) where
+  aesonOptions = aesonOptions @xs
+
+instance KnownSymbol t => OpenApiOptionModifier (Description t) where
+  openApiSchemaModifier = schema . description <>~ Just (toTextLine @t)
+
+toTextLine :: forall s. KnownSymbol s => Text
+toTextLine = "\n\n" <> T.pack (symbolVal (Proxy @s))
+
+-- $setup
+-- >>> import qualified Data.Yaml
+-- >>> import qualified Data.ByteString.Char8
+
+type CustomOpenApi = CustomJSON
+
+instance
+  (OpenApiOptionModifier xs, GToSchema (Rep x), Generic x, Typeable x, Typeable xs, Typeable k) =>
+  ToSchema (CustomJSON (xs :: k) x)
+  where
+  declareNamedSchema Proxy =
+    openApiSchemaModifier @xs
+      <$> genericDeclareNamedSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
+
+instance
+  (OpenApiOptionModifier xs, GToParamSchema (Rep x), Generic x) =>
+  ToParamSchema (CustomJSON (xs :: k) x)
+  where
+  toParamSchema Proxy =
+    genericToParamSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
+
+class OpenApiOptionModifier x where
+  openApiOptionsModifier :: SchemaOptions -> SchemaOptions
+  openApiOptionsModifier = id
+  openApiSchemaModifier :: NamedSchema -> NamedSchema
+  openApiSchemaModifier = id
+
+data DatatypeNameModifier t
+
+instance (StringModifier f) => OpenApiOptionModifier (DatatypeNameModifier f) where
+  openApiOptionsModifier o = o {datatypeNameModifier = getStringModifier @f}
+
+instance (AesonOptions xs) => AesonOptions (DatatypeNameModifier f ': xs) where
+  aesonOptions = aesonOptions @xs
+
+-- deriving-aeson-based instances
+
+instance OpenApiOptionModifier UnwrapUnaryRecords where
+  openApiOptionsModifier o = o {unwrapUnaryRecords = True}
+
+instance OpenApiOptionModifier OmitNothingFields
+
+#if MIN_VERSION_deriving_aeson(0,2,4)
+instance OpenApiOptionModifier RejectUnknownFields where
+  openApiSchemaModifier = schema . additionalProperties .~ Just (AdditionalPropertiesAllowed False)
+#endif
+
+instance StringModifier f => OpenApiOptionModifier (FieldLabelModifier f) where
+  openApiOptionsModifier o = o {fieldLabelModifier = getStringModifier @f}
+
+instance StringModifier f => OpenApiOptionModifier (ConstructorTagModifier f) where
+  openApiOptionsModifier o = o {constructorTagModifier = getStringModifier @f}
+
+instance
+  TypeError ('Text "deriving-openapi3 does not currently the `TagSingleConstructors` modifier.") =>
+  OpenApiOptionModifier TagSingleConstructors
+
+instance OpenApiOptionModifier NoAllNullaryToStringTag where
+  openApiOptionsModifier o = o {allNullaryToStringTag = False}
+
+instance (KnownSymbol t, KnownSymbol c) => OpenApiOptionModifier (SumTaggedObject t c) where
+  openApiOptionsModifier o = o {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
+
+instance OpenApiOptionModifier SumUntaggedValue where
+  openApiOptionsModifier o = o {sumEncoding = A.UntaggedValue}
+
+instance OpenApiOptionModifier SumObjectWithSingleField where
+  openApiOptionsModifier o = o {sumEncoding = A.ObjectWithSingleField}
+
+instance OpenApiOptionModifier SumTwoElemArray where
+  openApiOptionsModifier o = o {sumEncoding = A.TwoElemArray}
+
+instance OpenApiOptionModifier '[]
+
+instance (OpenApiOptionModifier x, OpenApiOptionModifier xs) => OpenApiOptionModifier (x ': xs) where
+  openApiOptionsModifier = openApiOptionsModifier @xs . openApiOptionsModifier @x
+  openApiSchemaModifier = openApiSchemaModifier @xs . openApiSchemaModifier @x
