docs: Rework the Build your first app guide for 3.5#557
Conversation
Swiftaxe
left a comment
There was a problem hiding this comment.
Overall, a massive improvement of this guide! I added some comments to polish some details even further.
| - Persistent storage with the database. | ||
| - A Flutter app that talks to all of it through the generated client. | ||
|
|
||
| We're excited to see what you'll build next. If you need help, ask in our [community on GitHub](https://github.com/serverpod/serverpod/discussions) or join the [Discord community](https://serverpod.dev/discord). To go deeper into any topic, browse the [Concepts](../06-concepts/01-working-with-endpoints/01-working-with-endpoints.md) section. |
There was a problem hiding this comment.
Discord is the main channel for community discussions, so rearrange this to front with Discord.
| ); | ||
| } | ||
| } | ||
| import 'package:magic_recipe_client/magic_recipe_client.dart'; |
There was a problem hiding this comment.
While most of this code example is related to the Flutter framework, which needs no explanation, this import is a serverpod concept. We should say something about why we add it.
| /// Holds the last result or null if no result exists yet. | ||
| class _RecipeScreenState extends State<RecipeScreen> { | ||
| /// The recipe currently shown, or null if there's none yet. | ||
| Recipe? _recipe; |
There was a problem hiding this comment.
This is the first time we use a type in Flutter that was generated by Serverpod. It deserves at least a sentence. See the comment above about the import.
|
|
||
| :::info | ||
| On the server, you can do things you don't want to do in the app, like calling an API secured by a secret key or accessing a database. The server can also do things that are impossible in the app, like sending push notifications or emails. | ||
| The server is the right place for work you can't or shouldn't do in the app, such as calling an API secured by a secret key, accessing a database, or sending push notifications and emails. Here, it keeps your Gemini API key off the client. |
There was a problem hiding this comment.
Sometimes we call the serverpod application and 'app', so let's be specific about what type of app here:
| The server is the right place for work you can't or shouldn't do in the app, such as calling an API secured by a secret key, accessing a database, or sending push notifications and emails. Here, it keeps your Gemini API key off the client. | |
| The server is the right place for work you can't or shouldn't do in the Flutter app, such as calling an API secured by a secret key, accessing a database, or sending push notifications and emails. Here, it keeps your Gemini API key off the client. |
| ## Before you start | ||
|
|
||
| - [Serverpod installed](../04-get-started/01-installation.md). Run `serverpod version` to confirm it works. | ||
| - A free Gemini API key. Create one on [Google AI Studio](https://aistudio.google.com/app/apikey); it's free, but you need to sign in with a Google account. |
There was a problem hiding this comment.
We leave the user without guidance on how to create an API key. Consider adding a screenshot and instructions, or just linking to a high-quality guide maintained by the Google AI Studio team, to explain the steps.
| You've built and deployed a full-stack app with Flutter and Serverpod: | ||
|
|
||
| - A custom endpoint that calls an external API from the server. | ||
| - A typed data model shared between the server and the app. |
There was a problem hiding this comment.
| - A typed data model shared between the server and the app. | |
| - A typed data model shared between the server and the Flutter app. |
|
|
||
| :::info | ||
| For methods to be recognized by Serverpod, they need to return a typed `Future` or `Stream`, where the type must be `void` `bool`, `int`, `double`, `String`, `UuidValue`, `Duration`, `DateTime`, `ByteData`, `Uri`, `BigInt`, or a [serializable model](../06-concepts/02-models/01-models.md). The first parameter must be a `Session` object. You can pass any serializable types as parameters, and even use `List`, `Map`, `Set` or Dart records as long as they are typed. | ||
| Endpoint methods take a `Session` as their first parameter and return a typed `Future` or `Stream`. You can pass and return primitive types or any [serializable model](../06-concepts/02-models/01-models.md). The class name's `Endpoint` suffix is dropped on the client, so `RecipeEndpoint` is called through `client.recipe`. See [How it works](../04-get-started/03-how-it-works.md) for how that call reaches the server. |
There was a problem hiding this comment.
Some minor simplification of the language:
| Endpoint methods take a `Session` as their first parameter and return a typed `Future` or `Stream`. You can pass and return primitive types or any [serializable model](../06-concepts/02-models/01-models.md). The class name's `Endpoint` suffix is dropped on the client, so `RecipeEndpoint` is called through `client.recipe`. See [How it works](../04-get-started/03-how-it-works.md) for how that call reaches the server. | |
| Endpoint methods take a `Session` as their first parameter and return a typed `Future` or `Stream`. You can pass and return primitive types or any model defined in a .spy.yaml file. The class name's `Endpoint` suffix is dropped on the client, so `RecipeEndpoint` is called via `client.recipe`. See [How it works](../04-get-started/03-how-it-works.md) for how that call reaches the server. |
| # Create data models | ||
|
|
||
| Serverpod ships with a powerful data modeling system that uses easy-to-read definition files in YAML. It generates Dart classes with all the necessary code to serialize and deserialize the data and connect to the database. This allows you to define your data models for the server and the app in one place, eliminating any inconsistencies. The models give you fine-grained control over the visibility of properties and how they interact with each other. | ||
| On the [previous page](./01-creating-endpoints.md) your endpoint returned a plain string. Here you'll define a `Recipe` model so the server returns structured, typed data instead. You define the model once in YAML, and Serverpod generates the Dart class plus all the serialization that moves it between server and client. |
There was a problem hiding this comment.
| On the [previous page](./01-creating-endpoints.md) your endpoint returned a plain string. Here you'll define a `Recipe` model so the server returns structured, typed data instead. You define the model once in YAML, and Serverpod generates the Dart class plus all the serialization that moves it between server and client. | |
| On the [previous page](./01-creating-endpoints.md) your endpoint returned a plain string. Here you'll define a `Recipe` model so the server returns structured, typed data instead. You define the model once in YAML, and Serverpod generates the Dart class plus all the serialization needed between server and client. |
| Then enter some ingredients and tap **Generate Recipe**. The app calls your endpoint and displays the result: | ||
|
|
||
| Try out the app by clicking the button to get a new recipe. The app will call the endpoint on the server and display the result in the app. | ||
|  |
There was a problem hiding this comment.
The button says "Send to server", while the code and guide set it to "Generate Recipe". Update the screenshot.
| # Manage the database | ||
|
|
||
| In this section, we will build upon the models we created in the previous section and add a database to store the recipes that users create in the app. This will allow our application to persist data between sessions. | ||
| Right now your recipes disappear when the app reloads. Here you'll store them in the database so they persist, and list previously generated recipes in the app. Serverpod maps your model to a table and gives you a typed API to read and write rows, without writing any SQL. |
There was a problem hiding this comment.
| Right now your recipes disappear when the app reloads. Here you'll store them in the database so they persist, and list previously generated recipes in the app. Serverpod maps your model to a table and gives you a typed API to read and write rows, without writing any SQL. | |
| Right now your recipes disappear when the Flutter app reloads. Here you'll store them in the database so they persist, and list previously generated recipes in the app. Serverpod maps your model to a table and gives you a typed API to read and write rows, without writing any SQL. |
Reworks the "Build your first app" tutorial (the magic recipe app) for Serverpod 3.5, per #545.
What changed across the four pages:
docker compose+dart bin/main.dart+ manualserverpod generatewithserverpod start(watch mode, hot reload, embedded Postgres). The app starts once on page 1 and hot reloads through the rest.screens/layout and pre-wiredclient) instead of the stale code snippets.serverpod startTUI (the M and A keys).titleanddescriptionfrontmatter, plus "Before you start" and continuation intros, per the tutorial template.Note: the upstream magic_recipe example repo that feeds the SNIPSTART snippets is stale for beta.9. This PR inlines correct code; the example repo still needs a separate update.