Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/lemon-eagles-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@effect/platform-node": patch
"@effect/platform": patch
---

add template literal api for defining HttpApiEndpoint path schema
5 changes: 1 addition & 4 deletions packages/platform-node/examples/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ export class Authentication extends HttpApiMiddleware.Tag<Authentication>()("Aut

class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/:id")
.setPath(Schema.Struct({
id: Schema.NumberFromString
}))
HttpApiEndpoint.get("findById")`/${HttpApiSchema.param("id", Schema.NumberFromString)}`
.addSuccess(User)
.setHeaders(Schema.Struct({
page: Schema.NumberFromString.pipe(
Expand Down
18 changes: 6 additions & 12 deletions packages/platform-node/test/HttpApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,11 @@ class Authorization extends HttpApiMiddleware.Tag<Authorization>()("Authorizatio

class GroupsApi extends HttpApiGroup.make("groups")
.add(
HttpApiEndpoint.get("findById", "/:id")
.setPath(Schema.Struct({
id: Schema.NumberFromString
}))
HttpApiEndpoint.get("findById")`/${HttpApiSchema.param("id", Schema.NumberFromString)}`
.addSuccess(Group)
)
.add(
HttpApiEndpoint.post("create", "/")
HttpApiEndpoint.post("create")`/`
.setPayload(Schema.Union(
Schema.Struct(Struct.pick(Group.fields, "name")),
Schema.Struct({ foo: Schema.String }).pipe(
Expand All @@ -309,14 +306,11 @@ class GroupsApi extends HttpApiGroup.make("groups")

class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/:id")
.setPath(Schema.Struct({
id: Schema.NumberFromString
}))
HttpApiEndpoint.get("findById")`/${HttpApiSchema.param("id", Schema.NumberFromString)}`
.addSuccess(User)
)
.add(
HttpApiEndpoint.post("create", "/")
HttpApiEndpoint.post("create")`/`
.setPayload(Schema.Struct(Struct.omit(
User.fields,
"id",
Expand All @@ -329,7 +323,7 @@ class UsersApi extends HttpApiGroup.make("users")
.addError(UserError)
)
.add(
HttpApiEndpoint.get("list", "/")
HttpApiEndpoint.get("list")`/`
.setHeaders(Schema.Struct({
page: Schema.NumberFromString.pipe(
Schema.optionalWith({ default: () => 1 })
Expand All @@ -342,7 +336,7 @@ class UsersApi extends HttpApiGroup.make("users")
.annotateContext(OpenApi.annotations({ identifier: "listUsers" }))
)
.add(
HttpApiEndpoint.post("upload", "/upload")
HttpApiEndpoint.post("upload")`/upload`
.setPayload(HttpApiSchema.Multipart(Schema.Struct({
file: Multipart.SingleFileSchema
})))
Expand Down
61 changes: 35 additions & 26 deletions packages/platform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Let's define a simple CRUD API for managing users. First, we need to make an
`HttpApiGroup` that contains our endpoints.

```ts
import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform"
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"
import { Schema } from "effect"

// Our domain "User" Schema
Expand All @@ -44,17 +44,16 @@ class User extends Schema.Class<User>("User")({
const usersApi = HttpApiGroup.make("users")
.add(
// each endpoint has a name and a path
HttpApiEndpoint.get("findById", "/users/:id")
// You can use a template string to define path parameter schemas
HttpApiEndpoint.get(
"findById"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
// the endpoint can have a Schema for a successful response
.addSuccess(User)
// and here is a Schema for the path parameters
.setPath(
Schema.Struct({
id: Schema.NumberFromString
})
)
)
.add(
// you can also pass the path as a string and use `.setPath` to define the
// path parameter schema
HttpApiEndpoint.post("create", "/users")
.addSuccess(User)
// and here is a Schema for the request payload / body
Expand All @@ -68,9 +67,15 @@ const usersApi = HttpApiGroup.make("users")
)
)
// by default, the endpoint will respond with a 204 No Content
.add(HttpApiEndpoint.del("delete", "/users/:id"))
.add(
HttpApiEndpoint.patch("update", "/users/:id")
HttpApiEndpoint.del(
"delete"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
)
.add(
HttpApiEndpoint.patch(
"update"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
.addSuccess(User)
.setPayload(
Schema.Struct({
Expand All @@ -85,7 +90,9 @@ We will use this API style in the following examples:

```ts
class UsersApi extends HttpApiGroup.make("users").add(
HttpApiEndpoint.get("findById", "/users/:id")
HttpApiEndpoint.get(
"findById"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
// ... same as above
) {}
```
Expand Down Expand Up @@ -117,7 +124,9 @@ import { OpenApi } from "@effect/platform"

class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/users/:id")
HttpApiEndpoint.get(
"findById"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
// ... same as above
)
// add an OpenApi title & description
Expand Down Expand Up @@ -170,11 +179,12 @@ class Unauthorized extends Schema.TaggedError<Unauthorized>()(

class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/users/:id")
HttpApiEndpoint.get(
"findById"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`
// here we are adding our error response
.addError(UserNotFound, { status: 404 })
.addSuccess(User)
.setPath(Schema.Struct({ id: Schema.NumberFromString }))
)
// or we could add an error to the group
.addError(Unauthorized, { status: 401 }) {}
Expand All @@ -195,7 +205,7 @@ shape of the multipart request.
import { HttpApiSchema, Multipart } from "@effect/platform"

class UsersApi extends HttpApiGroup.make("users").add(
HttpApiEndpoint.post("upload", "/users/upload").setPayload(
HttpApiEndpoint.post("upload")`/users/upload`.setPayload(
HttpApiSchema.Multipart(
Schema.Struct({
// add a "files" field to the schema
Expand All @@ -215,7 +225,7 @@ Here is an example of changing the encoding to text/csv:

```ts
class UsersApi extends HttpApiGroup.make("users").add(
HttpApiEndpoint.get("csv", "/users/csv").addSuccess(
HttpApiEndpoint.get("csv")`/users/csv`.addSuccess(
Schema.String.pipe(
HttpApiSchema.withEncoding({
kind: "Text",
Expand Down Expand Up @@ -248,7 +258,8 @@ import {
HttpApi,
HttpApiBuilder,
HttpApiEndpoint,
HttpApiGroup
HttpApiGroup,
HttpApiSchema
} from "@effect/platform"
import { DateTime, Effect, Layer, Schema } from "effect"

Expand All @@ -260,13 +271,11 @@ class User extends Schema.Class<User>("User")({
}) {}

class UsersApi extends HttpApiGroup.make("users").add(
HttpApiEndpoint.get("findById", "/users/:id")
.addSuccess(User)
.setPath(
Schema.Struct({
id: Schema.NumberFromString
})
)
HttpApiEndpoint.get(
"findById"
)`/users/${HttpApiSchema.param("id", Schema.NumberFromString)}`.addSuccess(
User
)
) {}

class MyApi extends HttpApi.empty.add(UsersApi) {}
Expand Down Expand Up @@ -428,7 +437,7 @@ class Logger extends HttpApiMiddleware.Tag<Logger>()("Http/Logger", {
// apply the middleware to an `HttpApiGroup`
class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/:id")
HttpApiEndpoint.get("findById")`/${Schema.NumberFromString}`
// apply the middleware to a single endpoint
.middleware(Logger)
)
Expand Down Expand Up @@ -492,7 +501,7 @@ class Authorization extends HttpApiMiddleware.Tag<Authorization>()(
// apply the middleware to an `HttpApiGroup`
class UsersApi extends HttpApiGroup.make("users")
.add(
HttpApiEndpoint.get("findById", "/:id")
HttpApiEndpoint.get("findById")`/${Schema.NumberFromString}`
// apply the middleware to a single endpoint
.middleware(Authorization)
)
Expand Down
Loading