Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Customizing HTTP behavior #136

Closed
dnys1 opened this issue May 20, 2024 · 2 comments
Closed

[RFC] Customizing HTTP behavior #136

dnys1 opened this issue May 20, 2024 · 2 comments
Labels
RFC Request for comments

Comments

@dnys1
Copy link
Member

dnys1 commented May 20, 2024

This is a request-for-comment regarding the ability to customize the HTTP API exposed by Celest Functions.


In cases where Celest's standard HTTP conventions do not align with your requirements, you can control the HTTP API generated for your Celest Functions using the HTTP annotations.

Execution Controls

The @http and @httpError annotations allow you to control the behavior of the HTTP endpoint, such as how the endpoint is exposed, and how it responds in both success and error cases.

@http

To configure the method and default status code of your HTTP endpoint, use the @http annotation.

@http(
  method: HttpMethod.put, 
  statusCode: HttpStatus.created,
)
Future<String> greet(String name) async {
  return 'Hello, $name';
}

No changes are made to the generated client interface, only its execution.

final result = await celest.functions.greet('Celest');
PUT /greet
Content-Type: application/json

{
  "name": "Celest"
}
HTTP/1.1 201 Created
Content-Type: application/json

{
  "response": "Hello, Celest"
}

@httpError

To control the status code returned from one or more thrown types, use the @httpError annotation.

@httpError(403, UnauthorizedException)
@httpError(404, BadNameException, NotFoundException)
Future<String> greet(String name) async {
  if (name.isEmpty) {
    throw BadNameException();
  }
  if (!name.startsWith('C')) {
    throw NotFoundException();
  }
  if (name != 'Celest') {
    throw UnauthorizedException();
  }
  return 'Hello, $name';
}

Exception types are handled in order of specificity. Specifying a type that other types inherit from or implement will only apply the status code to subtypes which are not already covered by a more specific case.

For example, the following will return a 404 status code for both the concrete NotFoundException type and BadNameException (since it is a subtype of Exception), but not UnauthorizedException which is covered by a more specific control.

@httpError(404, NotFoundException, Exception)
@httpError(403, UnauthorizedException)
Future<String> greet(String name) async {
  if (name.isEmpty) {
    throw BadNameException(); // 404
  }
  if (!name.startsWith('C')) {
    throw NotFoundException(); // 404
  }
  if (name != 'Celest') {
    throw UnauthorizedException(); // 403
  }
  return 'Hello, $name';
}

Request Controls

The @httpHeader and @httpQuery annotations allow you to map input parameters to HTTP headers and query parameters, respectively.

Any parameters which are not targeted by these annotations are passed as the request body.

Note

Some combinations are disallowed. For example, if the @http annotation specifies a GET method, then all input
parameters must be mapped, or there must be no input parameters, such that the request body is empty.

@httpHeader

To map an input parameter to an HTTP header, use the @httpHeader annotation.

Note

Only parameters of type String, int, double, bool, DateTime, or a List of these types, can be targeted by @httpHeader.

Future<String> greet(
  @httpHeader('x-api-key') String apiKey,
  String name,
) async {
  return 'Hello, $name';
}

In the generated client, these parameters are passed transparently as function arguments.

final result = await celest.functions.greet('123', 'Celest');
POST /greet
Content-Type: application/json
x-api-key: 123

{
  "name": "Celest"
}
HTTP/1.1 200 OK
Content-Type: application/json

{
  "response": "Hello, Celest"
}

The following HTTP headers are reserved and cannot be targeted by @httpHeader:

Header Name
Connection
Content-Length
Expect
Host
Max-Forwards
Server
TE
Trailer
Transfer-Encoding
Upgrade
User-Agent
Via
X-Forwarded-For

@httpQuery

To map an input parameter to a query parameter, use the @httpQuery annotation.

Note

Only parameters of type String, int, double, bool, DateTime, or a List of these types, can be targeted by @httpQuery.

@http(method: HttpMethod.get)
Future<String> greet(
  @httpQuery('name') String name,
) async {
  return 'Hello, $name';
}

In the generated client, these parameters are passed transparently as function arguments.

final result = await celest.functions.greet('Celest');
GET /greet?name=Celest
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "response": "Hello, Celest"
}
@dnys1 dnys1 added the RFC Request for comments label May 20, 2024
@dnys1 dnys1 pinned this issue May 20, 2024
@dnys1
Copy link
Member Author

dnys1 commented May 28, 2024

This feature has been released in Celest 0.4.0! You can read all the details here: https://www.celest.dev/docs/functions/http/customization

I'll leave this issue open to collect feedback as you explore it 🚀

@dnys1
Copy link
Member Author

dnys1 commented Oct 15, 2024

Feedback still welcome - V1 has now been released and we'll continue to explore the features necessary to give you the right level of customization building Celest backends 💪

@dnys1 dnys1 closed this as completed Oct 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC Request for comments
Projects
None yet
Development

No branches or pull requests

1 participant