scotty
Haskell web framework inspired by Ruby's Sinatra, using WAI and Warp
https://github.com/scotty-web/scotty
| LTS Haskell 24.16: | 0.22@rev:2 |
| Stackage Nightly 2025-10-24: | 0.22@rev:2 |
| Latest on Hackage: | 0.22@rev:2 |
scotty-0.22@sha256:b3c799b3c4896176342062c1140c290ffb9a8d81e6da2ea3e12f7a83cbda78d4,6013Module documentation for 0.22
- Web
- Web.Scotty
- Web.Scotty.Cookie
- Web.Scotty.Internal
- Web.Scotty.Trans
- Web.Scotty
Scotty

A Haskell web framework inspired by Ruby’s Sinatra, using WAI and Warp.
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
main = scotty 3000 $
get "/:word" $ do
beam <- pathParam "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
Scotty is the cheap and cheerful way to write RESTful, declarative web applications.
- A page is as simple as defining the verb, URL pattern, and Text content.
- It is template-language agnostic. Anything that returns a Text value will do.
- Conforms to the web application interface (WAI).
- Uses the very fast Warp webserver by default.
As for the name: Sinatra + Warp = Scotty.
Examples
Run /basic.hs to see Scotty in action:
runghc examples/basic.hs
Setting phasers to stun... (port 3000) (ctrl-c to quit)
Or equivalently with stack:
stack exec -- scotty-basic
Once the server is running you can interact with it with curl or a browser:
curl localhost:3000
foobar
curl localhost:3000/foo_query?p=42
<h1>42</h1>
Additionally, the examples directory shows a number of concrete use cases, e.g.
More Information
Tutorials and related projects can be found in the Scotty wiki.
Contributing
Feel free to ask questions or report bugs on the Github issue tracker.
Github issues are now (September 2023) labeled, so newcomers to the Haskell language can start with easy fix ones and gradually progress to new features, bugs and R&D :)
Package versions
Scotty adheres to the Package Versioning Policy.
FAQ
- Fails to compile regex-posix on Windows
- If you are using stack, add the following parameters to
stack.yaml:-
extra-deps: - regex-posix-clib-2.7 flags: regex-posix: _regex-posix-clib: true
-
- If you are using cabal, update the
constraintssection ofcabal.project.localas follows:-
constraints: regex-posix +_regex-posix-clib
-
- If you are using stack, add the following parameters to
Contributors
Copyright
(c) 2012-Present, Andrew Farmer and Scotty contributors
Changes
next [????.??.??]
0.22 [2024.03.09]
New
- add
instance Parsable UTCTime(#250) - add
filesOpts(#369). Form parameters and files are only parsed from the request body if needed;filesOptsintroduces options to customize upload limits, a mechanism to back uploads with temporary files based on resourcet, as well as a continuation-based syntax to process such temporary files.
Fixes
filesdoes not accept unbounded uploads anymore (see #183, #203), but likefilesOptsit is backed by temporary files which are read back in memory and removed right away. The limits forfilesare prescribed bydefaultParseBodyOptionsin wai-extra (#369).- Path parameters with value matching the parameter name prefixed by colon will properly populate
pathParamswith their literal value :/:paramwill match/:paramand add aParamwith value("param", ":param")(#301) - Accept text-2.1 (#364)
- Remove dependency upper bounds on
textandbytestring(#371) - When in ‘verbose’ mode any unhandled exceptions are printed to stderr as well (#374)
Breaking changes
- some ActionT API functions have now a MonadIO or MonadUnliftIO constraint rather than Monad reflecting that there is where request reading takes place. E.g.
fileshas now a MonadUnliftIO constraint on its base monad. (#369) - the File type has now a type parameter to reflect whether it carries file contents or just a filepath pointing to the temp file (#369).
0.21 [2023.12.17]
New
- add
getResponseHeaders,getResponseStatus,getResponseContent(#214) - add
captureParamMaybe,formParamMaybe,queryParamMaybe(#322) - add
Web.Scotty.Trans.StrictandWeb.Scotty.Trans.Lazy(#334) - renamed
captureParam,captureParamMaybe, andcaptureParamstopathParam,pathParamMaybe,pathParamsrespectively, keeping the old names as their synonyms (#344)
Deprecated
- deprecate
rescueandliftAndCatchIO(#332) - Deprecate
StatusError,raiseandraiseStatus(#351)
Fixes
- Reverted the
MonadReaderinstance ofActionTso that it inherits the base monad (#342) - Scotty’s API such as
queryParamnow throwsScottyExceptionrather thanStatusException. Uncaught exceptions are handled byscottyExceptionHandler, resembling the existing behaviour
Breaking changes
Filetype: the first component of the tuple is strict text now (used to be lazy prior to 0.21) (#370)
Documentation
- Add doctest, refactor some inline examples into doctests (#353)
- document “
defaultHandleronly applies to endpoints defined after it” (#237)
0.20.1 [2023.10.03]
- remove dependencies on ‘base-compat’ and ‘base-compat-batteries’ (#318)
- re-add MonadFail (ActionT m) instance (#325)
- re-add MonadError (ActionT m) instance, but the error type is now specialized to ‘StatusError’ (#325)
- raise lower bound on base ( > 4.14 ) to reflect support for GHC >= 8.10 (#325).
0.20 [2023.10.02]
- Drop support for GHC < 8.10 and modernise the CI pipeline (#300).
- Adds a new
nestedhandler that allows you to place an entire WAI Application under a Scotty route (#233). - Disambiguate request parameters (#204). Adjust the
Envtype to have three[Param]fields instead of one, addcaptureParam,formParam,queryParamand the associatedcaptureParams,formParams,queryParams. Add deprecation notices toparamandparams. - Add
Scotty.Cookiemodule (#293). - Change body parsing behaviour such that calls to
nextdon’t result in POST request bodies disappearing (#147). - (Internal) Remove unused type
RequestBodyState(#313) - Rewrite
ActionTusing the “ReaderT pattern” (#310) https://www.fpcomplete.com/blog/readert-design-pattern/
Breaking:
- (#310) Introduce
unliftioas a dependency, and base exception handling oncatch. - (#310) Clarify the exception handling mechanism of ActionT, ScottyT.
rescuechanges signature to use properExceptiontypes rather than strings. RemoveScottyErrortypeclass. - (#310) All ActionT methods (
text,htmletc.) have now a MonadIO constraint on the base monad rather than Monad because the response is constructed in a TVar inside ActionEnv.rescuehas a MonadUnliftIO constraint. The Alternative instance of ActionT also is based on MonadUnliftIO because<|>is implemented in terms ofcatch.ScottyTandActionTdo not have an exception type parameter anymore. - (#310) MonadError e (ActionT m) instance removed
- (#310) MonadFail (ActionT m) instance is missing by mistake.
0.12.1 [2022.11.17]
- Fix CPP bug that prevented tests from building on Windows.
- Allow building with
transformers-0.6.*andmtl-2.3.*. Because theMonadTrans tclass gained aforall m. Monad m => Monad (t m)superclass intransformers-0.6.0.0, theMonadTransandMonadTransControlinstances forActionT enow have aScottyError einstance context to match theMonadinstance.
0.12 [2020.05.16]
- Provide
MonadReaderandMonadStateinstances forActionT. - Add HTTP Status code as a field to
ActionError, and add a sister function toraise,raiseStatus. This makes throwing a specific error code and exiting much cleaner, and avoids the strange defaulting to HTTP 500. This will make internal functions easier to implement with the right status codes ‘thrown’, such asjsonData. - Correct http statuses returned by
jsonData(#228). - Better error message when no data is provided to
jsonData(#226). - Add
SemigroupandMonoidinstances forActionTandScottyT - ScottyT: Use strict StateT instead of lazy
- Handle adjacent slashes in the request path as one (thanks @SkyWriter)
0.11.5 [2019.09.07]
- Allow building the test suite with
hspec-wai-0.10.
0.11.4 [2019.05.02]
- Allow building with
base-4.13(GHC 8.8).
0.11.3 [2019.01.08]
- Drop the test suite’s dependency on
hpc-coveralls, which is unmaintained and does not build with GHC 8.4 or later.
0.11.2 [2018.07.02]
- Migrate from
NetworktoNetwork.Socketto avoid deprecation warnings.
0.11.1 [2018.04.07]
- Add
MonadThrowandMonadCatchinstances forActionT[abhinav] - Fix
matchAnyso that all methods are matched, not just standard ones [taphu]
0.11.0
- IO exceptions are no longer automatically turned into ScottyErrors by
liftIO. UseliftAndCatchIOto get that behavior. - New
finishfunction. - Text values are now leniently decoded from ByteStrings.
- Added
MonadFailinstance forScottyT - Lots of bound bumps on dependencies.
0.10.2
- Removed debug statement from routes
0.10.1
Parsableinstances forWord,Word8,Word16,Word32,Word64[adamflott]Parsableinstances forInt8,Int16,Int32,Int64, andNatural- Removed redundant
Monadconstraint onmiddleware
0.10.0
-
The monad parameters to
ScottyThave been decoupled, causing the type of theScottyTconstructor to change. As a result,ScottyTis no longer aMonadTransinstance, and the type signatures ofscottyT,scottyAppT, andscottyOptsThave been simplified. [ehamberg] -
socketDescriptionno longer uses the deprecatedPortNumconstructor. Instead, it uses theShowinstance forPortNumber. This changes the bytes from host to network order, so the output ofsocketDescriptioncould change. [ehamberg] -
Alternative,MonadPlusinstances forActionT -
scottynow depends ontransformers-compat. As a result,ActionTnow usesExceptT, regardless of which version oftransformersis used. As a result, several functions inWeb.Scotty.Transno longer require aScottyErrorconstraint, sinceExceptTdoes not require anErrorconstraint (unlikeErrorT). -
Added support for OPTIONS routes via the
optionsfunction [alvare] -
Add
scottySocketandscottySocketT, exposing Warp Unix socket support [hakujin] -
Parsableinstance for lazyByteString[tattsun] -
Added streaming uploads via the
bodyReaderfunction, which retrieves chunks of the request body. [edofic]ActionEnvhad agetBodyChunkfield added (inWeb.Scotty.Internal.Types)RequestBodyStateandBodyPartiallyStreamedadded toWeb.Scotty.Internal.Types
-
jsonDatausesaeson’seitherDecodeinstead of justdecode[k-bx]
0.9.1
- text/html/json only set Content-Type header when not already set
0.9.0
-
Add
charset=utf-8toContent-Typefortext,htmlandjson -
Assume HTTP status 500 for
defaultHandler -
Remove deprecated
sourcemethod. -
No longer depend on conduit.
0.8.2
-
Bump
aesonupper bound -
Fix
mtlrelated deprecation warnings
0.8.1
-
Export internal types
-
Added
MonadBase,MonadTransControlandMonadBaseControlinstances forActionT
0.8.0
-
Upgrade to wai/wai-extra/warp 3.0
-
No longer depend on conduit-extra.
-
The
sourceresponse method has been deprecated in favor of a newstreamresponse, matching changes in WAI 3.0. -
Removed the deprecated
reqHeaderfunction.
0.7.3
- Bump upper bound for case-insensitive, mtl and transformers.
0.7.2
- Bump lower bound on conduit, add conduit-extra to cabal build depends.
0.7.1
- Default warp settings now use
setFdCacheDuration 0to work around a warp issue where file changes are not getting picked up.
0.7.0
-
Renamed
reqHeadertoheader. Addedheadersfunction to get all headers. -
Changed
MonadIOinstance forActionTsuch that IO exceptions are lifted intoScottyErrors viastringError. -
Make
Boolparsing case-insensitive. Goal: support both Haskell’s True/False and Javascript’s true/false. Thanks to Ben Gamari for suggesting this. -
Bump
aeson/textupper bounds. -
Bump
wai/wai-extra/warpbounds, including new lower bound forwarp, which fixes a security issue related to Slowloris protection.
0.6.2
- Bump upper bound for
text.
0.6.1
- Match changes in
wai-extra.
0.6.0
-
The Scotty transformers (
ScottyTandActionT) are now parameterized over a custom exception type, allowing one to extend Scotty’sErrorTlayer with something richer thanTexterrors. See theexceptionsexample for use.ScottyMandActionMremain specialized toTextexceptions for simplicity. -
Both monads are now instances of
FunctorandApplicative. -
There is a new
cookiesexample. -
Internals brought up-to-date with WAI 2.0 and related packages.
0.5.0
-
The Scotty monads (
ScottyMandActionM) are now monad transformers, allowing Scotty applications to be embedded in arbitraryMonadIOs. The old API continues to be exported fromWeb.Scottywhere:type ScottyM = ScottyT IO type ActionM = ActionT IOThe new transformers are found in
Web.Scotty.Trans. See theglobalstateexample for use. Special thanks to Dan Frumin (co-dan) for much of the legwork here. -
Added support for HTTP PATCH method.
-
Removed lambda action syntax. This will return when we have a better story for typesafe routes.
-
reqHeader :: Text -> ActionM Text==>reqHeader :: Text -> ActionM (Maybe Text) -
New
rawmethod to set body to a rawByteString -
Parse error thrown by
jsonDatanow includes the body it couldn’t parse. -
headersplit intosetHeaderandaddHeader. The former replaces a response header (original behavior). The latter adds a header (useful for multipleSet-Cookies, for instance).