Skip to content

Commit

Permalink
chore(docs): Initial version of Docs
Browse files Browse the repository at this point in the history
Most of this is straight from the RFC, but I added a couple lines and
images for explanation.

The Introduction Sections both will need some of the teams input to
introduce the "vision"

Also I'm using Prettier so it autoformatted the config file, but there
were no changes. I wasn't sure whether to include in the PR, but if the
team uses Prettier as well, this will make future PRs easier
  • Loading branch information
tadaspetra authored Jan 11, 2024
1 parent efffa20 commit 60f65c7
Show file tree
Hide file tree
Showing 28 changed files with 614 additions and 529 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"label": "Tutorial - Extras",
"position": 3,
"label": "Functions",
"position": 2,
"link": {
"type": "generated-index"
}
Expand Down
100 changes: 100 additions & 0 deletions docs/functions/data-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
sidebar_position: 6
---

# Use Custom Data Types

With Celest Functions, serialization is handled out-of-the-box in most cases. In situations requiring custom serialization, we support any custom classes that you’re already using without any extra setup.

Imagine you're working on an e-commerce application with an `Order` class defined in your codebase.

```dart
class Order {
const Order ({
required this.id,
required this.customerName,
required this.price,
});
final int id;
final String customerName;
final Price price;
}
enum Currency { usd, cad, ... }
class Price {
const Price({
required this.currency,
required this.dollars,
required this.cents,
}): assert(cents < 100);
final Currency currency;
final int dollars;
final int cents;
}
```

You can use this `Order` type in any Celest Function as both a parameter or return value, without the need to manually add serialization logic.

```dart
import 'package:celest/celest.dart';
import 'types/order.dart';
Future<String> createOrder(
FunctionContext context,
Order customerOrder,
) async {
// ...
}
```

When communicating with your backend, Celest will serialize the `Order` class as a JSON map with the field names as keys.

```json
{
"id": 123,
"customerName": "Celest",
"price": {
"currency": "usd",
"dollars": 100,
"cents": 34
}
}
```


## Custom Serialization Logic
If you need custom handling over serialization logic, add a `fromJson` constructor and `toJson` method to your datatype. Celest will use your custom `fromJson`/`toJson` implementations instead when transmitting the type to and from your backend.

Here, the `Price.toJson` method is used to upper-case the `currency` value.

```dart
class Price {
// ...
factory Price.fromJson(Map<String, dynamic> json) {
// ...
}
Map<String, dynamic> toJson() => {
'currency': currency.name.toUpperCase(),
'dollars': dollars,
'cents': cents,
};
}
```

```json
{
"id": 123,
"customerName": "Celest",
"price": {
"currency": "USD",
"dollars": 100,
"cents": 34
}
}
```
71 changes: 71 additions & 0 deletions docs/functions/env-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
sidebar_position: 8
---

# Managing Environment Variables

Environment variables can be used to provide environment-specific configuration to your backend. They allow you to keep their values separate from your codebase, improving flexibility when running in different environments.

To set up environment variables in your backend, navigate to the `<flutter_app>/celest/config/env.dart` file and list all the variables you’ll need throughout your backend.

```dart
import 'package:celest/celest.dart';
const EnvironmentVariable greetingUrl = EnvironmentVariable(name: 'GREETING_URL');
```

To ensure a function has access to the variable when it runs, pass it as a parameter and annotate with the variable definition. Here, the greeting service URL will be securely injected by the server when your function starts.


:::note
Annotated parameters (like `greetingUrl`) will not appear in the generated client, but can be used in your backend when unit testing and mocking (see [Testing your backend resources](/docs//functions/testing)).
:::

```dart
import 'package:celest/celest.dart';
import 'package:http/http.dart' as http;
import '../resources.dart';
Future<String> sayHello(
FunctionContext context,
String name, {
@envVariables.greetingUrl required String greetingUrl,
}) async {
// Call an external greeting service.
final response = await http.post(
Uri.parse(greetingUrl).replace(path: '/sayHello'),
body: jsonEncode({
'name': name,
}),
);
if (response.statusCode != 200) {
throw GreetingException(
'Failed to say hello to $name: ${response.body}',
);
}
return response.body;
}
class GreetingException implements Exception {
const GreetingException(this.message);
final String message;
}
```

### Setting up environment variable values locally
When you run `celest start` or `celest deploy`, the CLI will look for values of the environment variables in your shell environment. For any variables not defined, the CLI will prompt you for their values.

```shell
Please provide values for the following environment variables:
? GREETING_URL: <Enter your value>
```

To change the values of environment variables previously defined, re-export the value from your terminal before running `celest start` or `celest deploy`.

```shell
export GREETING_URL=<new URL>
```

Celest will detect the presence of a new value and update your local/deployed function to reflect the change.
52 changes: 52 additions & 0 deletions docs/functions/exceptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
sidebar_position: 7
---

# Create Custom Exceptions

You can create custom exception types in your backend to control how functions behave when there are errors. This enables you to have clear exceptions thrown in your Flutter app that you can react to.

Below is an example of how to define a custom exception. You can create exceptions in any folder inside your `celest` folder. For this example, the exception type is defined in `<flutter_app>/celest/functions/my_exception.dart`.

```dart
class MyException implements Exception {
const MyException(this.message);
final String message;
}
```

You can then throw these exceptions in your functions whenever needed as shown below.

```dart
import 'package:celest/celest.dart';
import 'my_exception.dart';
Future<String> sayHello(
FunctionContext context,
String name,
) async {
// Perform custom validation
if (name.isEmpty) {
throw MyException('Input cannot be empty');
}
return 'Hello, $name';
}
```

In your Flutter app, the same `MyException` type will be thrown by the generated client if an error occurs.

```dart
import 'celest/client.dart' as celest;
Future<String> getGreeting(String name) async {
try {
return await celest.functions.greeting.sayHello(name);
// Catch the exception type defined in your backend
} on MyException catch (e) {
print('Uh oh! Could not greet $name: $e');
rethrow;
}
}
```
74 changes: 74 additions & 0 deletions docs/functions/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
sidebar_position: 3
---

# Create a Function

:::warning
Ensure you have completed [Setting up Celest](/docs/functions/get-started.md) prior to reading this section.
:::

Creating functions with Celest enables you to connect and aggregate information from different parts of your backend, and build custom business logic that runs completely in the cloud. You define your functions as regular Dart functions, and Celest takes care of setting up and managing the backend infrastructure around them.


![Function Call Flow](img/function.png)

To get started with building your first function, navigate to the `<flutter_app>/celest/functions/` folder and create a file named `<function_file>.dart`. You can create as many function files as you want in this directory. The name of the file is used to organize and retrieve these functions.

:::tip
Access to your functions is denied by default. What this means is that you’ll need to add the `@functions.public()` annotation to the top of the file for functions to be publicly accessible.

This can be defined for all functions in a file or on a per-function basis. (Same syntax as [middleware](/docs/functions/middleware#applying-middleware-to-single-function))
:::

```dart
// Enables public access to the function.
@functions.public()
library;
import 'package:celest/functions.dart' as functions;
Future<String> sayHello(
FunctionContext context,
String name,
) async {
return 'Hello, $name';
}
Future<String> sayGoodbye(
FunctionContext context,
String name,
) async {
return 'Goodbye, $name';
}
```

The above code snippet is all you need to define your functions! When the `celest start` command runs, a local environment is spun up and a Dart client is generated to help you connect to the local backend.

Below is an example of how you would use the generated client in your `main.dart` file.

```dart
import 'package:flutter/material.dart';
import 'package:flutter_app/celest/client.dart' as celest;
void main() {
celest.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return FutureBuilder(
// Call your function like a normal Dart function!
future: celest.functions.greeting.sayHello('Celest'),
builder: (_, snapshot) => switch (snapshot) {
AsyncSnapshot(:final data?) => Text(data),
_ => const CircularProgressIndicator(),
},
);
}
}
```
50 changes: 50 additions & 0 deletions docs/functions/get-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
sidebar_position: 2
---

# Setup Celest

## Prerequisites
To use Celest in your Flutter app, you need the following prerequisites:

1. Install Flutter
2. Start a new fluter project using the `flutter create` command
3. Install the Celest CLI using a shell command



```shell
$ curl --proto '=https' --tlsv1.2 https://install.celest.dev | sh
```

That’s it! You do not need any additional tooling to build, test, and deploy your backend.

## Setting up the Celest CLI
After installing the Celest CLI, navigate to the root of your Flutter project and run the following command.

```shell
$ celest start
```

You will be prompted to sign in using GitHub. Once the authentication with GitHub is successful, a watch command will continue to run in your CLI to detect changes made to your Celest backend definition and code-generate a Dart client for you in the following path `<flutter_app>/lib/celest/client.dart` to test your changes locally. We will cover later how to use the code-generated client after defining your Celest Functions.

The CLI will also create a folder in your project called `celest`, which will include the following files.

```shell
flutter_app/
└── celest/
├── functions/
│ ├── greeting.dart # a function definition
│ └── middleware.dart # middleware definitions
└── config/
└── env.dart # environment variables
```


## Deploying your backend resources
When you have tested and validated your backend locally, use the Celest CLI to deploy your backend resources to the cloud.

```shell
$ celest deploy
```

Loading

0 comments on commit 60f65c7

Please sign in to comment.