-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(docs): Initial version of Docs
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
1 parent
efffa20
commit 60f65c7
Showing
28 changed files
with
614 additions
and
529 deletions.
There are no files selected for viewing
4 changes: 2 additions & 2 deletions
4
docs/tutorial-extras/_category_.json → docs/functions/_category_.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
}, | ||
); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` | ||
|
Oops, something went wrong.