Coder Social home page Coder Social logo

softwaremill / tapir Goto Github PK

View Code? Open in Web Editor NEW
1.3K 46.0 403.0 17.53 MB

Rapid development of self-documenting APIs

Home Page: https://tapir.softwaremill.com

License: Apache License 2.0

Scala 99.70% Makefile 0.02% Python 0.18% Batchfile 0.03% Shell 0.01% HTML 0.01% JavaScript 0.01% TypeScript 0.02% Nix 0.03%
akka-http openapi http4s sttp http scala declarative type-safe functional-programming akka

tapir's People

Contributors

adamw avatar aesteve avatar danielleontiev avatar dvgica avatar felix-lipski avatar frekw avatar ghostbuster91 avatar ghostdogpr avatar gzhk avatar hughsimpson avatar ikhoon avatar kciesielski avatar kubinio123 avatar lbialy avatar marcin-jozefowicz avatar matwojcik avatar mbore avatar mergify[bot] avatar micossow avatar mkrzemien avatar mszczygiel avatar pask423 avatar pewniak747 avatar pierscin avatar rafalambrozewicz avatar rucek avatar sameerparekh avatar scala-steward avatar slabiakt avatar softwaremill-ci avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tapir's Issues

Add authentication/authorization support

OpenAPI already has the authentication spec so that it'd be nice to be able to bake it into the tapir endpoints.

Currently TSec seems like the most modular and feature-rich project in this regard, so that I'm interested in combining a tapir endpoint with an auth spec and tsec in the server side.

TSec already has tsec-http4s module. To support both akka-http and http4s, we can:

  1. Factor out some common components outside of tsec-http4s and then add something like tsec-akka-http on TSec side.
  2. Make such common module in tapir depending on tsec-core from scratch.
  3. Just use tsec-http4s as it is, and make another subproject for akka-http.

Support for form field bodies

New endpoint io: form[T](name) which:

  • mandates the body to be urlencoded form parameters
  • reads the given form data parameter

Streaming support

Maybe using the same mechanism as in sttp - an additional type parameter specifying requirements for the interpreter?

Possible syntax: streamBody[S] (as in sttp). This would allow capturing the requirement for a specific stream support.

Support openapi/jsonschema value constraints

When interpreted as openapi docs, this would translate to the following properties from the schema object (https://swagger.io/specification/#schemaObject):

  • enum
  • minimum, maximum
  • pattern
  • maxLength/minLength

The tapir.Schema trait should be extended with a description of value constraints (default no constraints).

The encouraged (and documented) way to define constraints would be by creating new types. This could be through e.g. tagged types (which have no runtime overhead, e.g. https://github.com/softwaremill/scala-common#tagging), value types or "normal" custom types.

We should provide a convenient way to define constraints for a type - through builder methods on schema. Possible syntax:

implicit val schemaForEmail: SchemaFor[String @@ Email] = 
  SchemaFor[String].constraint(Pattern(emailRegex))

implicit val schemaForColors: SchemaFor[String @@ Color] = 
  SchemaFor[String].constraint(Enum("red", "pink", "violet"))

The constraints defined on the schema should also be checked during decoding - resulting in a new DecodeResult.InvalidValue (or maybe reuse/replace Mismatch)?

GraphQL support

Hello! Intriguing project! :)
Are there any plans or thoughts to add GraphQL-server support?
Thank you!

Status code mappings as part of the endpoint description

As suggested on gitter, it would be nice if there was a way to describe status code mappings. Currently, this is only available when interpreting as a server by providing implicit StatusMapper values for error and normal outputs; the openapi documentation always generates two responses: default (for errors) and 200 (for normal outputs). This can only be customised by hand after generating the openapi model.

It would be better if there was a way to capture status code mappings as part of the endpoint description.

Possible API, in Tapir.scala:

  def statusFrom[I](io: EndpointIO[I], default: StatusCode, when: (When[I], StatusCode)*): EndpointIO[I] = ???

  def whenClass[U: ClassTag: SchemaFor]: When[Any] = WhenClass(implicitly[ClassTag[U]], implicitly[SchemaFor[U]])
  def whenValue[U](p: U => Boolean): When[U] = WhenValue(p)

  trait When[-I] {
    def matches(i: I): Boolean
  }
  case class WhenClass[T](ct: ClassTag[T], s: SchemaFor[T]) extends When[Any] {
    override def matches(i: Any): Boolean = ct.runtimeClass.isInstance(i)
  }
  case class WhenValue[T](p: T => Boolean) extends When[T] {
    override def matches(i: T): Boolean = p(i)
  }

usage:

  import tapir._
  import tapir.json.circe._
  import io.circe.generic.auto._

  trait ErrorInfo {
    def z: Boolean
  }
  case class E1(x: String, z: Boolean) extends ErrorInfo
  case class E2(y: Int, z: Boolean) extends ErrorInfo

  implicit val c: CodecForOptional[ErrorInfo, MediaType.Json, _] = null

  val ee = endpoint.out(
    statusFrom(
      jsonBody[ErrorInfo],
      StatusCodes.InternalServerError,
      whenClass[E1] -> StatusCodes.NotFound,
      whenClass[E2] -> StatusCodes.BadRequest,
      whenValue[ErrorInfo](_.z) -> StatusCodes.AlreadyReported
    ))

The server would then be able to generate a proper response code basing on the output value. The openapi docs could contain different output body schemas for different status codes. The client interpreter here wouldn't do anything with this kind of metadata, as the only thing it could do is validate that the status code matches the value received.

Outstanding problems:

  1. for automatic codec derivation this relies on #10: node the null codec in the example above to make the code compile. If there are multiple status codes, this usually means multiple subclasses of a class representing errors.
  2. should statusFrom wrap any kind of input, or only bodies? I think it needs to be constrained to wrapping bodies only, so that we could require the schema of the subclasses, and use these schemas in documentation. This would mean we wouldn't support differentiating status codes on headers (which is the other output option)

Define schemas for maps

The default schema for Map should be an object without arbitrary fields. Alternatively: only for Map[String, V] for any V.

Such a schema should also be properly generated in the docs.

All paths should start with /

As discussed in #23 , path matching should start with /:

endpoint.path(/) // matches only root (no path and /)
endpoint.path(/ "x" / "y" / "z") // matches only /x/y/z and /x/y/z/
endpoint.path(/ "x").path(/ "y" / "z")

Hence, / should be an "empty path" object, and a method to combine two paths

No type parameters found

I am running the example and occur errors like this:

Error:(80, 13) no type parameters for method toRoute: (logic: FN[scala.concurrent.Future[Either[String,Unit]]])(implicit paramsAsArgs: tapir.typelevel.ParamsAsArgs.Aux[(tapir.example.Book, tapir.example.Endpoints.AuthToken),FN], implicit serverOptions: tapir.server.akkahttp.AkkaHttpServerOptions, implicit statusMapper: tapir.server.StatusMapper[Unit], implicit errorStatusMapper: tapir.server.StatusMapper[String])akka.http.scaladsl.server.Route exist so that it can be applied to arguments ((tapir.example.Book, tapir.example.Endpoints.AuthToken) => scala.concurrent.Future[Either[String,Unit]]) --- because --- argument expression's type is not compatible with formal parameter type; found : (tapir.example.Book, tapir.example.Endpoints.AuthToken) => scala.concurrent.Future[Either[String,Unit]] (which expands to) (tapir.example.Book, String) => scala.concurrent.Future[Either[String,Unit]] required: ?FN[scala.concurrent.Future[Either[String,Unit]]] addBook.toRoute(bookAddLogic _) ~

Why this error? and how to solve?

Handling of value classes

I'm using value classes to have a type-safe representation of e.g. IDs.

For example, I have this case class:

case class UserId(value: String) extends AnyVal

With the help of io.circe.generic.extras.deriveUnwrappedEncoder this is represented in JSON as "id": "1" instead of "id": {"value": "1"}.

How can I make tapir aware of this situation. It currently outputs this OpenAPI doc:

id:
  required:
  - value
  type: object
  properties:
    value:
      type: string

instead of:

id:
  type: string

Improve api consistency

I wonder if it is a good thing that Endpoint has so flexible api, e.g. following case are equivalent:

val booksListing: Endpoint[Limit, String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all")
    .in(limitParameter)
    .out(jsonBody[Vector[Book]])
val booksListing: Endpoint[Limit, String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all" and limitParameter)
    .out(jsonBody[Vector[Book]])
val booksListing: Endpoint[Limit, String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all" / limitParameter)
    .out(jsonBody[Vector[Book]])
val booksListing: Endpoint[Limit, String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all" & limitParameter)
    .out(jsonBody[Vector[Book]])

where limitParameter is definied as follow:

private val limitParameter = query[Option[Int]]("limit")
        .description("Maximum number of books to retrieve")

What's even worse, somebody can just type:

val booksListing: Endpoint[(Limit,Limit) String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all" & limitParameter / "something" / limitParamter)
    .out(jsonBody[Vector[Book]])

which is a pure abomination :)

Doesn't such ambiguity make adoption slower?

In opposite to that I was thinking about something like this:

val booksListing: Endpoint[Limit, String, Vector[Book], Nothing] = baseEndpoint.get
    .in("list" / "all" ? limitParameter & otherQueryParam)
    .out(jsonBody[Vector[Book]])

Where "list" and "all" are both PathParams and only they have this / method to concatenate with other pathParams. Also there is ? method defined on them which allows joining with queryParams and it changes the object type to queryParam so there is no option to add next pathParams anymore (which in contrary is possible with current api).

There are still some things which are unclear with proposed change like:

  • how to concatenate with headers? and other types
  • should we allow specyfing EndpointInput multiple times?

I made a trashy implementation to better visualize my idea.

From it it seems that it is not a good approach due to sealed traits explosion and duplication.

Maybe it would be better to change Endoint.in method to accept some kind of higher abstraction dsl which would be internally converted to old EndpointInput structure?

See e8795f7

Investigate stackoverflow when dealing with tree structure

For structure as in #84 user should be able to simply write:

  def sEdge:SchemaFor[Edge] = implicitly[SchemaFor[Edge]]
  def sSimpleNode:SchemaFor[SimpleNode] = implicitly[SchemaFor[SimpleNode]]

  implicit val sNode: SchemaFor[Node] =
    SchemaFor.oneOf[Node, String](_.kind, _.toString)(
      "Edge" -> sEdge,
      "SimpleNode" -> sSimpleNode
    )

Unfortunately this code produces stackoverflow exception.

Monitor files

Quickpar has the function to monitor files when there are some files are missing or uncomplete. The application looks for new files from time to time and updates the file list. I miss this option in multipar? is there something like this or can it be implemented?

Support for multiple response codes

Currently all responses end with 200 or 400 response codes. When creating a server, this should be customisable. Maybe the endpoint should hold classifiers E => StatusCode, O => StatusCode, where: Endpoint[I, E, O]?

Runtime error when there is no schema for object

Given following enum:

    sealed trait EntityType
    case object Person extends EntityType
    case object Org extends EntityType

and endpoint:

  val e = endpoint.post
    .out(jsonBody[List[EntityType]])

actual behavior:

Exception in thread "main" java.util.NoSuchElementException: key not found: SObjectInfo(sakiewka.local.WalletApi.Response.User,List())
	at scala.collection.MapLike.default(MapLike.scala:235)
	at scala.collection.MapLike.default$(MapLike.scala:234)
	at scala.collection.AbstractMap.default(Map.scala:63)
	at scala.collection.MapLike.apply(MapLike.scala:144)
	at scala.collection.MapLike.apply$(MapLike.scala:143)
	at scala.collection.AbstractMap.apply(Map.scala:63)
	at tapir.docs.openapi.schema.SchemaReferenceMapper.map(SchemaReferenceMapper.scala:8)
	at tapir.docs.openapi.schema.TSchemaToOSchema$$anonfun$apply$2.applyOrElse(TSchemaToOSchema.scala:49)
	at tapir.docs.openapi.schema.TSchemaToOSchema$$anonfun$apply$2.applyOrElse(TSchemaToOSchema.scala:49)
	at scala.PartialFunction.$anonfun$runWith$1$adapted(PartialFunction.scala:145)
	at scala.collection.immutable.Set$Set3.foreach(Set.scala:169)
	at scala.collection.TraversableLike.collect(TraversableLike.scala:274)
	at scala.collection.TraversableLike.collect$(TraversableLike.scala:272)
	at scala.collection.AbstractTraversable.collect(Traversable.scala:108)
	at tapir.docs.openapi.schema.TSchemaToOSchema.apply(TSchemaToOSchema.scala:49)
	at tapir.docs.openapi.schema.TSchemaToOSchema.apply(TSchemaToOSchema.scala:35)
	at tapir.docs.openapi.schema.ObjectSchemas.apply(ObjectSchemas.scala:17)
	at tapir.docs.openapi.CodecToMediaType.apply(CodecToMediaType.scala:18)
	at tapir.docs.openapi.EndpointToOperationResponse$$anonfun$3.applyOrElse(EndpointToOperationResponse.scala:80)
	at tapir.docs.openapi.EndpointToOperationResponse$$anonfun$3.applyOrElse(EndpointToOperationResponse.scala:79)
	at scala.PartialFunction.$anonfun$runWith$1$adapted(PartialFunction.scala:145)
	at scala.collection.Iterator.foreach(Iterator.scala:941)
	at scala.collection.Iterator.foreach$(Iterator.scala:941)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1429)
	at scala.collection.IterableLike.foreach(IterableLike.scala:74)
	at scala.collection.IterableLike.foreach$(IterableLike.scala:73)
	at scala.collection.AbstractIterable.foreach(Iterable.scala:56)
	at scala.collection.TraversableLike.collect(TraversableLike.scala:274)
	at scala.collection.TraversableLike.collect$(TraversableLike.scala:272)
	at scala.collection.AbstractTraversable.collect(Traversable.scala:108)
	at tapir.docs.openapi.EndpointToOperationResponse.outputToResponse(EndpointToOperationResponse.scala:79)
	at tapir.docs.openapi.EndpointToOperationResponse.outputToResponses(EndpointToOperationResponse.scala:43)
	at tapir.docs.openapi.EndpointToOperationResponse.apply(EndpointToOperationResponse.scala:15)
	at tapir.docs.openapi.EndpointToOpenApiPaths.endpointToOperation(EndpointToOpenApiPaths.scala:54)
	at tapir.docs.openapi.EndpointToOpenApiPaths.pathItem(EndpointToOpenApiPaths.scala:32)
	at tapir.docs.openapi.EndpointToOpenAPIDocs$.$anonfun$toOpenAPI$2(EndpointToOpenAPIDocs.scala:19)
	at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:237)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at scala.collection.TraversableLike.map(TraversableLike.scala:237)
	at scala.collection.TraversableLike.map$(TraversableLike.scala:230)
	at scala.collection.immutable.List.map(List.scala:298)
	at tapir.docs.openapi.EndpointToOpenAPIDocs$.toOpenAPI(EndpointToOpenAPIDocs.scala:19)
	at tapir.docs.openapi.TapirOpenAPIDocs$RichOpenAPIEndpoints.toOpenAPI(TapirOpenAPIDocs.scala:18)
	at tapir.docs.openapi.TapirOpenAPIDocs$RichOpenAPIEndpoints.toOpenAPI(TapirOpenAPIDocs.scala:15)

expected behavior:
It should work :)

Also please notice that when specifying endpoint like that:

  val e = endpoint.post
    .out(jsonBody[EntityType])

everything works fine.

I also noticed that when Entity is specified as follow:

 sealed trait EntityType{
    case object Person extends EntityType
    case object Org extends EntityType
}

There is a compilation error about not being able to find schemaFor Entity

Support for sealed trait families

Derive the schema correctly
Specify discriminators for documentation generation

Encoding/decoding is handled by user-provided circe encoders/decoders, so nothing to do in the client/server front

Support for SSE and websockets

Tapir provides support for Akka Http routes and writing client for that, but it would be great if there is a support for Server-sent events (SSE) and websockets at server and client level as Akka Http supports both of them.

Failing tests on travis

Occasionally tests fail on travis due to binding exception.
It affects both akka and http4s tests. I came up with a solution to restart test if the exception was BindException but for unknown reasons to me akkaHttp wraps exception with akka.stream.impl.io.ConnectionSourceStage$$anon$1$$anon$2. I could workaround it catching both exceptions within recoverWith section, or apply this solution separately to both implementations, neither of those options satisfy me.

My solution was (this one works only for akka):

  def testServer(name: String, rs: => NonEmptyList[ROUTE])(runTest: Uri => IO[Assertion]): Unit = {
    val resources = for {
      port <- Resource.liftF(IO(nextPort()))
      _ <- server(rs, port)
    } yield uri"http://localhost:$port"
    test(name)(restartOnBindException(resources).use(runTest).unsafeRunSync())
  }

  private def restartOnBindException(r: Resource[IO, Uri]): Resource[IO, Uri] =
    r.recoverWith { case e if e.getCause.isInstanceOf[BindException] => restartOnBindException(r) }

Might it be related with something like travis-ci/travis-ci#8089?

Add support for scala 2.11

I tried to use tapir with scala 2.11 but cannot locate any public jar files for that version of Scala. Would you be able to support this?

Alternative Client API style

I'm not sure if it makes sense for this project, but in the past I've seen a (closed source) api client generator that generated calls that resembled the API url.

Example: Imagine an endpoint which has the url {host}/api/v1/profile/{profileId}/properties. A "get call" for that endpoint, in client code, would look like ExternalService.api.v1.profile(profileId).properties.get.

The two things that I really like about that was that the code resembled the url and that the IDE helped discover the API with autocompletion.

Is this kind of client something that you would consider?

Scanning for object schemas doesn't go deep enough

There are two cases where the objects are not scanned:

  1. When TSchema.SCoproduct contains a complex object - only the object itself is added - there is no recursive call to the fields
  2. case TSchema.SArray(o: TSchema.SCoproduct) is not covered - it misses the objects that are defined with oneOf if they are in TSchema.SArray

It is impossible to reference to a SCoproduct

I have a complex API structure - in the form of a Graph. the nodes are recursive.

this means that I need to use SRef to avoid cyclic definitions of schemas.
the problem is that I cannot use an SRef to reference a SCoproduct - since SCoproduct doesn't have its own name.

consider this structure for example:

sealed trait Node
case class Edge(id: Long, source: Node, target: Node) extends Node
case class SimpleNode(id: Long, data: List[Data]) extends Node

case class Data(attribute: Attr)

sealed trait Attr
case class SimpleAttr(name: String, value: String) extends Attr
case class MultiAttr(name: String, value: List[String]) extends Attr

defining implicit schemas for the above class would require me to use:

  implicit val sNode: SchemaFor[Node] =
    SchemaFor.oneOf[Node, String](_.kind, _.toString)(
      "Edge" -> sEdge,
      "SimpleNode" -> sSimpleNode
    ) 

and then I would need to reference it:

  implicit val sEdge: SchemaFor[Edge] =
    SchemaFor(SObject(Schema.SObjectInfo("Edge"), List(
      ("id", Schema.SNumber),
      ("source", SRef(SObjectInfo("Node"))),
      ("target", SRef(SObjectInfo("Node")))
    ), List("id", "source", "target")))

but this doesn't work - since there is no such referench for "Node" since it is a SCoproduct and doesn't have an ObjectInfo

Multipart support

New endpoint io: multipart[T](name), which yields/accepts a Part[T] value, which has the following parameters:

  • additional headers
  • optional filename
  • content

The part content can be any of the basic body types (string, byte array, file, etc.). Are streaming parts possible? Probably not.

DecodeFailureHandler should optionally return error values

The DecodeFailureHandler should be extended with a type parameter: DecodeFailureHandler[+E], so that for a decode failure, it can either directly complete a request, return a no match, or (which is new) an error value, which will then be handled as if the server logic returned that error value.

The default failure handler can have the type DecodeFailureHandler[Nothing] (hence the variance)

All-parameter inputs/outputs

Support inputs/outputs which capture all parameters of a given type:

  • allQueryParams: Seq[(String, String)] / queryParams
  • allHeaders: Seq[(String, String)] / headers
  • remainingPath: List[String]

All urlencoded-form parameters are already part of #2 (supporting the form data body type).

Detailed comparison for the similar projects

Tapir is a really nice start! But there are already many other projects similar to this and it may be really hard to understand which one to choose and when. Would be nice to have a detailed comparison of the main competitors. :)

Content-Type JSON is missing since version 0.7.7

Hi,

I've found some regression. Since version 0.7.7 Content-Type JSON is not present in response from generated routes (http4s).

Example response with Tapir 0.7.6:

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json
Date: Thu, 16 May 2019 13:41:35 GMT

{
    "x": "1 ABC", 
    "y": 6
}

Example response with Tapir 0.7.9 (the same for 0.7.8 and 0.7.7):

HTTP/1.1 200 OK
Content-Length: 19
Date: Thu, 16 May 2019 13:40:49 GMT

{"x":"1 ABC","y":6}

Support for other body types

To be done:

  • body[Array[Byte]]
  • body[ByteBuffer]
  • body[InputStream]
  • body[File]: save server request body/client response to temporary file (in the future: configurable file storage); server response/client request: send the given file
  • body[Seq[(String, String)]] / body[Map[String, String]]: form data parameters (only for urlencoded forms, not multipart!)

Request handled by wrong route

when defining 2 routes: one with /xx/{id} (id defined as Long) and another with /xx/yy
the request to /xx/yy is handled by the route for /xx/{id}

the error I get for it is: Invalid value for: path
which is not very informative, I would rather also get the caused by - as only by debugging it I understood what was the bug.
in my case that would have been: numberFormatException on "export"

I have the following base endpoints: (they contain previous parts of the paths - 3 Ints as path param)

val notionEndpoint = baseEndpoint.in("notion")
val notionIdEndpoint = notionEndpoint.in(path[Long]("notionId"))

then I have some routes:

 val getNotion: ServerEndpoint[(Int, Int, Int, Long), Unit, Option[Notion], Nothing, Future] =
      notionIdEndpoint.get.summary("Get a single notion").tag("Notion")
        .out(jsonBody[Option[Notion]])
        .serverLogic((getNotionLogic _).tupled)

val export: ServerEndpoint[(Int, Int, Int, Boolean, Boolean), Unit, (String, Nodes), Nothing, Future] = {
      notionEndpoint.get
        .in("export")
        .in(query[Option[Boolean]]("includeX")
          .example(Some(true))
          .map(_.getOrElse(true))(Option.apply))
        .in(query[Option[Boolean]]("includeY")
          .example(Some(true))
          .map(_.getOrElse(true))(Option.apply))
        .out(header[String]("Content-Disposition"))
        .out(jsonBody[Nodes])
        .tag("Import Export")
        .serverLogic((exportLogic _).tupled)
    }

and lastly - I return a list of endpoints to be later transformed to routes:

List(getNotion, export)

workaround: manually set the order of the endpoints for the export to be first

Error Handling & status codes - default and route specific

I'd like to have the 'default' error types defined once and to be able to handle exceptions and return those error types.

i.e. have one place that defines: 400, 401, 404, 409, etc. with a default message (i.e. 401 Not Authorized)
and if there is a specific error for a specific route - add its own error type and doc, and be able to override an already existing status code.
i.e. in the specific route - 401 "You don't have permissions to modify the book store"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.