Skip to main content

Examples

This section provides comprehensive examples of using Baklava to document different types of API endpoints. We use Pekko HTTP and Spec2 here.

Basic CRUD Operations

Base test class

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshalling.ToEntityMarshaller
import org.apache.pekko.http.scaladsl.server.Route
import org.apache.pekko.http.scaladsl.testkit.Specs2RouteTest
import org.apache.pekko.http.scaladsl.unmarshalling.FromEntityUnmarshaller
import org.specs2.mutable.SpecificationLike
import org.specs2.specification.core.AsExecution
import org.specs2.specification.core.Fragment
import org.specs2.specification.core.Fragments
import pl.iterators.baklava.pekkohttp.BaklavaPekkoHttp
import pl.iterators.baklava.specs2.BaklavaSpecs2

trait BaseRouteSpec
extends Specs2RouteTest
with SpecificationLike
with BaklavaPekkoHttp[Fragment, Fragments, AsExecution]
with BaklavaSpecs2[Route, ToEntityMarshaller, FromEntityUnmarshaller] {

// Define the routes to test
def allRoutes: Route = UserApiServer.userRoutes

// Required implementations for Baklava framework
implicit val executionContext: scala.concurrent.ExecutionContext =
system.dispatcher

def strictHeaderCheckDefault: Boolean = false

override def performRequest(
routes: Route,
request: HttpRequest
): HttpResponse =
request ~> routes ~> check {
response
}
}

List of users

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.example.baklava.UserApiServer.*

class GetUsersRouteSpec extends BaseRouteSpec {

path(path = "/users")(
supports(
GET,
description = "Get all users with optional pagination and search",
summary = "Retrieve a list of users",
queryParameters = (
q[Option[Int]]("page"),
q[Option[Int]]("limit"),
q[Option[String]]("search")
),
tags = List("Users")
)(
onRequest(queryParameters = (None, None, None))
.respondsWith[List[User]](OK, description = "Return all users")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(5)
},
onRequest(queryParameters = (Some(1), Some(2), None))
.respondsWith[List[User]](OK, description = "Return first page with 2 users")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(2)
},
onRequest(queryParameters = (None, None, Some("jane")))
.respondsWith[List[User]](OK, description = "Return users matching 'jane'")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(1)
}
)
)

}

Getting user by id

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.example.baklava.UserApiServer.*

class GetUsersUserIdRouteSpec extends BaseRouteSpec {

path(path = "/users/{userId}")(
supports(
GET,
pathParameters = p[Long]("userId"),
description = "Get a specific user by ID",
summary = "Retrieve a specific user",
tags = List("Users")
)(
onRequest(pathParameters = (1L))
.respondsWith[User](OK, description = "Return user with ID 1")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.id should beEqualTo(1L)
},
onRequest(pathParameters = (999L))
.respondsWith[ErrorResponse](NotFound, description = "Return 404 for non-existent user")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
ErrorResponse("User with the specified ID does not exist", "USER_NOT_FOUND")
}
}
)
)

}

Create new user

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.example.baklava.UserApiServer.*

class PostUsersRouteSpec extends BaseRouteSpec {

path(path = "/users")(
supports(
POST,
description = "Create a new user",
summary = "Create a new user",
tags = List("Users")
)(
onRequest(body = CreateUserRequest("Test User", "test@example.com"))
.respondsWith[User](Created, description = "User created successfully")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
User(6L, "Test User", "test@example.com")
}
response.headers.exists(h => h.name == "Location" && h.value == s"/users/${response.body.id}") should beTrue
}
)
)

}

Update user

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.example.baklava.UserApiServer.*

class PutUsersUserIdRouteSpec extends BaseRouteSpec {

path(path = "/users/{userId}")(
supports(
PUT,
pathParameters = p[Long]("userId"),
description = "Update an existing user",
summary = "Update an existing user",
tags = List("Users")
)(
onRequest(pathParameters = (1L), body = UpdateUserRequest(Some("Updated User"), Some("updated@example.com")))
.respondsWith[User](OK, description = "User updated successfully")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
User(1L, "Updated User", "updated@example.com")
}
},
onRequest(pathParameters = (999L), body = UpdateUserRequest(Some("Updated User"), Some("updated@example.com")))
.respondsWith[ErrorResponse](NotFound, description = "Return 404 for non-existent user")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
ErrorResponse("User does not exist", "USER_NOT_FOUND")
}
}
)
)

}

Delete user

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.baklava.EmptyBody
import pl.iterators.example.baklava.UserApiServer.*

class DeleteUsersUserIdRouteSpec extends BaseRouteSpec {

path(path = "/users/{userId}")(
supports(
DELETE,
pathParameters = p[Long]("userId"),
description = "Delete an existing user",
summary = "Delete an existing user",
tags = List("Users")
)(
onRequest(pathParameters = (1L))
.respondsWith[EmptyBody](NoContent, description = "User deleted successfully")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.status.status should beEqualTo(204)
},
onRequest(pathParameters = (999L))
.respondsWith[ErrorResponse](NotFound, description = "Return 404 for non-existent user")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
ErrorResponse("User does not exist", "USER_NOT_FOUND")
}
}
)
)

}

Authorization

Define security schema in test case:

import pl.iterators.baklava.{HttpBearer, SecurityScheme}

val bearer: HttpBearer = HttpBearer(bearerFormat = "JWT")
val bearerScheme: SecurityScheme = SecurityScheme("bearer", bearer)

Then add security attribute in onRequest and test for getting all users with authorization can look like that:

package pl.iterators.example.baklava

import org.apache.pekko.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.*
import org.apache.pekko.http.scaladsl.model.HttpMethods.*
import org.apache.pekko.http.scaladsl.model.StatusCodes.*
import org.specs2.specification.core.AsExecution
import pl.iterators.baklava.{HttpBearer, SecurityScheme}
import pl.iterators.example.baklava.UserApiServer.*

class GetUsersRouteSpec extends BaseRouteSpec {

val bearer: HttpBearer = HttpBearer(bearerFormat = "JWT")
val bearerScheme: SecurityScheme = SecurityScheme("bearer", bearer)

path(path = "/users")(
supports(
GET,
description = "Get all users with optional pagination and search",
summary = "Retrieve a list of users",
securitySchemes = Seq(bearerScheme),
queryParameters = (
q[Option[Int]]("page"),
q[Option[Int]]("limit"),
q[Option[String]]("search")
),
tags = List("Users")
)(
onRequest(queryParameters = (None, None, None))
.respondsWith[ErrorResponse](Unauthorized, description = "Return 401 for missing token")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body should beEqualTo {
ErrorResponse("Unauthorized", "UNAUTHORIZED")
}
},
onRequest(queryParameters = (None, None, None), security = bearer.apply("token"))
.respondsWith[List[User]](OK, description = "Return all users")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(5)
},
onRequest(queryParameters = (Some(1), Some(2), None), security = bearer.apply("token"))
.respondsWith[List[User]](OK, description = "Return first page with 2 users")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(2)
},
onRequest(queryParameters = (None, None, Some("jane")), security = bearer.apply("token"))
.respondsWith[List[User]](OK, description = "Return users matching 'jane'")
.assert { ctx =>
val response = ctx.performRequest(allRoutes)

response.body.length should beEqualTo(1)
}
)
)

}