Merge branch 'main' into feat-improve-code-fence

This commit is contained in:
Vincent (Wen Yu) Ge
2024-04-08 19:24:46 -04:00
142 changed files with 6598 additions and 501 deletions

View File

@@ -228,3 +228,14 @@ Get started with Appwrite and Nuxt
Get started with Appwrite and SvelteKit
{% /cards_item %}
{% /cards %}
#### Accordions
{% accordion %}
{% accordion_item title="Team ID" %}
{% /accordion_item %}
{% accordion_item title="Bundle ID" %}
{% /accordion_item %}
{% /accordion %}

View File

@@ -15,14 +15,18 @@
const links: Record<string, { label: string; href: string; target?: string; rel?: string }[]> =
{
'Quick starts': [
{ label: 'Flutter', href: '/docs/quick-starts/flutter' },
{ label: 'Web', href: '/docs/quick-starts/web' },
{ label: 'Next.js', href: '/docs/quick-starts/nextjs' },
{ label: 'React', href: '/docs/quick-starts/react' },
{ label: 'Vue.js', href: '/docs/quick-starts/vue' },
{ label: 'Nuxt', href: '/docs/quick-starts/nuxt' },
{ label: 'SvelteKit', href: '/docs/quick-starts/sveltekit' },
{ label: 'Refine', href: '/docs/quick-starts/refine' },
{ label: 'Angular', href: '/docs/quick-starts/angular' },
{ label: 'React Native', href: '/docs/quick-starts/react-native' },
{ label: 'Flutter', href: '/docs/quick-starts/flutter' },
{ label: 'Apple', href: '/docs/quick-starts/apple' },
{ label: 'Android', href: '/docs/quick-starts/android' },
{ label: 'Nuxt', href: '/docs/quick-starts/nuxt' },
{ label: 'Angular', href: '/docs/quick-starts/angular' },
{ label: 'Qwik', href: '/docs/quick-starts/qwik' },
{ label: 'Astro', href: '/docs/quick-starts/astro' }
],

View File

@@ -31,16 +31,16 @@ export enum Platform {
ClientAndroidJava = 'client-android-java',
ClientGraphql = 'client-graphql',
ClientRest = 'client-rest',
ServerNodeJs = 'server-nodejs',
ServerPython = 'server-python',
ServerDart = 'server-dart',
ServerDeno = 'server-deno',
ServerDotNet = 'server-dotnet',
ServerNodeJs = 'server-nodejs',
ServerPhp = 'server-php',
ServerPython = 'server-python',
ServerRuby = 'server-ruby',
ServerSwift = 'server-swift',
ServerKotlin = 'server-kotlin',
ServerJava = 'server-java',
ServerDotNet = 'server-dotnet',
ServerGraphql = 'server-graphql',
ServerRest = 'server-rest'
}

View File

@@ -1,5 +1,12 @@
<div style:margin-block="1.5rem">
<div>
<ul class="web-grid-row-2">
<slot />
</ul>
</div>
<style>
div {
margin-block-start: 1rem;
margin-block-end: 2rem;
}
</style>

View File

@@ -0,0 +1,23 @@
<script lang="ts">
import { setContext } from 'svelte';
export let href: string;
export let light = '';
export let dark = '';
export let title: string;
setContext('no-paragraph', true);
</script>
<li>
<a {href} class="web-card is-normal" style:margin-block-end="0">
<img src={dark} alt="" class="u-only-dark" width="32" height="32" />
<img src={light} alt="" class="u-only-light" width="32" height="32" />
<h4 class="web-sub-body-500 web-u-color-text-primary u-margin-block-start-8">
{title}
</h4>
<p class="web-sub-body-400 u-margin-block-start-4">
<slot />
</p>
</a>
</li>

View File

@@ -8,6 +8,7 @@
export { default as Icon_Image } from './Icon_Image.svelte';
export { default as Cards } from './Cards.svelte';
export { default as Cards_Item } from './Cards_Item.svelte';
export { default as Cards_Image_Item } from './Cards_Image_Item.svelte';
export { default as Only_Light } from './Only_Light.svelte';
export { default as Only_Dark } from './Only_Dark.svelte';
export { default as Video } from './Video.svelte';

View File

@@ -1,32 +1,144 @@
## Authentication events {% #authentication-events %}
| Name | Payload | Description |
|----------------------------------------|------------------------------------|-----------------------------------------------------|
| `teams.*` | [Team Object](/docs/references/cloud/models/team) | This event triggers on any teams event. |
| `teams.*.create` | [Team Object](/docs/references/cloud/models/team) | This event triggers when a team is created. |
| `teams.*.delete` | [Team Object](/docs/references/cloud/models/team) | This event triggers when a team is deleted. |
| `teams.*.memberships.*` | [Membership Object](/docs/references/cloud/models/membership) | This event triggers on any team memberships event. |
| `teams.*.memberships.*.create` | [Membership Object](/docs/references/cloud/models/membership) | This event triggers when a membership is created. |
| `teams.*.memberships.*.delete` | [Membership Object](/docs/references/cloud/models/membership) | This event triggers when a membership is deleted. |
| `teams.*.memberships.*.update` | [Membership Object](/docs/references/cloud/models/membership) | This event triggers when a membership is updated. |
| `teams.*.memberships.*.update.status` | [Membership Object](/docs/references/cloud/models/membership) | This event triggers when a team memberships status is updated. |
| `teams.*.update` | [Team Object](/docs/references/cloud/models/team) | This event triggers when a team is updated. |
| `teams.*.update.prefs` | [Team Object](/docs/references/cloud/models/team) | This event triggers when a team's preferences are updated. |
| `users.*` | [User Object](/docs/references/cloud/models/user) | This event triggers on any user's event. |
| `users.*.create` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user is created. |
| `users.*.delete` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user is deleted. |
| `users.*.recovery.*` | [Token Object](/docs/references/cloud/models/token) | This event triggers on any user's recovery token event. |
| `users.*.recovery.*.create` | [Token Object](/docs/references/cloud/models/token) | This event triggers when a recovery token for a user is created. |
| `users.*.recovery.*.update` | [Token Object](/docs/references/cloud/models/token) | This event triggers when a recovery token for a user is validated. |
| `users.*.sessions.*` | [Session Object](/docs/references/cloud/models/session) | This event triggers on any user's sessions event. |
| `users.*.sessions.*.create` | [Session Object](/docs/references/cloud/models/session) | This event triggers when a session for a user is created. |
| `users.*.sessions.*.delete` | [Session Object](/docs/references/cloud/models/session) | This event triggers when a session for a user is deleted. |
| `users.*.update` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user is updated. |
| `users.*.update.email` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user's email address is updated. |
| `users.*.update.name` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user's name is updated. |
| `users.*.update.password` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user's password is updated. |
| `users.*.update.prefs` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user's preferences is updated. |
| `users.*.update.status` | [User Object](/docs/references/cloud/models/user) | This event triggers when a user's status is updated. |
| `users.*.verification.*` | [Token Object](/docs/references/cloud/models/token) | This event triggers on any user's verification token event. |
| `users.*.verification.*.create` | [Token Object](/docs/references/cloud/models/token) | This event triggers when a verification token for a user is created. |
| `users.*.verification.*.update` | [Token Object](/docs/references/cloud/models/token) | This event triggers when a verification token for a user is validated. |
{% table %}
* Name
* Description
---
* `teams.*`
*
This event triggers on any teams event.
Returns [Team Object](/docs/references/cloud/models/team)
---
* `teams.*.create`
*
This event triggers when a team is created.
Returns [Team Object](/docs/references/cloud/models/team)
---
* `teams.*.delete`
*
This event triggers when a team is deleted.
Returns [Team Object](/docs/references/cloud/models/team)
---
* `teams.*.memberships.*`
*
This event triggers on any team memberships event.
Returns [Membership Object](/docs/references/cloud/models/membership)
---
* `teams.*.memberships.*.create`
*
This event triggers when a membership is created.
Returns [Membership Object](/docs/references/cloud/models/membership)
---
* `teams.*.memberships.*.delete`
*
This event triggers when a membership is deleted.
Returns [Membership Object](/docs/references/cloud/models/membership)
---
* `teams.*.memberships.*.update`
*
This event triggers when a membership is updated.
Returns [Membership Object](/docs/references/cloud/models/membership)
---
* `teams.*.memberships.*.update.status`
*
This event triggers when a team memberships status is updated.
Returns [Membership Object](/docs/references/cloud/models/membership)
---
* `teams.*.update`
*
This event triggers when a team is updated.
Returns [Team Object](/docs/references/cloud/models/team)
---
* `teams.*.update.prefs`
*
This event triggers when a team's preferences are updated.
Returns [Team Object](/docs/references/cloud/models/team)
---
* `users.*`
*
This event triggers on any user's event.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.create`
*
This event triggers when a user is created.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.delete`
*
This event triggers when a user is deleted.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.recovery.*`
*
This event triggers on any user's recovery token event.
Returns [Token Object](/docs/references/cloud/models/token)
---
* `users.*.recovery.*.create`
*
This event triggers when a recovery token for a user is created.
Returns [Token Object](/docs/references/cloud/models/token)
---
* `users.*.recovery.*.update`
*
This event triggers when a recovery token for a user is validated.
Returns [Token Object](/docs/references/cloud/models/token)
---
* `users.*.sessions.*`
*
This event triggers on any user's sessions event.
Returns [Session Object](/docs/references/cloud/models/session)
---
* `users.*.sessions.*.create`
*
This event triggers when a session for a user is created.
Returns [Session Object](/docs/references/cloud/models/session)
---
* `users.*.sessions.*.delete`
*
This event triggers when a session for a user is deleted.
Returns [Session Object](/docs/references/cloud/models/session)
---
* `users.*.update`
*
This event triggers when a user is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.update.email`
*
This event triggers when a user's email address is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.update.name`
*
This event triggers when a user's name is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.update.password`
*
This event triggers when a user's password is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.update.prefs`
*
This event triggers when a user's preferences is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.update.status`
*
This event triggers when a user's status is updated.
Returns [User Object](/docs/references/cloud/models/user)
---
* `users.*.verification.*`
*
This event triggers on any user's verification token event.
Returns [Token Object](/docs/references/cloud/models/token)
---
* `users.*.verification.*.create`
*
This event triggers when a verification token for a user is created.
Returns [Token Object](/docs/references/cloud/models/token)
---
* `users.*.verification.*.update`
*
This event triggers when a verification token for a user is validated.
Returns [Token Object](/docs/references/cloud/models/token)
{% /table %}

View File

@@ -1,22 +1,93 @@
## Databases events {% #databases-events %}
| Name | Payload | Description |
| ----------------------------------------- | ------------------------------ | --------------------------------------- |
| `databases.*` | [Database Object](/docs/references/cloud/models/database) | This event triggers on any database event. |
| `databases.*.collections.*` | [Collection Object](/docs/references/cloud/models/collection) | This event triggers on any collection event. |
| `databases.*.collections.*.attributes.*` | [Attribute Object](/docs/references/cloud/models/attributeList) | This event triggers on any attributes event. |
| `databases.*.collections.*.attributes.*.create` | [Attribute Object](/docs/references/cloud/models/attributeList) | This event triggers when an attribute is created. |
| `databases.*.collections.*.attributes.*.delete` | [Attribute Object](/docs/references/cloud/models/attributeList) | This event triggers when an attribute is deleted. |
| `databases.*.collections.*.create` | [Collection Object](/docs/references/cloud/models/collection) | This event triggers when a collection is created. |
| `databases.*.collections.*.delete` | [Collection Object](/docs/references/cloud/models/collection) | This event triggers when a collection is deleted. |
| `databases.*.collections.*.documents.*` | [Document Object](/docs/references/cloud/models/document) | This event triggers on any documents event. |
| `databases.*.collections.*.documents.*.create` | [Document Object](/docs/references/cloud/models/document) | This event triggers when a document is created. |
| `databases.*.collections.*.documents.*.delete` | [Document Object](/docs/references/cloud/models/document) | This event triggers when a document is deleted. |
| `databases.*.collections.*.documents.*.update` | [Document Object](/docs/references/cloud/models/document) | This event triggers when a document is updated. |
| `databases.*.collections.*.indexes.*` | [Index Object](/docs/references/cloud/models/index) | This event triggers on any indexes event. |
| `databases.*.collections.*.indexes.*.create` | [Index Object](/docs/references/cloud/models/index) | This event triggers when an index is created. |
| `databases.*.collections.*.indexes.*.delete` | [Index Object](/docs/references/cloud/models/index) | This event triggers when an index is deleted. |
| `databases.*.collections.*.update` | [Collection Object](/docs/references/cloud/models/collection) | This event triggers when a collection is updated. |
| `databases.*.create` | [Database Object](/docs/references/cloud/models/database) | This event triggers when a database is created. |
| `databases.*.delete` | [Database Object](/docs/references/cloud/models/database) | This event triggers when a database is deleted. |
| `databases.*.update` | [Database Object](/docs/references/cloud/models/database) | This event triggers when a database is updated. |
{% table %}
* Name
* Description
---
* `databases.*`
*
This event triggers on any database event.
Returns [Database Object](/docs/references/cloud/models/database)
---
* `databases.*.collections.*`
*
This event triggers on any collection event.
Returns [Collection Object](/docs/references/cloud/models/collection)
---
* `databases.*.collections.*.attributes.*`
*
This event triggers on any attributes event.
Returns [Attribute Object](/docs/references/cloud/models/attributeList)
---
* `databases.*.collections.*.attributes.*.create`
*
This event triggers when an attribute is created.
Returns [Attribute Object](/docs/references/cloud/models/attributeList)
---
* `databases.*.collections.*.attributes.*.delete`
*
This event triggers when an attribute is deleted.
Returns [Attribute Object](/docs/references/cloud/models/attributeList)
---
* `databases.*.collections.*.create`
*
This event triggers when a collection is created.
Returns [Collection Object](/docs/references/cloud/models/collection)
---
* `databases.*.collections.*.delete`
*
This event triggers when a collection is deleted.
Returns [Collection Object](/docs/references/cloud/models/collection)
---
* `databases.*.collections.*.documents.*`
*
This event triggers on any documents event.
Returns [Document Object](/docs/references/cloud/models/document)
---
* `databases.*.collections.*.documents.*.create`
*
This event triggers when a document is created.
Returns [Document Object](/docs/references/cloud/models/document)
---
* `databases.*.collections.*.documents.*.delete`
*
This event triggers when a document is deleted.
Returns [Document Object](/docs/references/cloud/models/document)
---
* `databases.*.collections.*.documents.*.update`
*
This event triggers when a document is updated.
Returns [Document Object](/docs/references/cloud/models/document)
---
* `databases.*.collections.*.indexes.*`
*
This event triggers on any indexes event.
Returns [Index Object](/docs/references/cloud/models/index)
---
* `databases.*.collections.*.indexes.*.create`
*
This event triggers when an index is created.
Returns [Index Object](/docs/references/cloud/models/index)
---
* `databases.*.collections.*.indexes.*.delete`
*
This event triggers when an index is deleted.
Returns [Index Object](/docs/references/cloud/models/index)
---
* `databases.*.collections.*.update`
*
This event triggers when a collection is updated.
Returns [Collection Object](/docs/references/cloud/models/collection)
---
* `databases.*.create`
*
This event triggers when a database is created.
Returns [Database Object](/docs/references/cloud/models/database)
---
* `databases.*.delete`
*
This event triggers when a database is deleted.
Returns [Database Object](/docs/references/cloud/models/database)
---
* `databases.*.update`
*
This event triggers when a database is updated.
Returns [Database Object](/docs/references/cloud/models/database){% /table %}

View File

@@ -1,16 +1,64 @@
## Functions events {% #functions-events %}
| Name | Payload | Description |
| ------------------------------------------- | --------------------------------- | ----------------------------------------- |
| `functions.*` | [Function Object](/docs/references/cloud/models/function) | This event triggers on any functions event. |
| `functions.*.create` | [Function Object](/docs/references/cloud/models/function) | This event triggers when a function is created. |
| `functions.*.delete` | [Function Object](/docs/references/cloud/models/function) | This event triggers when a function is deleted. |
| `functions.*.deployments.*` | [Deployment Object](/docs/references/cloud/models/deployment) | This event triggers on any deployments event. |
| `functions.*.deployments.*.create` | [Deployment Object](/docs/references/cloud/models/deployment) | This event triggers when a deployment is created. |
| `functions.*.deployments.*.delete` | [Deployment Object](/docs/references/cloud/models/deployment) | This event triggers when a deployment is deleted. |
| `functions.*.deployments.*.update` | [Deployment Object](/docs/references/cloud/models/deployment) | This event triggers when a deployment is updated. |
| `functions.*.executions.*` | [Execution Object](/docs/references/cloud/models/execution) | This event triggers on any executions event. |
| `functions.*.executions.*.create` | [Execution Object](/docs/references/cloud/models/execution) | This event triggers when an execution is created. |
| `functions.*.executions.*.delete` | [Execution Object](/docs/references/cloud/models/execution) | This event triggers when an execution is deleted. |
| `functions.*.executions.*.update` | [Execution Object](/docs/references/cloud/models/execution) | This event triggers when an execution is updated. |
| `functions.*.update` | [Function Object](/docs/references/cloud/models/function) | This event triggers when a function is updated. |
{% table %}
* Name
* Description
---
* `functions.*`
*
This event triggers on any functions event.
Returns [Function Object](/docs/references/cloud/models/function)
---
* `functions.*.create`
*
This event triggers when a function is created.
Returns [Function Object](/docs/references/cloud/models/function)
---
* `functions.*.delete`
*
This event triggers when a function is deleted.
Returns [Function Object](/docs/references/cloud/models/function)
---
* `functions.*.deployments.*`
*
This event triggers on any deployments event.
Returns [Deployment Object](/docs/references/cloud/models/deployment)
---
* `functions.*.deployments.*.create`
*
This event triggers when a deployment is created.
Returns [Deployment Object](/docs/references/cloud/models/deployment)
---
* `functions.*.deployments.*.delete`
*
This event triggers when a deployment is deleted.
Returns [Deployment Object](/docs/references/cloud/models/deployment)
---
* `functions.*.deployments.*.update`
*
This event triggers when a deployment is updated.
Returns [Deployment Object](/docs/references/cloud/models/deployment)
---
* `functions.*.executions.*`
*
This event triggers on any executions event.
Returns [Execution Object](/docs/references/cloud/models/execution)
---
* `functions.*.executions.*.create`
*
This event triggers when an execution is created.
Returns [Execution Object](/docs/references/cloud/models/execution)
---
* `functions.*.executions.*.delete`
*
This event triggers when an execution is deleted.
Returns [Execution Object](/docs/references/cloud/models/execution)
---
* `functions.*.executions.*.update`
*
This event triggers when an execution is updated.
Returns [Execution Object](/docs/references/cloud/models/execution)
---
* `functions.*.update`
*
This event triggers when a function is updated.
Returns [Function Object](/docs/references/cloud/models/function)
{% /table %}

View File

@@ -1,18 +1,74 @@
## Messaging events {% #messaging-events %}
| Name | Payload | Description |
| ------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------ |
| `providers.*` | [Provider Object](/docs/references/1.5.x/models/provider) | This event triggers on any providers event. |
| `providers.*.create` | [Provider Object](/docs/references/1.5.x/models/provider) | This event triggers when a provider is created. |
| `providers.*.delete` | [Provider Object](/docs/references/1.5.x/models/provider) | This event triggers when a provider is deleted. |
| `providers.*.update` | [Provider Object](/docs/references/1.5.x/models/provider) | This event triggers when a provider is updated. |
| `topics.*` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers on any topic event. |
| `topics.*.create` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers when a topic is created. |
| `topics.*.delete` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers when a topic is deleted. |
| `topics.*.update` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers when a topic is updated. |
| `topics.*.subscribers.*.create` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers when a subscriber to a topic is created. |
| `topics.*.subscribers.*.delete` | [Topic Object](/docs/references/1.5.x/models/topic) | This event triggers when a subscriber to a topic is deleted. |
| `messages.*` | [Message Object](/docs/references/1.5.x/models/message) | This event triggers on any message event. |
| `messages.*.create` | [Message Object](/docs/references/1.5.x/models/message) | This event triggers when a message is created. |
| `messages.*.delete` | [Message Object](/docs/references/1.5.x/models/message) | This event triggers when a message is deleted. |
| `messages.*.update` | [Message Object](/docs/references/1.5.x/models/message) | This event triggers when a message is updated. |
{% table %}
* Name
* Description
---
* `providers.*`
*
This event triggers on any providers event.
Returns [Provider Object](/docs/references/1.5.x/models/provider)
---
* `providers.*.create`
*
This event triggers when a provider is created.
Returns [Provider Object](/docs/references/1.5.x/models/provider)
---
* `providers.*.delete`
*
This event triggers when a provider is deleted.
Returns [Provider Object](/docs/references/1.5.x/models/provider)
---
* `providers.*.update`
*
This event triggers when a provider is updated.
Returns [Provider Object](/docs/references/1.5.x/models/provider)
---
* `topics.*`
*
This event triggers on any topic event.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `topics.*.create`
*
This event triggers when a topic is created.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `topics.*.delete`
*
This event triggers when a topic is deleted.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `topics.*.update`
*
This event triggers when a topic is updated.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `topics.*.subscribers.*.create`
*
This event triggers when a subscriber to a topic is created.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `topics.*.subscribers.*.delete`
*
This event triggers when a subscriber to a topic is deleted.
Returns [Topic Object](/docs/references/1.5.x/models/topic)
---
* `messages.*`
*
This event triggers on any message event.
Returns [Message Object](/docs/references/1.5.x/models/message)
---
* `messages.*.create`
*
This event triggers when a message is created.
Returns [Message Object](/docs/references/1.5.x/models/message)
---
* `messages.*.delete`
*
This event triggers when a message is deleted.
Returns [Message Object](/docs/references/1.5.x/models/message)
---
* `messages.*.update`
*
This event triggers when a message is updated.
Returns [Message Object](/docs/references/1.5.x/models/message)
{% /table %}

View File

@@ -1,12 +1,44 @@
## Storage events {% ##storage-events %}
| Name | Payload | Description |
|---------------------------------|-------------------------------------------------|----------------------------------------------|
| `buckets.*` | [Bucket Object](/docs/references/cloud/models/bucket) | This event triggers on any buckets event. |
| `buckets.*.create` | [Bucket Object](/docs/references/cloud/models/bucket) | This event triggers when a bucket is created.|
| `buckets.*.delete` | [Bucket Object](/docs/references/cloud/models/bucket) | This event triggers when a bucket is deleted.|
| `buckets.*.files.*` | [File Object](/docs/references/cloud/models/file) | This event triggers on any files event. |
| `buckets.*.files.*.create` | [File Object](/docs/references/cloud/models/file) | This event triggers when a file is created. |
| `buckets.*.files.*.delete` | [File Object](/docs/references/cloud/models/file) | This event triggers when a file is deleted. |
| `buckets.*.files.*.update` | [File Object](/docs/references/cloud/models/file) | This event triggers when a file is updated. |
| `buckets.*.update` | [Bucket Object](/docs/references/cloud/models/bucket) | This event triggers when a bucket is updated.|
{% table %}
* Name
* Description
---
* `buckets.*`
*
This event triggers on any buckets event.
Returns [Bucket Object](/docs/references/cloud/models/bucket)
---
* `buckets.*.create`
*
This event triggers when a bucket is created.
Returns [Bucket Object](/docs/references/cloud/models/bucket)
---
* `buckets.*.delete`
*
This event triggers when a bucket is deleted.
Returns [Bucket Object](/docs/references/cloud/models/bucket)
---
* `buckets.*.files.*`
*
This event triggers on any files event.
Returns [File Object](/docs/references/cloud/models/file)
---
* `buckets.*.files.*.create`
*
This event triggers when a file is created.
Returns [File Object](/docs/references/cloud/models/file)
---
* `buckets.*.files.*.delete`
*
This event triggers when a file is deleted.
Returns [File Object](/docs/references/cloud/models/file)
---
* `buckets.*.files.*.update`
*
This event triggers when a file is updated.
Returns [File Object](/docs/references/cloud/models/file)
---
* `buckets.*.update`
*
This event triggers when a bucket is updated.
Returns [Bucket Object](/docs/references/cloud/models/bucket)
{% /table %}

View File

@@ -151,17 +151,29 @@
"link": "/docs/authentication-security",
"redirect": "/docs/products/auth/security"
},
{
"link": "/docs/products/functions/development",
"redirect": "/docs/products/functions/develop"
},
{
"link": "/docs/products/functions/deployment",
"redirect": "/docs/products/functions/deploy-from-git"
},
{
"link": "/docs/products/functions/execution",
"redirect": "/docs/products/functions/execute"
},
{
"link": "/docs/functions-develop",
"redirect": "/docs/products/functions/development"
"redirect": "/docs/products/functions/develop"
},
{
"link": "/docs/functions-deploy",
"redirect": "/docs/products/functions/deployment"
"redirect": "/docs/products/functions/deploy-from-git"
},
{
"link": "/docs/functions-execute",
"redirect": "/docs/products/functions/execution"
"redirect": "/docs/products/functions/execute"
},
{
"link": "/docs/functions-runtimes",
@@ -169,7 +181,7 @@
},
{
"link": "/docs/functions-examples",
"redirect": "/docs/products/functions/examples"
"redirect": "/docs/products/functions"
},
{
"link": "/docs/migrations",

View File

@@ -180,7 +180,7 @@
{@const author = data.authors.find(
(author) => author.slug === post.author
)}
{#if author}
{#if author && !post.draft}
<Article
title={post.title}
href={post.href}

View File

@@ -27,7 +27,8 @@ export function load() {
timeToRead: frontmatter.timeToRead,
author: frontmatter.author,
category: frontmatter.category,
href: `${base}/blog/post/${postName}`
href: `${base}/blog/post/${postName}`,
draft: frontmatter.draft
};
})
.sort((a, b) => {

View File

@@ -0,0 +1,11 @@
---
layout: author
slug: bradley-schofield
name: Bradley Schofield
role: Software Engineer
bio: Integrating platforms and managing data in Appwrite.
avatar: /images/avatars/bradley.png
twitter: https://x.com/ionicisere
github: https://github.com/PineappleIOnic
linkedin: https://www.linkedin.com/in/bradley-schofield-7512b7188/
---

View File

@@ -16,6 +16,7 @@ export type AuthorData = {
href: string;
};
export type PostsData = {
draft: boolean;
title: string;
description: string;
date: Date;

View File

@@ -7,6 +7,7 @@ cover: /images/blog/a-recap-of-init/the-recap.png
timeToRead: 12
author: laura-du-ry
category: OSS
featured: true
---
Init has come to an end, and were happy how all of you showed up and made it an amazing week filled with product announcements, events, content, celebrations, a release, and most of all, community fun!

View File

@@ -44,7 +44,7 @@ Visit the **Domains** tab on the function page and copy the domain URL to test
Go to the function URL in your web browser, and you'll see a short url like the one shown below.
# Next Steps
# Next steps
URL shorteners often offer additional features such as tracking click-through rates, providing analytics on link usage, and allowing users to customize the shortened URL to some extent.This Function Template can be extended to perform a lot of functionalities. Some examples are:

View File

@@ -7,7 +7,7 @@ cover: /images/blog/init-announcement.png
timeToRead: 4
author: eldad-fux
category: open-source
featured: true
featured: false
---
We are very excited to announce Init.

View File

@@ -55,7 +55,7 @@ The next step is extend the template to show real invoice data. Heres a high-
3. If necessary, use the order document to ensure the user has permission to see the order. For example, compare the `x-appwrite-user-id` header with the document user ID.
4. Change the function to populate the PDF template with the real order document.
# Next Steps
# Next steps
Weve covered the basics, and now its your time to shine! With a few changes, you can extend this template to fit your application. Be sure to check out the other available Function Templates. Weve created many that could be of use in your projects. You can find the [templates GitHub repository here](https://github.com/appwrite/templates).

View File

@@ -0,0 +1,203 @@
---
layout: post
title: Improving user security in your web apps with email OTP auth
description: Understand how email OTP authentication works and how you can implement it in a SvelteKit application.
date: 2024-04-05
cover: /images/blog/email-otp-auth-sveltekit/cover.png
timeToRead: 6
author: aditya-oberai
category: authentication
---
To discover a balance between security and user convenience, one growing trend we have seen recently is the implementation of passwordless authentication. Today, both small and large companies are transitioning to using passwordless authentication methods over traditional password-based ones, such as Expensify (whose transition was also covered in a Forbes [article](https://www.forbes.com/sites/quickerbettertech/2023/05/29/on-technology-expensify-forces-passwordless-on-its-users-and-good-for-them/?sh=397a7b017cac) in 2023). Appwrite has always maintained support for both types of authentication, featuring phone-based OTPs (one-time passwords) and magic URLs in our list of authentication methods.
With the recent Appwrite 1.5 release, we added a new passwordless authentication method: email OTPs. In this blog, youll learn how email OTP authentication works and how you can implement it in a SvelteKit application.
# What is email OTP authentication?
[Email OTP authentication](https://appwrite.io/docs/products/auth/email-otp) lets users create accounts using their email address and sign in using a 6-digit code delivered to their email inbox. This method is similar to [Magic URL login](https://appwrite.io/docs/products/auth/magic-url) but can provide a better user experience in some scenarios.
## Email OTP vs magic URL
Email OTP authentication sends an email with a 6-digit code that a user needs to enter into the app, while magic URL authentication delivers a clickable button or link to the user's inbox. Both allow passwordless login flows with different advantages.
| Benefits of email OTP | Downsides of email OTP |
| --- | --- |
| Doesn't require the user to be signed into their email inbox on the device | Expires quicker |
| Doesn't disturb the application flow with a redirect | Requires more inputs from the user |
| Doesn't require deep linking on mobile apps | |
# Implementing email OTP in a SvelteKit app
Now that we have a basic understanding of email OTP authentication, lets learn how we can implement it in a SvelteKit application.
## Set up Appwrite 1.5
Email OTP authentication is not yet released on Cloud, which is on `Appwrite 1.4.x`. This is a feature available in `Appwrite 1.5.x` and will be available on Appwrite Cloud later. In the meantime, you can use it by [self-hosting Appwrite](https://appwrite.io/docs/advanced/self-hosting) on your system or an external VM/VPS.
You can self-host Appwrite using the following Docker command:
```bash
docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.4
```
Once that is done, [set up email delivery](https://appwrite.io/docs/advanced/self-hosting/email) on your self-hosted Appwrite instance. This can be done by visiting your `appwrite` directory and updating the `.env` file in a similar manner as follows:
```
_APP_SMTP_HOST=smtp.sendgrid.net
_APP_SMTP_PORT=587
_APP_SMTP_SECURE=tls
_APP_SMTP_USERNAME=YOUR-SMTP-USERNAME
_APP_SMTP_PASSWORD=YOUR-SMTP-PASSWORD
_APP_SYSTEM_EMAIL_ADDRESS=YOUR-SENDER-EMAIL
```
Dont forget to run `docker compose up -d` after you update the environment variables to ensure the changes are implemented in your instance.
Once your Appwrite instance is set up, create a project and copy both the Appwrite API endpoint and project ID.
## Create your SvelteKit web app
In order to create your SvelteKit application, open your terminal and enter the command `npm create svelte@latest` to create a project.
```bash
npm create svelte@latest
create-svelte version 6.0.10
┌ Welcome to SvelteKit!
◇ Where should we create your project?
│ appwrite-email-otp
◇ Which Svelte app template?
│ Skeleton project
◇ Add type checking with TypeScript?
│ No
◇ Select additional options (use arrow keys/space bar)
│ Add ESLint for code linting, Add Prettier for code formatting
└ Your project is ready!
```
Once that is done, enter the directory and install any existing dependencies.
```bash
cd appwrite-email-otp
npm i
```
Also, create a file `.env` in the root directory and add your Appwrite API endpoint and project ID.
```
PUBLIC_APPWRITE_ENDPOINT=
PUBLIC_APPWRITE_PROJECT_ID=
```
## Install the Appwrite Web SDK and prepare the app logic
Once your project is ready, install the Appwrite Web SDK using NPM:
```bash
npm i appwrite
```
As soon as thats done, we can start preparing our application logic. For that, visit the `src/lib` directory and create a file `appwrite.js`. Add the following code to the same.
```js
import { PUBLIC_APPWRITE_ENDPOINT, PUBLIC_APPWRITE_PROJECT_ID } from "$env/static/public";
import { Client, Account } from "appwrite";
const client = new Client()
.setEndpoint(PUBLIC_APPWRITE_ENDPOINT)
.setProject(PUBLIC_APPWRITE_PROJECT_ID);
export const account = new Account(client);
```
In the same directory, create a file `user.js` and add the following code.
```js
import { account } from "./appwrite";
import { ID } from "appwrite";
export const user = {
createOtp: async (email) => {
return await account.createEmailToken(ID.unique(), email, true);
},
verifyOtp: async (userId, secret) => {
return await account.createSession(userId, secret);
}
}
```
## Update the application UI
Once the authentication logic is ready, we can create the web apps user interface. To prioritize ease of understanding and simplicity, the example will not feature any styling-related code.
In the `src/routes` directory, visit the file `+page.svelte` and replace it with the following code.
```html
<script>
import { user } from "$lib/user.js";
var token;
var securityPhrase = "Create OTP first to get the security phrase.";
async function createOtp(e) {
e.preventDefault();
const formData = new FormData(e.target);
token = await user.createOtp(formData.get('email'));
securityPhrase = `Security phrase: ${token.phrase}`;
alert("OTP sent to email");
}
async function verifyOtp(e) {
e.preventDefault();
const formData = new FormData(e.target);
const response = await user.verifyOtp(token.userId, formData.get('otp'));
alert("OTP verified");
}
</script>
<section>
<div class="container">
<div id="email">
<h2>Enter Email</h2>
<form on:submit={createOtp}>
<input type="email" name="email" id="email" placeholder="team@appwrite.io" required>
<button class="button" type="submit">Submit</button>
</form>
</div>
<div id="otp">
<h2>Enter OTP</h2>
<form on:submit={verifyOtp}>
<input type="text" name="otp" id="otp" placeholder="012345" required>
<button class="button" type="submit">Submit</button>
</form>
<p>{securityPhrase}</p>
</div>
</div>
</section>
```
This UI features both steps of the email OTP flow, entering the email to send an OTP followed by entering the OTP to login. It also features the security phrase sent on the email along with the OTP so that the user can verify that a third party didnt initiate the authentication flow.
Once this is done, you can test the application by running the command `npm run dev` and opening the app URL in your browser.
# Next steps
And with that, our demo application is ready. If you liked this project or want to investigate the full codebase, visit the [GitHub repository](https://github.com/adityaoberai/appwrite-email-otp-demo).
For more information about Appwrite Authentication, visit the following resources:
- [Appwrite Authentication Docs](https://appwrite.io/docs/products/auth): These docs provide more information on how to use the different methods offered under Appwrite Authentication.
- [Appwrite Discord](https://discord.com/invite/appwrite): Connect with other developers and the Appwrite team for discussion, questions, and collaboration.

View File

@@ -0,0 +1,48 @@
---
layout: post
title: Ensuring security amidst the XZ Utils backdoor concern
description: Learn about Appwrite's response to XZ Utils backdoor concerns and how to safeguard your Appwrite projects.
date: 2024-04-02
cover: /images/blog/ensuring-security-amidst-xz-concern/cover.png
timeToRead: 7
author: jake-barnby
category: engineering
featured: false
---
In the light of recent unsettling revelations regarding a backdoor discovered in the widely-used XZ Utils,
a compression tool used in Linux environments, including Red Hat and Debian systems, the cyber-security landscape has been abuzz with concern.
This discovery had a large potential impact on encrypted SSH connections, a backbone of secure communications in the tech world.
At Appwrite, ensuring the security and trust of our developers and users is paramount.
We understand the concerns that arise from such vulnerabilities and their potential implications.
It's crucial for the Appwrite community to know that Appwrite's services **remain unaffected** by the XZ Utils backdoor.
This issue affected beta and test versions of Red Hat and Debian distributions, which Appwrite **does not use**.
# What does this mean for self-hosting Appwrite?
For our valued users who prefer the self-hosted route, leveraging Appwrite on affected operating systems (OS),
we understand your concerns. Here are our recommendations to ensure your self-hosted Appwrite instances remain secure:
- Immediate Update/Removal: The first and foremost step is to check if you have the affect versions (`5.6.0`,` 5.6.1`) of the XZ Utils installed. If so, downgrade to a safe version or remove the utility altogether.
- Enhanced Monitoring: Keep a keen eye on network traffic and system logs for any unusual activity.
- Employ Firewalls: Employ stringent firewall rules to limit inbound and outbound connections to the bare minimum required for your operations. This reduces the attack surface significantly.
- Regular System Audits: Conduct thorough audits of your systems to ensure no unauthorized modifications have been made to the OS or installed utilities.
- Stay Informed: Follow updates from your OS's security advisory to apply security patches as soon as they are released.
# Does this affect Appwrite Cloud developers?
Appwrite Cloud users can rest assured that our cloud infrastructure is secure and unaffected by the XZ Utils backdoor.
The Appwrite team has taken necessary measures to ensure that containers in our cloud environment do not have the affected versions of the XZ Utils installed.
We also took further steps to restrict SSH access to our cloud infrastructure to reduce attack surfaces further.
No actions are required from Appwrite Cloud developers at this time.
In a world where cyber threats are evolving at an alarming pace,
the Appwrite team is committed to ensuring the security and reliability of Appwrite Cloud's infrastructure,
so you can build applications with peace of mind.
The team will continue to monitor the situation closely, take necessary actions to mitigate any potential risks,
and communicate any updates transparently to the community.
For any further questions or concerns, please reach out through [email](https://appwrite.io/contact-us) or on [Discord](https://appwrite.io/discord).
We're here to support you every step of the way.

View File

@@ -0,0 +1,106 @@
---
layout: post
title: Chat with your favorite fictional character using OpenAI and Appwrite Functions
description: Learn how you can use Appwrite Functions and OpenAI to chat with popular characters such as Batman.
date: 2024-04-04
cover: /images/blog/function-chat-fictional-character/cover.png
timeToRead: 6
author: aditya-oberai
category: functions
---
Have you ever wondered what it would feel like to interact with your favorite fictional characters, such as Superman, Hermione Granger, Gandalf, or Snow White? As a part of an internal hackathon at Appwrite recently, my team developed an Appwrite Function that you can use to chat with any popular fictional character you like (we really wanted to talk to Batman!)
In this blog, lets learn how you can build this Appwrite Function using OpenAIs GPT-4 API.
![Prototype of Bruce Wayne chat](/images/blog/function-chat-fictional-character/prototype.png)
# Setting up the OpenAI platform
To develop this project, you first need an OpenAI API Key, for which you must create an account on the [OpenAI platform](https://platform.openai.com/). Once your account is set up, visit their [API keys](https://platform.openai.com/account/api-keys) page and create an API Key. Ensure you copy and save this key in a safe place, as the OpenAI platform will not let you view the key after it is created.
![OpenAI API Keys](/images/blog/function-chat-fictional-character/openai.png)
> Note: To use the GPT-4 API, your account must be upgraded to the Usage tier 1. To learn more, visit their [Usage tiers documentation](https://platform.openai.com/docs/guides/rate-limits/usage-tiers?context=tier-one).
# Initializing the Appwrite Function
Now that we have our OpenAI API Key, let us get the function ready on [Appwrite](https://cloud.appwrite.io/). Head over to your Appwrite project and visit the Functions page. From there, we will use the Node.js starter template and create a function.
![Appwrite Functions](/images/blog/function-chat-fictional-character/functions.png)
Once the function is ready, we must visit the Settings tab on the Function page and add the following environment variables:
- `OPENAI_API_KEY`: API Key from our OpenAI account
- `OPENAI_MAX_TOKENS`: Maximum number of tokens that the OpenAI response should contain (well set this as `512`)
Once that is done, visit the functions GitHub repository and clone the project.
# Developing the function logic
To develop the function, we must first install the `openai` npm package. Open your terminal in the project directory and run the following command:
```bash
npm i openai
```
Once that is done, visit the `src/main.js` file and replace the entire code with the following:
```js
import { OpenAI } from 'openai';
export default async ({ req, res, log, error }) => {
try {
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
var prompt = `You are ${req.body.character}.\nRespond to the following question in first-person: ${req.body.question}\n${req.body.additionalPrompt}`
const response = await openai.chat.completions.create({
model: 'gpt-4',
max_tokens: parseInt(process.env.OPENAI_MAX_TOKENS ?? '512'),
messages: [{ role: 'user', content: prompt }],
});
const completion = response.choices[0].message?.content;
log(completion);
return res.json({ ok: true, answer: completion }, 200);
} catch (err) {
error(err.message);
return res.json({ ok: false, error: err.message }, 500);
}
};
```
This function will accept the name of a character, the question from a user, and any additional prompt you might optionally like to give. For example, in our hackathon project, we wanted to interact with Bruce Wayne and ensure that his Batman alter-ego was not directly given away, so heres what our inputs looked like:
| Character name | Question | Additional prompt |
| --- | --- | --- |
| Bruce Wayne | Are you Batman? | Ensure that you don't reveal your Batman alter-ego but you can tip-toe around it. |
# Testing the function
Once youve completed all the aforementioned steps, you can push the code to the generated GitHub repository, at which point Appwrite Cloud will automatically deploy the changes to your function.
You can test the function by sending it a cURL request from your terminal or any other API testing client.
```bash
curl --location '<YOUR_FUNCTION_URL>' \
--header 'Content-Type: application/json' \
--data '{
"character": "Bruce Wayne",
"question": "Are you Batman?",
"additionalPrompt": "Ensure that you don'\''t reveal your Batman alter-ego but you can tip-toe around it."
}'
```
![Thunder Client on VS Code](/images/blog/function-chat-fictional-character/http.png)
# Next steps
And with that, our fictional character chat function is ready! If you liked this project and/or want to investigate the function code, visit the [GitHub repository](https://github.com/adityaoberai/CharacterChat).
For more information about Appwrite Functions, visit the following resources:
- [Appwrite Function Docs](https://appwrite.io/docs/functions): These documents provide more information on how to use Appwrite Functions.
- [Appwrite Discord](https://discord.com/invite/appwrite): Connect with other developers and the Appwrite team for discussion, questions, and collaboration.

View File

@@ -60,7 +60,7 @@ Heres a screenshot of a test prompt that was sent to the function using Postm
![Postman](/images/blog/function-template-prompt-chatgpt/postman.png)
## Next Steps
## Next steps
Weve covered the basics, and now its your time to shine! With a few changes, you should be able to extend this template to fit your app. Be sure to check out the other available Function Templates. Weve created many that could be of use in your projects. You can find the [templates GitHub repository here](https://github.com/appwrite/templates).

View File

@@ -51,7 +51,7 @@ Open the WhatsApp app on your phone, join the Vonage WhatsApp Channel (via the s
![WhatsApp](/images/blog/function-template-whatsapp-vonage/whatsapp.png)
## Next Steps
## Next steps
Weve covered the basics, and now its your time to shine! With a few changes, you should be able to extend this template to fit your app. Be sure to check out the other available Function Templates. Weve created many that could be of use in your projects. You can find the [templates GitHub repository here](https://github.com/appwrite/templates).

View File

@@ -0,0 +1,55 @@
---
layout: post
title: Introducing Appwrite's React Native SDK in open beta
description: A new SDK that allows for more mobile develoeprs to benefit from Appwrite's powerfull tools.
date: 2024-04-09
cover: /images/blog/introducing-appwrite-react-native-sdk/react-native.png
timeToRead: 4
author: vincent-ge
category: product
---
If you're a mobile developer who doesn't (want to) use Flutter, we have great news for you. Appwrite now has a React Native SDK in open beta. This will allow more mobile developers to benefit from Appwrite, giving everything you need to build your mobile applications backend, without the hassle of building it yourself.
# A brief history of React Native
[React Native](https://reactnative.dev/) has been a game-changer since its inception. Launched by Facebook in 2015, it emerged from the need to unify the development stacks for iOS and Android platforms, enabling a single codebase for both. Its importance to the developer community is monumental, offering rapid development, native capabilities, and using one code base for both iOS and Android platforms. With over 2,000 contributors on [GitHub](https://github.com/facebook/react-native) and used by thousands of apps worldwide, React Native has significantly contributed to the mobile development landscape by making app development more accessible, efficient, and scalable.
# Appwrite and React Native
The addition of the SDK to Appwrite is great news for React Native mobile developers. Appwrite's commitment to providing a secure, scalable backend, combined with React Native's efficiency and native capabilities, creates a strong toolkit for building mobile applications. This integration means developers can now enjoy the best of both worlds: Appwrite's [ready-to-use APIs](https://appwrite.io/docs/references) for Authentication, Database, Storage, Messaging, Realtime, and more, alongside React Native's seamless developer experience and native feel.
Appwrite already has a Web SDK, so why a dedicated React Native SDK? React Native requires abstractions of device and system APIs to access device permissions, cameras, storage, gyroscope, and more. Appwrite leverages [Expo](https://docs.expo.dev/) to implement APIs such as Appwrite Storage that require access to device and system APIs.
Why is this good news for the [React Native community](https://www.reddit.com/r/reactnative/)? It simplifies the development process significantly. With Appwrite's React Native SDK, setting up backend services becomes as straightforward as developing the front end. This means less time worrying about backend complexities and more time crafting exceptional user experiences.
As the SDK is released in open beta we will be working together with the React Native community to improve the SDK. We invite all developers to use the SDK and share feedback to help us make it more stable in the next few months.
# Building mobile apps
What can you build with Appwrite and React Native? Basically, anything you can think of from social media platforms and e-commerce apps to productivity tools and interactive games. Appwrite even supports push notifications for your mobile app with [Messaging](https://appwrite.io/docs/products/messaging/send-push-notifications).
We've created a playground on [GitHub](https://github.com/appwrite/playground-for-react-native) where we will add simple ideas for getting started with Appwrite and React Native. You can also find inspiration from other Appwrite projects on [builtwithappwrite.io](http://builtwithappwrite.io) to see how other mobile developers have used Appwrite as their backend.
If you are used to working with [Firebase and React Native](https://stackshare.io/react-native-firebase/alternatives), but have been looking for an open source alternative, Appwrite is now a solid choice.
# Getting started with Appwrite and React Native
Diving into Appwrite's React Native SDK is easy. Here's how to get started:
1. **Set up Appwrite**: Begin by setting up Appwrite on your server. It's a straightforward process, with detailed documentation available to guide you through each step.
2. **Install the React Native SDK**: Once Appwrite is up and running, install the React Native SDK in your project. This will connect your React Native app with Appwrite's suite of backend services.
3. **Explore the Documentation**: Familiarize yourself with the SDK's documentation. It's packed with tutorials, examples, and API references designed to get you up to speed in no time.
4. **Start Building**: With everything in place, you're ready to start building your app. The React Native SDK is designed to be intuitive, allowing you to implement features like authentication, database operations, and file storage with ease.
Read the [quick start](https://appwrite.io/docs/quick-starts/react-native) to get started or find a [tutorial](https://appwrite.io/docs/tutorials/react-native/step-1) to build an ideas tracker with React Native.
# Resources
Visit our documentation to learn more about our SDKs, join us on Discord to be part of the discussion, view our blog and YouTube channel, or visit our GitHub repository to see our source code.
- [Docs](https://appwrite.io/docs/sdks)
- [Discord](https://appwrite.io/discord)
- [Blog](https://appwrite.io/blog)
- [YouTube](https://www.youtube.com/channel/UCtBJ1v69gm8NgbCju_03Fiw)
- [GitHub](https://github.com/appwrite/appwrite)

View File

@@ -87,7 +87,7 @@ Functions are "self-contained" modules of code that accomplish a specific task.
*Differences:*
- Appwrite offers easy and quick deployment of [functions using Git](https://appwrite.io/docs/products/functions/deployment#git), has ready-to-use templates, and supports a large number of runtimes.
- Appwrite offers easy and quick deployment of [functions using Git](https://appwrite.io/docs/products/functions/deploy-from-git), has ready-to-use templates, and supports a large number of runtimes.
- Appwrite supports [function runtimes across over 10 different languages](https://appwrite.io/docs/products/functions/runtimes), including Dart, Bun, Kotlin, and Swift, whereas Firebase only supports JavaScript, TypeScript, and Python.
- Firebase offers additional event triggers for different Google Cloud services.

View File

@@ -0,0 +1,99 @@
---
layout: post
title: State of computer vision
description: Get up to speed on the latest trends in computer vision and how they are shaping the future of the industry.
date: 2024-03-25
cover: /images/blog/state-of-computer-vision/cover.png
timeToRead: 15
author: bradley-schofield
category: product
featured: false
draft: true
---
# Introduction
Computer vision is a field of artificial intelligence where we work towards giving machines a comprehensive understanding of visual data from a variety of sources, a few examples are: Images, Videos, Point Clouds and X-Rays and MRI's from medical devices with the goal of being able to further parse and process this information for downstream tasks
From rotating and scaling your images to applying your Snapchat and Instagram filters, the applications of computer vision are many fold! Computer vision technology has revolutionized many industries with it being used in the medical industry to diagnose the onset of cancer, tumors and other life threatening diseases, to the agriculture industry that uses the technology to identify weeds in their vast amount of crops, while the manufacturing sector uses it to identify faults in products before they ever come close to a customer's hands. All these new and exciting uses for the technology has resulted in the young computer vision industry to be predicted to be worth over $50bn by 2030[1].
The purpose of this blog post is to give you a good understanding of the fundamentals of computer vision, the most popular tasks and applications of computer vision and how we can leverage Appwrite to build computer vision enabled applications.
# A (not so) brief history of computer vision
The bedrock of which most computer vision systems are built upon was discovered from the least likely of places: the humble cat. In 1959, after a series of failures when neurophysiologists David Hubel and Torsten Wiesel were trying to map out how the brain processed images, they found completely by accident a neuron in the cat's brain lit up right when they switched slides. With this revelation they came to the conclusion that the cat and by extension the human brain processes lines and edges before moving into more complex processing.[2]
At the same time the very first image scanner was being developed by Russell Kirsch and his team with the first image being captures of his then three month old son. The two building blocks were now in place, we had a starting point for how to teach machines to understand what they are seeing and a sensor that allowed for them to see for the first time.
Jumping a bit forward, in 1966, a group within MIT's famous Project MAC (Project for Mathematics and Computing) led by Seymour Papert set out to solve the computer vision problem in a couple months[3] during a summer camp. Needless to say they were extremely optimistic and unfortunately the project was a failure, however it was still important as it is widely regarded as the birth of the field of computer vision in academic study.
Following the summer camp various groups kept on working in the field of computer vision and in 1974 we saw the widespread introduction of OCR (Optical Character Recognition) technology into our lives which allowed machines to read actual text for the first time.
1982 came and saw British neuroscientist David Marr propose that vision is hierarchical and that its main objective is to translate what we saw into a 3D representation inside our heads so we can better understand and interact with our environments[4].
Just as David Marr was creating his groundbreaking thesis, around 5000 miles away Japanese computer scientist Kunihiko Fukushima had proposed a neural network he called Neocognitron[5] , this network is considered the grandfather of all convolutional neural networks (CNNs) and it should be noted that Kunihiko Kukushima also created the ReLU (rectified linear unit) activation function in 1969 which didn't become widely used until 2011 when it was found to be better at training deep neural networks and even today ReLU is the most popular activation function.
In 1989 Yann LeCun while working on a system to recognise hand-written ZIP Codes found that creating the algorithms that allowed his neural network to detect them was laborious and time consuming. Instead he introduced a technique called “back propagation”, what this allowed the network to do was after it had calculated the result it would try and figure out how off its prediction was the intended result. It would then propagate that error backwards across the weights to figure out how much each weight contributed to the failure, after it had achieved that it would adjust the weights and try again. This removed the need for a human to try and figure out the optimal weights and instead allowed the machine to learn from its own mistakes.[6]
Yann LeCun would continue to experiment with this approach and later he would introduce LeNet-5. A revolutionary CNN built with 7 levels, it was designed to recognise handwritten numbers on bank cheques and did so with an astonishing 99.05% accuracy[7] but was limited to 32x32 pixel images as to process larger amounts of data required more and larger layers within the CNN which was extremely computationally intensive for the time.
After LeCun's innovation and a brief renaissance, the use of CNNs in the field of computer vision stagnated. As I had mentioned before, the computational power to run increasingly more complex CNNs just didn't exist. The CV industry as a whole shifted its focus away from them and onto object detection during this time more great innovations were made
In 2001, the first real-time facial recognition algorithm was developed by Paul Viola and Michael Jones. As the field continued to evolve so did the data they were working with, the way that data was annotated and tagged was standardized during this time and in 2010 the ImageNet dataset was released, containing over a million images all manually cleaned and tagged by humans giving all researchers an excellent dataset to train their algorithms on. With the introduction of ImageNet also came the introduction of the ImageNet Large Scale Visual Recognition Competition (ILSVRC) which pitted algorithms against each other to see which one had the highest accuracy, for two years the error rate stayed around 26%.
Enter, AlexNet. Created in 2012 by researcher Alex Krizhevsky and aided by Ilya Sutskever and Geoffrey Hinton was an industry changing evolution in the computer vision field. Earlier we had discussed that CNN's were too computationally expensive back when LeCun created LeNet, Well these researchers instead of using CPUs used the power of GPUs in order to train the neural networks. This wasn't the first time it was done mind you but it was the first time it had been done with such high accuracy hitting a huge decrease in error rates of 15.3%
After AlexNet's groundbreaking unveiling most computer vision research moved towards CNNs being powered by GPUs and a little bit later TPUs however more conventional techniques also still exist for less powerful devices.
### Convolutions and Kernels
In the history section of this page at the very beginning I mentioned that we discovered that the visual processing system our brains have detects more basic features such as lines and edges before moving onto more complex stages. You may be wondering how we can get machines to do the same thing in an efficient manner and the answer is by using convolutions. Convolution is the fundamental operation in almost all of computer vision, so it's crucial to develop an understanding of how convolutions and kernels work.
Let's imagine we have a photograph and we want to detect all the edges in this photograph, the first thing we would do is convert the image into numbers which represent how bright each pixel is, sometimes this is referred to as the Y value of an image or the luminance.
Do note that even though I am focusing on edges in this example, kernels exist that can detect a wide range of features from diagonals, spirals
Next, let's introduce the concept of a “kernel”, these can also be referred to as “filters”; these are small grids (that can be a square or a rectangle) with numbers in it. These numbers are not random but are instead specifically picked to detect a certain feature. For example, a filter we could use to detect edges is the sobel edge detector.
![Image of a kernel](/images/blog/state-of-computer-vision/kernel.png)
Now you have both the image as luminance values and this kernel perform the following steps:
You will overlay the kernel over start of the image
Next you will times each luminance pixel with the part of the grid it corresponds to in the kernel and then add up the sum of all these numbers
Next you will slide this kernel one pixel to the side performing the same operation as the previous step. Each time you perform this operation you are creating a new pixel to create a new image
Once you've slid the kernel across the entire image and you have all these new values, you can create a new image from these values. This new image is called the “feature map” and if you use this edge kernel it will highlight all the edges in the image.
There are different kernels to compute loads of different features and they aren't always a square, sometimes they are rectangular and they can be as big or small as you want, but keep in mind that bigger kernels result in more computation.
Convolutions aren't only used for things like edge detection but are extremely common in the image processing world, for tools you've probably used loads without even thinking about it, such as blurring images, sharpening and embossing.
{% video src="/images/blog/state-of-computer-vision/convolution.mp4" /%}
# Applications of Computer Vision
Now you have a good understanding of both the history and fundamentals of computer vision, you can dive into our other guides where we provide an even deeper dive into the different types of computer vision and how to implement them in your Appwrite application.
{% cards %}
{% cards_item
href="/docs/products/ai/tutorials/image-classification"
title="Image classification" %}
Understand and label the contents of images
{% /cards_item %}
{% cards_item
href="/docs/products/ai/tutorials/object-detection"
title="Object detection" %}
Detect and label objects in images
{% /cards_item %}
{% /cards %}
# Sources
1. [https://www.statista.com/outlook/tmo/artificial-intelligence/computer-vision/worldwide](https://www.statista.com/outlook/tmo/artificial-intelligence/computer-vision/worldwide)
1. [A Brief History of Computer Vision (and CNNs) - Hackernoon](https://hackernoon.com/a-brief-history-of-computer-vision-and-convolutional-neural-networks-8fe8aacc79f)
1. [https://dspace.mit.edu/bitstream/handle/1721.1/6125/AIM-100.pdf](https://dspace.mit.edu/bitstream/handle/1721.1/6125/AIM-100.pdf)
1. [http://mechanism.ucsd.edu/teaching/f18/David_Marr_Vision_A_Computational_Investigation...](http://mechanism.ucsd.edu/teaching/f18/David_Marr_Vision_A_Computational_Investigation_into_the_Human_Representation_and_Processing_of_Visual_Information.chapter1.pdf)
1. [https://www.cs.princeton.edu/courses/archive/spr08/cos598B/Readings/Fukushima1980.pdf](https://www.cs.princeton.edu/courses/archive/spr08/cos598B/Readings/Fukushima1980.pdf)
1. [http://yann.lecun.org/exdb/publis/pdf/lecun-89e.pdf](http://yann.lecun.org/exdb/publis/pdf/lecun-89e.pdf)
1. [http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf](http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf)

View File

@@ -0,0 +1,12 @@
---
layout: post
title: State of natural language processing
description: Get up to speed on the latest trends in natural language processing and how they are shaping the future of the industry.
date: 2024-03-27
cover: /images/blog/state-of-natural-language-processing/cover.png
timeToRead: 15
author: luke-silver
category: product
featured: false
draft: true
---

View File

@@ -0,0 +1,26 @@
---
layout: changelog
title: Appwrite 1.5.4 released
date: 2024-03-25
cover: /images/changelog/2024-03-25.png
---
Appwrite 1.5.4 brings several fixes and a miscellaneous update to enhance your development experience with our platform.
Fixes:
* Updated executor version to resolve Docker conflict errors when running Appwrite Functions
* Improved webhooks to fix failed connections
* Adjusted msg91 parameters to better match their new terminology
* Fixed 401 error displayed for Appwrite Function domains when permissions are not configured
* Fixed Appwrite Functions build command by adjusting trim behavior
Misc:
* Upgraded console to version 1.5.4 for an improved user interface and added features
You can update by following the [upgrade docs]( https://appwrite.io/docs/advanced/self-hosting/update).
Note that upgrading from 1.5.x -> 1.5.4 does not require running migration. For a detailed view of changes, visit the full [release notes](https://github.com/appwrite/appwrite/releases).
{% arrow_link href="https://github.com/appwrite/appwrite/releases" %}
Visit release notes
{% /arrow_link %}

View File

@@ -0,0 +1,14 @@
---
layout: changelog
title: Introducing Appwrite's React Native SDK in open beta
date: 2024-04-09
cover: /images/changelog/2024-04-09.png
---
Were excited to announce the new React Native SDK in open beta.
This will allow more mobile developers to benefit from Appwrite, giving everything you need to build your mobile applications backend, without the hassle of building it yourself.
{% arrow_link href="/blog/post/introducing-appwrite-react-native-sdk" %}
Read the announcement to learn more
{% /arrow_link %}

View File

@@ -65,6 +65,12 @@
icon: 'icon-folder',
isParent: true
},
{
label: 'AI',
href: '/docs/products/ai',
icon: 'icon-chip',
isParent: true
}
]
},
{

View File

@@ -58,7 +58,7 @@ To begin migrating to Appwrite, follow these steps.
1. Remember to [add appropriate permissions](/docs/advanced/platform/permissions) to the migrated resources to protect user data and privacy.
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/development).
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/develop).
1. Explore Appwrite's unique features by exploring the rest of the [Appwrite Documentation](/docs).

View File

@@ -45,7 +45,7 @@ Before migrating to Appwrite make sure you've read the [migration overview](/doc
1. Remember to [add appropriate permissions](/docs/advanced/platform/permissions) to the migrated resources to protect user data and privacy.
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/development).
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/develop).
1. Explore Appwrite's unique features by exploring the rest of the [Appwrite Documentation](/docs).

View File

@@ -47,7 +47,7 @@ Before migrating to Appwrite make sure you've read the [migration overview](/doc
1. Remember to [add appropriate permissions](/docs/advanced/platform/permissions) to the migrated resources to protect user data and privacy.
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/development).
1. Migrate functions manually, by [pick a runtime](/docs/products/functions/runtimes) and [learn to develop Appwrite Functions](/docs/products/functions/develop).
1. Explore Appwrite's unique features by exploring the rest of the [Appwrite Documentation](/docs).

View File

@@ -10,18 +10,27 @@ You can subscribe to these events with Appwrite [Functions](/docs/products/funct
You can subscribe to events for specific resources using their ID or subscribe to changes of all resources of the same type by using a wildcard character * instead of an ID.
You can also filter for events of specific actions like create, update, or delete.
You can find a list of events for Storage, Databases, Functions, and Authentication services below.
{% accordion %}
{% accordion_item title="Authentication" %}
{% partial file="auth-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Databases" %}
{% partial file="databases-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Storage" %}
{% partial file="storage-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Functions" %}
{% partial file="functions-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Messaging" %}
{% partial file="messaging-events.md" /%}
{% /accordion_item %}
{% /accordion %}
# Known limitations {% #known-limitations %}

View File

@@ -43,14 +43,22 @@ Appwrite has events that fire when a resource changes.
These events cover all Appwrite resources and can reflect create, update, and delete actions.
You can specify one or many events to subscribe to with webhooks.
{% accordion %}
{% accordion_item title="Authentication events" %}
{% partial file="auth-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Databases events" %}
{% partial file="databases-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Storage events" %}
{% partial file="storage-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Functions events" %}
{% partial file="functions-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Messaging events" %}
{% partial file="messaging-events.md" /%}
{% /accordion_item %}
{% /accordion %}
[Learn more about events](/docs/advanced/platform/api-keys)

View File

@@ -10,7 +10,7 @@ encrypt more than 363 million websites.
TLS certificates are generated for all of the following.
- Appwrite products and endpoints, like Databases, Storage, Authentication, Functions, Messaging, and all other endpoints.
- [Custom domains](/docs/advanced/platform/custom-domains) that you configure for your Appwrite projects.
- [Domains for Appwrite Functions](/docs/products/functions/deployment#domains), generated or user provided.
- [Domains for Appwrite Functions](/docs/products/functions/domains), generated or user provided.
TLS certificates are crucial to ensure all connections between your apps and Appwrite Cloud are encrypted.
This protects your users from attack vectors like man-in-the-middle and eavesdropping attacks.

View File

@@ -83,7 +83,7 @@ After creating your app, you'll have to configure the following environment vari
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `_APP_DOMAIN` | Your main Appwrite domain used to access the Appwrite Console. When setting a public suffix domain, Appwrite will attempt to issue a valid SSL certificate automatically. When used with a dev domain, Appwrite will assign a self-signed SSL certificate. If you're using a proxy for **localhost development**, such as [ngrok](https://ngrok.com/), this will be the domain of your localhost proxy. |
| `_APP_DOMAIN_TARGET` | A hostname to serve as a CNAME target for your Appwrite custom domains. You can use the same value as used for the Appwrite `_APP_DOMAIN` variable. If you're using a proxy for **localhost development**, such as [ngrok](https://ngrok.com/), this will be the domain of your localhost proxy, such as `dd65-2405-201-4013-d8d7-b4c5-fb73-39f9-285c.ngrok.io`. |
| `_APP_DOMAIN_FUNCTIONS` | This will be used for system generated [Function Domains](/docs/products/functions/deployment#domains). When a function domain is generated, it will be `[UNIQUE_ID].[_APP_DOMAIN_FUNCTIONS]`. If `_APP_DOMAIN_FUNCTIONS` is set to `example.com` for example, the generated domain for functions will be something like `64d4d22db370ae41a32e.example.com`. You can use the same value as used for the Appwrite `_APP_DOMAIN` variable. |
| `_APP_DOMAIN_FUNCTIONS` | This will be used for system generated [Function Domains](/docs/products/functions/domains). When a function domain is generated, it will be `[UNIQUE_ID].[_APP_DOMAIN_FUNCTIONS]`. If `_APP_DOMAIN_FUNCTIONS` is set to `example.com` for example, the generated domain for functions will be something like `64d4d22db370ae41a32e.example.com`. You can use the same value as used for the Appwrite `_APP_DOMAIN` variable. |
| `_APP_VCS_GITHUB_APP_NAME` | Name of your GitHub app. This is the display name you'll see on GitHub and it will be visible in your GitHub app's URL. |
| `_APP_VCS_GITHUB_PRIVATE_KEY`| RSA private key from GitHub wrapped with double quotes and newlines replaced with `\n`. You can generate private keys from GitHub application settings. |
| `_APP_VCS_GITHUB_APP_ID` | GitHub application ID. You can find it in your GitHub application details. |

View File

@@ -0,0 +1,111 @@
<script lang="ts">
import Docs from '$lib/layouts/Docs.svelte';
import Sidebar, { type NavParent, type NavTree } from '$lib/layouts/Sidebar.svelte';
const parent: NavParent = {
href: '/docs',
label: 'AI'
};
const navigation: NavTree = [
{
label: 'Getting started',
items: [
{
label: 'Overview',
href: '/docs/products/ai'
}
]
},
{
label: 'Concepts',
items: [
{
label: 'Computer vision',
href: '/docs/products/ai/computer-vision'
},
{
label: 'Natural language processing',
href: '/docs/products/ai/natural-language'
},
{
label: 'Audio processing',
href: '/docs/products/ai/audio-processing'
}
]
},
{
label: 'Computer vision',
items: [
{
label: 'Image classification',
href: '/docs/products/ai/tutorials/image-classification'
},
{
label: 'Object detection',
href: '/docs/products/ai/tutorials/object-detection'
}
]
},
{
label: 'Natural language processing',
items: [
{
label: 'Text generation',
href: '/docs/products/ai/tutorials/text-generation'
},
{
label: 'Language translation',
href: '/docs/products/ai/tutorials/language-translation'
}
]
},
{
label: 'Audio processing',
items: [
{
label: 'Speech recognition',
href: '/docs/products/ai/tutorials/speech-recognition'
},
{
label: 'Text to speech',
href: '/docs/products/ai/tutorials/text-to-speech'
}
]
},
{
label: 'Integrations',
items: [
{
label: 'Perplexity',
href: '/docs/products/ai/integrations/perplexity'
},
{
label: 'Replicate',
href: '/docs/products/ai/integrations/replicate'
},
{
label: 'OpenAI',
href: '/docs/products/ai/integrations/openai'
},
{
label: 'Pinecone',
href: '/docs/products/ai/integrations/pinecone'
},
{
label: 'ElevenLabs',
href: '/docs/products/ai/integrations/elevenlabs'
},
{
label: 'LangChain',
href: '/docs/products/ai/integrations/langchain'
}
]
}
];
</script>
<Docs variant="two-side-navs">
<Sidebar {navigation} {parent} />
<slot />
</Docs>

View File

@@ -0,0 +1,56 @@
---
layout: article
title: Artifical intelligence
description: "Learn how to implement machine learning models in your applications."
---
Appwrite allows you to build powerful AI powered applications with ease. Leverage Appwrite's
powerful functions architecture and start building the future.
# Explore capabilities {% #explore-capabilities %}
Detailed explanations and deep dives into how you can implement different machine techniques in your Appwrite projects.
{% cards %}
{% cards_image_item href="/docs/products/ai/computer-vision" title="Computer vision" light="/images/docs/ai/computer-vision-light.png" dark="/images/docs/ai/computer-vision-dark.png" %}
Label and understand the contents of images
{% /cards_image_item %}
{% cards_image_item href="/docs/products/ai/natural-language" title="Natural language processing" light="/images/docs/ai/natural-language-light.png" dark="/images/docs/ai/natural-language-dark.png" %}
Understand and generate human language
{% /cards_image_item %}
{% cards_image_item href="/docs/products/ai/audio-processing" title="Audio processing" light="/images/docs/ai/audio-processing-light.png" dark="/images/docs/ai/audio-processing-dark.png" %}
Process and generate audio data
{% /cards_image_item %}
{% /cards %}
# Show me some code {% #show-me-some-code %}
If you learn best from code examples, follow one of our tutorials.
### Computer vision
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/image-classification" title="Image classification" %}
Understand and label the contents of images
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/object-detection" title="Object detection" %}
Detect and label objects in images
{% /cards_item %}
{% /cards %}
### Natural language
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/text-generation" title="Text generation" %}
Generate human-like text
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/language-translation" title="Language translation" %}
Translate text between languages
{% /cards_item %}
{% /cards %}
### Audio processing
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/speech-recognition" title="Speech recognition" %}
Process speech audio into text
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/text-to-speech" title="Text to speech" %}
Convert text into speech
{% /cards_item %}
{% /cards %}

View File

@@ -0,0 +1,20 @@
---
layout: article
title: Audio processing
description: Learn about the basics of audio processing, the most popular tasks and applications of audio processing with ML and how we can leverage Appwrite to build audio processing enabled applications.
---
Audio processing is a field of machine learning that deals with allowing machines to understand, analyze, and manipulate various audio signals.
The applications are vast and varied, from speech recognition to music generation and all the way to noise reduction. it's used in many everyday tools you use including voice assistants,
music streaming services and for noise reduction in online calls.
# Tutorials
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/speech-recognition" title="Speech recognition" %}
Recognize and transcribe spoken language into text
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/text-to-speech" title="Text to speech" %}
Convert written text into spoken language
{% /cards_item %}
{% /cards %}

View File

@@ -0,0 +1,18 @@
---
layout: article
title: Computer vision
description: Learn about the basics of computer vision, the most popular tasks and applications of computer vision and how we can leverage Appwrite to build computer vision enabled applications.
---
Computer vision is a field of AI aiming to provide machines with a comprehensive understanding of visual data from a variety of sources. Images, Videos, Point Clouds, X-Rays, and MRI's from medical devices can be processed with the goal of parsing relevant information for subsequent tasks.
# Tutorials
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/image-classification" title="Image classification" %}
Understand and label the contents of images
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/object-detection" title="Object detection" %}
Detect and label objects in images
{% /cards_item %}
{% /cards %}

View File

@@ -0,0 +1,283 @@
---
layout: article
title: Integrating ElevenLabs
description: Learn how to integrate ElevenLabs into your Appwrite project.
difficulty: intermediate
readtime: 15
---
ElevenLabs is an text to speech tool that can generate natural sounding audio from text. It's an excellent tool for dubbing content, creating audiobooks, or even for accessibility purposes.
Integrating ElevenLabs into your Appwrite project is simple. This tutorial will guide you through the process of setting up the ElevenLabs API and integrating it into your Appwrite project.
# Prerequisites {% #prerequisites %}
- An Appwrite Project
- An [ElevenLabs API Key](https://elevenlabs.io/)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `ELEVENLABS_API_KEY`, generate it [here](https://elevenlabs.io/). For thee `APPWRITE_API_KEY`, tick the box to **Generate API key on completion**.
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, navigate to the freshly created repository and clone it to your local machine.
Install the `undici` package to make requests to the ElevenLabs API and `node-appwrite` package to upload the generated audio files to Appwrite Storage.
```bash
npm install undici node-appwrite
```
{% /section %}
{% section #step-3 step=3 title="Create utility functions" %}
For this example, the function will be able to take both `GET` and `POST` requests.
For the `GET` request, return a static HTML page that will have a form to submit text to the API.
Meanwhile the `POST` request will send the text to the ElevenLabs API and return the generated audio file.
To begin with write the code to return the static HTML page, to do this create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
Write the `GET` request handler in the `src/main.js` file. This handler will return a static HTML page you'll create later.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
return res.send(getStaticFile('index.html'), 200, {
'Content-Type': 'text/html; charset=utf-8',
});
}
};
```
A check is also included to ensure that the `ELEVENLABS_API_KEY`, `APPWRITE_API_KEY` and `APPWRITE_BUCKET_ID` environment variables is set.
{% /section %}
{% section #step-5 step=5 title="Create web page" %}
Create a HTML web page that the function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
</html>
```
Within the `<html>` tag, Add a `<head>` tag that will define the style and scripts.
```html
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ElevenLabs Demo</title>
<script>
async function onSubmit(prompt) {
const response = await fetch('/', {
method: 'POST',
body: JSON.stringify({ text: prompt }),
headers: {
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.ok || json.error) {
alert(json.error);
}
return json.response;
}
</script>
<script src="//unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
```
And after the `</head>` tag add this `<body>` which will contain the actual form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div
class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
>
<h1 class="heading-level-1">ElevenLabs Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p
class="body-text-1 u-normal u-margin-block-start-8"
style="max-width: 50rem"
>
Use this page to test your implementation with ElevenLabs. Enter
text and receive an audio response.
</p>
</div>
</div>
<div
class="container u-margin-block-start-negative-56"
x-data="{ prompt: '', response: '', loading: false }"
>
<div class="card u-flex u-gap-24 u-flex-vertical">
<div class="u-flex u-cross-center u-gap-8">
<div
class="input-text-wrapper is-with-end-button u-width-full-line"
>
<input x-model="prompt" type="search" placeholder="Enter text" />
<div class="icon-search" aria-hidden="true"></div>
</div>
<button
class="button"
x-bind:disabled="loading"
x-on:click="async () => { loading = true; response = ''; try { response = await onSubmit(prompt) } catch(err) { console.error(err); } finally { loading = false; } }"
>
<span class="text">Generate</span>
</button>
</div>
<template x-if="response">
<div class="u-flex u-flex-vertical u-gap-12">
<div class="u-flex u-flex-vertical u-gap-12 card">
<div class="u-flex u-gap-12">
<h5 class="eyebrow-heading-2">Output:</h5>
</div>
<div>
<audio x-bind:src="response" controls></audio>
</div>
</div>
</div>
</template>
</div>
</div>
</main>
</body>
```
All of this together will render a form that will submit your text to the Appwrite function through a POST request which you'll create next. The Appwrite function will call ElevenLabs's API, upload the audio to Appwrite Storage and return the URL, which will be displayed on your page.
{% /section %}
{% section #step-6 step=6 title="Handle POST Request" %}
Add methods necessary to integrate with the ElevenLabs API:
Import `fetch`, and the required features from the Appwrite Node.js SDK at the top of the `main.js` file
```js
import { Client, Storage, ID, InputFile, Permission, Role } from "node-appwrite";
import { fetch } from "undici";
```
Next add code to validate the body of the request and initialize the Appwrite SDK:
```js
const client = new Client()
.setEndpoint(process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1")
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
if (!req.body.text || typeof req.body.text !== "string") {
return res.json({ ok: false, error: "Missing required field `text`" }, 400);
}
```
Send a request to the ElevenLabs API and return the response:
```js
const body = {
accent: req.body.accent || "british",
accent_strength: 1.0,
age: req.body.age || "young",
gender: req.body.gender || "female",
text: req.body.text,
};
const response = await fetch(
"https://api.elevenlabs.io/v1/voice-generation/generate-voice",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"xi-api-key": process.env.ELEVENLABS_API_KEY,
},
body: JSON.stringify(body),
},
);
if (response.status !== 200) {
return res.json({ ok: false, error: "Failed to generate audio" }, 500);
}
```
This code will send the prompt to the ElevenLabs API and return the audio as a blob,
additionally it'll also catch any errors we could encounter and reports them for easy debugging.
{% /section %}
{% section #step-7 step=7 title="Store Audio in Appwrite Storage" %}
Store the audio file in Appwrite Storage for easy retrieval later:
```js
const storage = new Storage(client);
const file = await storage.createFile(
process.env.APPWRITE_BUCKET_ID,
ID.unique(),
InputFile.fromBlob(await response.blob(), "audio.mp3"),
[Permission.read(Role.any())],
);
```
To show it to the user, parse the download URL from Appwrite and return it in the response:
```js
const url = `${process.env.APPWRITE_ENDPOINT}/storage/buckets/${process.env.APPWRITE_BUCKET_ID}/files/${file.$id}/view?project=${process.env.APPWRITE_FUNCTION_PROJECT_ID}`;
return res.json({ ok: true, response: url });
```
This should finish up the function, Deploy it to Appwrite by pushing to the git repository created earlier.
{% /section %}
{% section #step-8 step=8 title="Test the function" %}
Now that the function is deployed, test it by visiting the function URL in your browser.
This should show the UI created earlier and to test it, write a prompt and click the submit button. After a brief moment you should see the audio appear below the input.
![Testing the function](/images/docs/ai/integrations/elevenlabs/demo.png)
{% /section %}

View File

@@ -0,0 +1,338 @@
---
layout: article
title: Integrating LangChain
description: Learn how to integrate LangChain into your Appwrite project.
difficulty: intermediate
readtime: 15
---
# Prerequisites {% #prerequisites %}
- An Appwrite project
- An Appwrite collection
- An [OpenAI API key](https://platform.openai.com/account/api-keys)
- A [Pinecone API key](https://docs.pinecone.io/guides/getting-started/quickstart#2-get-your-api-key)
- A Pinecone index
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `PINECONE_API_KEY`, generate it [here](https://docs.pinecone.io/guides/getting-started/quickstart#2-get-your-api-key). Add the `OPENAI_API_KEY`, generate it [here](https://platform.openai.com/account/api-keys).For the `APPWRITE_API_KEY`, tick the box to **Generate API key on completion**.
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, navigate to the freshly created repository and clone it to your local machine.
Add the following dependencies to the `package.json` file:
```bash
npm install @pinecone-database/pinecone openai @langchain/core @langchain/openai @langchain/pinecone langchain
```
{% /section %}
{% section #step-3 step=3 title="Create utility functions" %}
For this example, the function will be able to take both `GET` and `POST` requests.
For the `GET` request, return a static HTML page that will have a form to search the Pinecone index. Meanwhile the `POST /search` requests will send the search query to the Pinecone API and return the results.
All other `POST` requests will trigger the indexing of the Appwrite collection into the Pinecone index.
Write the code to return the static HTML page, to do this create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
Write the `GET` request handler in the `src/main.js` file. This handler will return a static HTML page you'll create later.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
const html = getStaticFile('index.html');
return res.send(html, 200, { 'Content-Type': 'text/html; charset=utf-8' });
}
};
```
The function will throw an error if any of the required environment variables are missing. The function will return the static HTML page when a `GET` request is made.
{% /section %}
{% section #step-5 step=5 title="Create web page" %}
Create a HTML web page that the function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
</html>
```
Within the `<html>` tag, Add a `<head>` tag that will define the style and scripts.
```html
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pinecone Demo</title>
<script src="https://unpkg.com/meilisearch@0.34.1"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
```
And after the `</head>` tag add this `<body>` which will contain the actual form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div
class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
>
<h1 class="heading-level-1">Pinecone Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p
class="body-text-1 u-normal u-margin-block-start-8"
style="max-width: 50rem"
>
Use this demo to verify that the sync between Appwrite Databases and
Pinecone was successful. Search your Pinecone vector database using
the input below.
</p>
</div>
</div>
<div
class="container u-margin-block-start-negative-56"
x-data="{ search: '', results: [ ] }"
x-init="$watch('search', async (value) => { results = await onSearch(value) })"
>
<div class="card u-flex u-gap-24 u-flex-vertical">
<div id="searchbox">
<div
class="input-text-wrapper is-with-end-button u-width-full-line"
>
<input x-model="search" type="search" placeholder="Search" />
<div class="icon-search" aria-hidden="true"></div>
</div>
</div>
<div id="hits" class="u-flex u-flex-vertical u-gap-12">
<template x-for="result in results">
<div class="card">
<pre x-text="JSON.stringify(result, null, '\t')"></pre>
</div>
</template>
</div>
</div>
</div>
</main>
<script>
window.onSearch = async function (prompt) {
const response = await fetch('/search', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: {
'Content-Type': 'application/json',
},
});
return response.matches;
};
</script>
</body>
```
This will render a form that will submit your search query to the function and display the results.
{% /section %}
{% section #step-6 step=6 title="Setup SDKs" %}
Add methods necessary to integrate with the OpenAI and Pinecone APIs
Import `openai` and `@pinecone-database/pinecone` at the top of the `main.js` file:
```js
import { Pinecone } from '@pinecone-database/pinecone';
import { OpenAI } from 'openai';
```
Add the following code at the end of request handler in the `main.js` file:
```js
const openai = new OpenAI();
const pinecone = new Pinecone();
const pineconeIndex = pinecone.index(process.env.PINECONE_INDEX_ID);
```
The functions checks the request method, and then initializes the OpenAI and Pinecone SDKs.
{% /section %}
{% section #step-7 step=7 title="Handle prompt requests" %}
First add the following imports from LangChain:
```js
import { formatDocumentsAsString } from 'langchain/util/document';
import { ChatOpenAI } from '@langchain/openai';
import { PineconeStore } from '@langchain/pinecone';
import { PromptTemplate } from '@langchain/core/prompts';
import {
RunnableSequence,
RunnablePassthrough,
} from '@langchain/core/runnables';
import { StringOutputParser } from '@langchain/core/output_parsers';
```
To handle the prompt requests, add the following code to the end of the request handler in the `main.js` file:
```js
if (req.path === '/prompt') {
if (!req.body.prompt || typeof req.body.prompt !== 'string') {
return res.json(
{ ok: false, error: 'Missing required field `prompt`' },
400
);
}
const vectorStore = await PineconeStore.fromExistingIndex(
new OpenAIEmbeddings(),
{ pineconeIndex }
);
const prompt = PromptTemplate.fromTemplate(
`Answer the question based with following context:{context}\nQuestion: {question}`
);
const chain = RunnableSequence.from([
{
context: vectorStore.asRetriever().pipe(formatDocumentsAsString),
question: new RunnablePassthrough(),
},
prompt,
new ChatOpenAI(),
new StringOutputParser(),
]);
const result = await chain.invoke(req.body.prompt);
return res.json({ ok: true, completion: result }, 200);
}
```
This code will handle the prompt requests by creating a LangChain sequence that will format the documents as strings, prompt the user for a question, and then use the OpenAI API to generate a response. The response is then parsed and returned to the user.
{% /section %}
{% section #step-8 step=8 title="Handle index requests" %}
The Appwrite collection needs to be indexed into the Pinecone index. Create a new file at `src/appwrite.js` with the following code:
```js
import { Client, Databases, Query } from 'node-appwrite';
export default class AppwriteService {
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
}
async getAllDocuments(databaseId, collectionId) {
const cumulative = [];
let cursor = null;
do {
const queries = [Query.limit(100)];
if (cursor) {
queries.push(Query.cursorAfter(cursor));
}
const { documents } = await this.databases.listDocuments(
databaseId,
collectionId,
queries
);
if (documents.length === 0) {
break;
}
cursor = documents[documents.length - 1].$id;
cumulative.push(...documents);
} while (cursor);
return cumulative;
}
}
```
The service provides a method to iterate the documents contained within an entire collection, fetching the limit of 100 documents per request.
```js
const appwrite = new AppwriteService();
const appwriteDocuments = await appwrite.getAllDocuments(
process.env.APPWRITE_DATABASE_ID,
process.env.APPWRITE_COLLECTION_ID
);
const documents = appwriteDocuments.map(
(document) =>
new Document({
metadata: { id: document.$id },
pageContent: Object.entries(document)
.filter(([key, _]) => !key.startsWith('$'))
.map(([key, value]) => `${key}: ${value}`)
.join('\n'),
})
);
await PineconeStore.fromDocuments(documents, new OpenAIEmbeddings(), {
pineconeIndex,
maxConcurrency: 5,
});
```
Within our function handler, the service is instantiated and used to create an array of LangChain documents. LangChain documents can then be used with the `PineconeStore.fromDocuments` method to retrieve embeddings from OpenAI and upsert them to your Pinecone index.
{% /section %}
{% section #step-9 step=9 title="Test the function" %}
Now that the function is deployed, test it by visiting the function URL in your browser.
This should show the UI created earlier and to test it, write a prompt and click the submit button. After a brief moment you should see the matched results.
{% /section %}

View File

@@ -0,0 +1,243 @@
---
layout: article
title: Integrating OpenAI
description: Learn how to integrate OpenAI into your Appwrite project.
difficulty: intermediate
readtime: 15
---
The OpenAI API is a powerful tool that can be used to generate text, images, and more. This tutorial will guide you through the process of setting up the OpenAI API and integrating it into your Appwrite project.
We'll create a simple function that takes a text prompt and generates a completion using OpenAI's GPT-3 model. Then, using Appwrite functions we'll create a user interface that allows users to input text and see the generated completion.
# Prerequisites {% #prerequisites %}
- An Appwrite Project
- An [OpenAI API Key](https://platform.openai.com/account/api-keys)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `OPENAI_API_KEY`, generate it [here](https://platform.openai.com/account/api-keys).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add OpenAI SDK" %}
Once the function is created, navigate to the freshly created repository and clone it to your local machine.
Install the `openai` package to simplify the process of interacting with the OpenAI API.
```bash
npm install openai
```
{% /section %}
{% section #step-3 step=3 title="Create utility function" %}
For this example, the function will be able to take both `GET` and `POST` requests.
For the `GET` request, return a static HTML page that will have a form to submit text to the API.
Meanwhile the `POST` request will send the text to the OpenAI API and return the generated text.
Write the code to return the static HTML page, to do this create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
Write the `GET` request handler in the `src/main.js` file. This handler will return a static HTML page you'll create later.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
return res.send(getStaticFile('index.html'), 200, {
'Content-Type': 'text/html; charset=utf-8',
});
}
};
```
If the method is `GET`, it returns the static HTML page.
{% /section %}
{% section #step-5 step=5 title="Create web page" %}
Create a HTML web page that the function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenAI Demo</title>
<script>
async function onSubmit(prompt) {
const response = await fetch('/', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: {
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.ok || json.error) {
alert(json.error);
}
return json.completion;
}
</script>
<script src="//unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
</html>
```
The code above includes a script that will handle the form submission and a script tag that includes the Alpine.js library. This library will be used to handle the form submission.
After the `</head>` tag add a `<body>` containing the visible form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div
class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
>
<h1 class="heading-level-1">Prompt ChatGPT Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p
class="body-text-1 u-normal u-margin-block-start-8"
style="max-width: 50rem"
>
Use this page to test your implementation with OpenAI ChatGPT. Enter
text and receive the model output as a response.
</p>
</div>
</div>
<div
class="container u-margin-block-start-negative-56"
x-data="{ prompt: '', answer: '', loading: false }"
>
<div class="card u-flex u-gap-24 u-flex-vertical">
<div class="u-flex u-cross-center u-gap-8">
<div
class="input-text-wrapper is-with-end-button u-width-full-line"
>
<input x-model="prompt" type="search" placeholder="Question" />
<div class="icon-search" aria-hidden="true"></div>
</div>
<button
class="button"
x-bind:disabled="loading"
x-on:click="async () => { loading = true; answer = ''; try { answer = await onSubmit(prompt) } catch(err) { console.error(err); } finally { loading = false; } }"
>
<span class="text">Submit</span>
</button>
</div>
<template x-if="answer">
<div class="u-flex u-flex-vertical u-gap-12">
<div class="u-flex u-flex-vertical u-gap-12 card">
<div class="u-flex u-gap-12">
<h5 class="eyebrow-heading-2">ChatGPT:</h5>
</div>
<div style="overflow-x: hidden; line-break: anywhere">
<p class="u-color-text-gray" x-text="answer"></p>
</div>
</div>
</div>
</template>
</div>
</div>
</main>
</body>
```
The form will allows users to submit your text to the Appwrite function through a POST request. The Appwrite function will call the OpenAI API, and return the response to the user.
{% /section %}
{% section #step-6 step=6 title="Handle POST request" %}
Add methods necessary to integrate with the OpenAI API.
Import `openai` at the top of the `main.js` file.
```js
import { OpenAIApi, Configuration } from 'openai';
```
Add code to validate the body of the request and initialize the Appwrite SDK:
```js
const client = new Client()
.setEndpoint(process.env.APPWRITE_ENDPOINT ?? "https://cloud.appwrite.io/v1")
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
if (!req.body.prompt && typeof req.body.prompt !== "string") {
return res.json({ ok: false, error: "Missing required field `prompt`" }, 400);
}
const openai = new OpenAIApi(
new Configuration({
apiKey: process.env.OPENAI_API_KEY,
})
);
```
Make a request to the OpenAI API and return the response:
```js
try {
const response = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
max_tokens: parseInt(process.env.OPENAI_MAX_TOKENS ?? '512'),
messages: [{ role: 'user', content: req.body.prompt }],
});
const completion = response.data.choices[0].message?.content;
return res.json({ ok: true, completion }, 200);
} catch (err) {
return res.json({ ok: false, error: 'Failed to query model.' }, 500);
}
```
{% /section %}
{% section #step-8 step=8 title="Test the function" %}
Now that the function is deployed, test it by visiting the function URL in your browser.
This should show the UI created earlier and to test it, write a prompt and click the submit button. After a brief moment you should see the generated text from the OpenAI API.
{% /section %}

View File

@@ -0,0 +1,253 @@
---
layout: article
title: Integrating Perplexity
description: Learn how to integrate the Perplexity API into your Appwrite project.
difficulty: intermediate
readtime: 15
---
Integrating Perplexity into your Appwrite project is simple. This tutorial will guide you through the process of setting up the Perplexity API and integrating it into your Appwrite project.
# Prerequisites {% #prerequisites %}
- An Appwrite Project
- A [Perplexity API Key](https://docs.perplexity.ai/docs/getting-started)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `PERPLEXITY_API_KEY`, generate it [here](https://docs.perplexity.ai/docs/getting-started).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add OpenAI SDK" %}
Once the function is created, clone the function and open it in your development environment.
The Perplexity API is compatible with the OpenAI SDK, so we can use the OpenAI SDK to interact with Perplexity.
Once you have the repository open, install the OpenAI SDK by running the following command in your terminal:
```bash
npm install openai
```
Perplexity's API is OpenAI compatible, so we can use the OpenAI SDK to interact with Perplexity.
{% /section %}
{% section #step-3 step=3 title="Create utility function" %}
For our example, our function will be able to take both `GET` and `POST` requests.
The function will return a web page on `GET` requests and return a response from Perplexity on `POST` requests.
To begin with we will write the code to return the static HTML page.
Create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
We're going to write our `GET` request handler in the `src/main.js` file. This handler will return a static HTML page we'll create later.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
return res.send(getStaticFile('index.html'), 200, {
'Content-Type': 'text/html; charset=utf-8',
});
}
};
```
{% /section %}
{% section #step-5 step=5 title="Create static page" %}
Create the static HTML page that our function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
</html>
```
Within the `<html>` tag, we're going to add a `<head>` tag that will define our style and scripts.
```html
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Perplexity AI Demo</title>
<script>
async function onSubmit(prompt) {
const response = await fetch('/', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: {
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.ok || json.error) {
alert(json.error);
}
return json.completion;
}
</script>
<script src="//unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
```
And after the `</head>` tag we're going to add our `<body>` which will contain the actual form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div
class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
>
<h1 class="heading-level-1">Perplexity AI Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p
class="body-text-1 u-normal u-margin-block-start-8"
style="max-width: 50rem"
>
Use this page to test your implementation with Perplexity AI. Enter
text and receive the model output as a response.
</p>
</div>
</div>
<div
class="container u-margin-block-start-negative-56"
x-data="{ prompt: '', answer: '', loading: false }"
>
<div class="card u-flex u-gap-24 u-flex-vertical">
<div class="u-flex u-cross-center u-gap-8">
<div
class="input-text-wrapper is-with-end-button u-width-full-line"
>
<input x-model="prompt" type="search" placeholder="Question" />
<div class="icon-search" aria-hidden="true"></div>
</div>
<button
class="button"
x-bind:disabled="loading"
x-on:click="async () => { loading = true; answer = ''; try { answer = await onSubmit(prompt) } catch(err) { console.error(err); } finally { loading = false; } }"
>
<span class="text">Submit</span>
</button>
</div>
<template x-if="answer">
<div class="u-flex u-flex-vertical u-gap-12">
<div class="u-flex u-flex-vertical u-gap-12 card">
<div class="u-flex u-gap-12">
<h5 class="eyebrow-heading-2">Perplexity AI:</h5>
</div>
<div style="overflow-x: hidden; line-break: anywhere">
<p class="u-color-text-gray" x-text="answer"></p>
</div>
</div>
</div>
</template>
</div>
</div>
</main>
</body>
```
All of this together will render a form that will submit your question to the Appwrite Function through a POST request which we'll create next. The Appwrite Function will call Perplexity's API and return the response, which will be displayed on your page.
{% /section %}
{% section #step-6 step=6 title="Handle POST Request" %}
Now that we're serving a basic HTML page, we can add methods necessary to integrate with Perplexity's API.
Import the OpenAI SDK at the top of our `main.js` file:
```js
import { OpenAI } from 'openai';
```
Next, add code to validate the body of the request and initialize the OpenAI SDK with the Perplexity API key:
```js
if (!req.body.prompt) {
return res.json({
ok: false,
error: 'Missing required fields: prompt'
}, 400);
}
const perplexity = new OpenAI({
apiKey: process.env.PERPLEXITY_API_KEY,
baseURL: 'https://api.perplexity.ai',
});
```
This code also allows us to modify what model we use by setting the `PERPLEXITY_MODEL` environment variable.
Send the request to the Perplexity API and return the response:
```js
try {
const response = await perplexity.chat.completions.create({
model: 'mistral-7b-instruct',
max_tokens: parseInt(process.env.PERPLEXITY_MAX_TOKENS ?? '512'),
messages: [{ role: 'user', content: req.body.prompt }],
stream: false,
});
const completion = response.choices[0].message?.content;
return res.json({ ok: true, completion }, 200);
} catch (err) {
return res.json({ ok: false, error: 'Failed to query model.' }, 500);
}
```
This code will send our prompt to the perplexity chat completions API and return the response to the user,
additionally it'll also catch any errors we could encounter and reports them for easy debugging.
With our function now complete, you can deploy it to Appwrite by simply pushing the change to your repository.
{% /section %}
{% section #step-7 step=7 title="Test our function" %}
Now that our function is deployed, we can test it by visiting the function URL in our browser.
Write a prompt and click the submit button, after a brief moment you should see the completion appear below the input.
![Testing the function](/images/docs/ai/integrations/perplexity/demo.png)
{% /section %}

View File

@@ -0,0 +1,306 @@
---
layout: article
title: Integrating Pinecone
description: Learn how to integrate Pinecone into your Appwrite project.
difficulty: intermediate
readtime: 15
---
Pinecone is a vector database that allows you to store and query high-dimensional vectors. It is a great tool for building recommendation systems, search engines, and more. In this tutorial, we'll show you how to integrate Pinecone into your Appwrite project.
Inside an Appwrite Function, we'll create a method to that indexes an Appwrite collection into Pinecone. We'll also create a method to query the Pinecone index and return the results.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- An Appwrite collection
- An [OpenAI API key](https://platform.openai.com/account/api-keys)
- A [Pinecone API key](https://docs.pinecone.io/guides/getting-started/quickstart#2-get-your-api-key)
- A Pinecone index
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `PINECONE_API_KEY`, generate it [here](https://docs.pinecone.io/guides/getting-started/quickstart#2-get-your-api-key). Add the `OPENAI_API_KEY`, generate it [here](https://platform.openai.com/account/api-keys).For the `APPWRITE_API_KEY`, tick the box to **Generate API key on completion**.
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, navigate to the freshly created repository and clone it to your local machine.
Install the `@pinecone-database/pinecone` package to simplify the process of interacting with the Pinecone API. We'll also install the `openai` package to interact with the OpenAI API.
```bash
npm install @pinecone-database/pinecone openai
```
{% /section %}
{% section #step-3 step=3 title="Create utility function" %}
For this example, the function will be able to take both `GET` and `POST` requests.
Create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
Write the `GET` request handler in the `src/main.js` file.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
const html = getStaticFile('index.html');
return res.send(html, 200, { 'Content-Type': 'text/html; charset=utf-8' });
}
};
```
The code checks if all required environment variables are present and then returns the static HTML page when a `GET` request is made.
{% /section %}
{% section #step-5 step=5 title="Create web page" %}
Create a HTML web page that the function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
</html>
```
Within the `<html>` tag, Add a `<head>` tag that will define the style and scripts.
```html
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pinecone Demo</title>
<script src="https://unpkg.com/meilisearch@0.34.1"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
```
And after the `</head>` tag add this `<body>` which will contain the actual form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div
class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
>
<h1 class="heading-level-1">Pinecone Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p
class="body-text-1 u-normal u-margin-block-start-8"
style="max-width: 50rem"
>
Use this demo to verify that the sync between Appwrite Databases and
Pinecone was successful. Search your Pinecone vector database using
the input below.
</p>
</div>
</div>
<div
class="container u-margin-block-start-negative-56"
x-data="{ search: '', results: [ ] }"
x-init="$watch('search', async (value) => { results = await onSearch(value) })"
>
<div class="card u-flex u-gap-24 u-flex-vertical">
<div id="searchbox">
<div
class="input-text-wrapper is-with-end-button u-width-full-line"
>
<input x-model="search" type="search" placeholder="Search" />
<div class="icon-search" aria-hidden="true"></div>
</div>
</div>
<div id="hits" class="u-flex u-flex-vertical u-gap-12">
<template x-for="result in results">
<div class="card">
<pre x-text="JSON.stringify(result, null, '\t')"></pre>
</div>
</template>
</div>
</div>
</div>
</main>
<script>
window.onSearch = async function (prompt) {
const response = await fetch('/search', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: {
'Content-Type': 'application/json',
},
});
return response.matches;
};
</script>
</body>
```
This will render a form that will submit your search query to the function and display the results.
{% /section %}
{% section #step-6 step=6 title="Setup SDKs" %}
Add methods necessary to integrate with the OpenAI and Pinecone APIs
Import `openai` and `@pinecone-database/pinecone` at the top of the `main.js` file:
```js
import { Pinecone } from '@pinecone-database/pinecone';
import { OpenAI } from 'openai';
```
Add the following code at the end of request handler in the `main.js` file:
```js
const openai = new OpenAI();
const pinecone = new Pinecone();
const pineconeIndex = pinecone.index(process.env.PINECONE_INDEX_ID);
```
The functions checks the request method, and then initializes the OpenAI and Pinecone SDKs.
{% /section %}
{% section #step-7 step=7 title="Handle search requests" %}
To handle the search requests, add the following code to the end of the request handler in the `main.js` file:
```js
if (req.path === '/search') {
const queryEmbedding = await openai.embeddings.create({
model: 'text-embedding-ada-002',
input: req.body.prompt,
});
const searchResults = await pineconeIndex.query({
vector: queryEmbedding.data[0].embedding,
topK: 5,
});
return res.json(searchResults);
}
```
For all requests with the path `/search`, the function sends the search query to the OpenAI API to get the embedding. The function then queries the Pinecone index with the embedding and returns the results.
{% /section %}
{% section #step-8 step=8 title="Handle indexing requests" %}
The Appwrite collection needs to be indexed into the Pinecone index. Create a new file at `src/appwrite.js` with the following code:
```js
import { Client, Databases, Query } from 'node-appwrite';
export default class AppwriteService {
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
}
async getAllDocuments(databaseId, collectionId) {
const cumulative = [];
let cursor = null;
do {
const queries = [Query.limit(100)];
if (cursor) {
queries.push(Query.cursorAfter(cursor));
}
const { documents } = await this.databases.listDocuments(
databaseId,
collectionId,
queries
);
if (documents.length === 0) {
break;
}
cursor = documents[documents.length - 1].$id;
cumulative.push(...documents);
} while (cursor);
return cumulative;
}
}
```
The service provides a method to iterate the documents contained within an entire collection, fetching the limit of 100 documents per request.
```js
const appwrite = new AppwriteService();
const documents = await appwrite.getAllDocuments(
process.env.APPWRITE_DATABASE_ID,
process.env.APPWRITE_COLLECTION_ID
);
const embeddings = await Promise.all(
documents.map(async (document) => {
const record = await openai.embeddings.create({
model: 'text-embedding-ada-002',
input: JSON.stringify(document),
});
return {
id: document.$id,
values: record.data[0].embedding,
metadata: document,
};
})
);
await pineconeIndex.upsert(embeddings);
```
The code fetches all documents from the Appwrite collection, then sends each document to the OpenAI API to get the embedding. The embeddings are then uploaded to the Pinecone index.
{% /section %}
{% section #step-9 step=9 title="Test the function" %}
Now that the function is deployed, test it by visiting the function URL in your browser.
This should show the UI created earlier and to test it, write a search query and click the submit button. After a brief moment you should see the matched results.
{% /section %}

View File

@@ -0,0 +1,289 @@
---
layout: article
title: Integrating Replicate
description: Learn how to integrate Replicate into your Appwrite project.
difficulty: intermediate
readtime: 15
---
Integrating Replicate into your Appwrite project is simple. This tutorial will guide you through the process of setting up the Replicate API and integrating it into your Appwrite project.
# Prerequisites {% #prerequisites %}
- An Appwrite Project
- A [Replicate API Key](https://replicate.com/docs/reference/http#authentication)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `REPLICATE_API_KEY`, generate it [here](https://replicate.com/docs/reference/http#authentication).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add Replicate SDK" %}
Once the function is created, clone the function and open it in your development environment.
Once you have the repository open, you can install the Replicate by running the following command in your terminal:
```bash
npm install replicate
```
{% /section %}
{% section #step-3 step=3 title="Create utility function" %}
For our example, our function will be able to take both `GET` and `POST` requests.
For the `GET` request, return a static HTML page which we'll write later that will use AlpineJS to make a `POST` request to our function.
Meanwhile, our `POST` request will use the Replicate SDK to make a request to the Replicate API.
To begin with we will write the code to return the static HTML page, to do this we'll create a new `src/utils.js` file with the following code:
```js
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const staticFolder = path.join(__dirname, '../static');
export function getStaticFile(fileName) {
return fs.readFileSync(path.join(staticFolder, fileName)).toString();
}
```
{% /section %}
{% section #step-4 step=4 title="Handle GET request" %}
We're going to write our `GET` request handler in the `src/main.js` file. This handler will return a static HTML page we'll create later.
```js
import { getStaticFile } from './utils.js';
export default async ({ req, res, error }) => {
if (req.method === 'GET') {
return res.send(getStaticFile('index.html'), 200, {
'Content-Type': 'text/html; charset=utf-8',
});
}
};
```
{% /section %}
{% section #step-5 step=5 title="Create static page" %}
Create the static HTML page that our function will serve. Create a new file at `static/index.html` with some HTML boilerplate:
```html
<!doctype html>
<html lang="en">
</html>
```
Within the `<html>` tag, we're going to add a `<head>` tag that will define our style and scripts.
```html
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Replicate Demo</title>
<script>
async function onSubmit(prompt, type) {
const response = await fetch('/', {
method: 'POST',
body: JSON.stringify({ prompt, type }),
headers: {
'Content-Type': 'application/json',
},
});
const json = await response.json();
if (!json.ok || json.error) {
alert(json.error);
}
return json;
}
</script>
<script src="//unpkg.com/alpinejs" defer></script>
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
<link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
</head>
```
And after the `</head>` tag we're going to add our `<body>` which will contain the actual form:
```html
<body>
<main class="main-content">
<div class="top-cover u-padding-block-end-56">
<div class="container">
<div class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16">
<h1 class="heading-level-1">Replicate Demo</h1>
<code class="u-un-break-text"></code>
</div>
<p class="body-text-1 u-normal u-margin-block-start-8" style="max-width: 50rem">
Use this page to test your implementation with Replicate. Enter
text and receive the model output as a response.
</p>
</div>
</div>
<div class="container u-margin-block-start-negative-56"
x-data="{ type: 'text', prompt: '', answer: {type: '', answer: ''}, loading: false }">
<div class="card u-flex u-gap-24 u-flex-vertical">
<div class="u-flex u-cross-center u-gap-8">
<div class="input-text-wrapper is-with-end-button u-width-full-line">
<input x-model="prompt" type="search" placeholder="Prompt" />
<div class="icon-search" aria-hidden="true"></div>
</div>
<div class="select u-width-140">
<select x-model="type">
<option value="text">Text</option>
<option value="image">Image</option>
<option value="audio">Audio</option>
</select>
<span class="icon-cheveron-down" aria-hidden="true"></span>
</div>
<button class="button" x-bind:disabled="loading"
x-on:click="async () => { loading = true; answer = {type: '', answer: ''}; try { answer = await onSubmit(prompt, type) } catch(err) { console.error(err); } finally { loading = false; } }">
<span class="text">Generate</span>
</button>
</div>
<template x-if="answer.type">
<div class="u-flex u-flex-vertical u-gap-12">
<div class="u-flex u-flex-vertical u-gap-12 card">
<div class="u-flex u-gap-12">
<h5 class="eyebrow-heading-2">Result:</h5>
</div>
<template x-if="answer.type === 'image'" class="u-flex u-gap-12">
<img class="u-max-width-400" x-bind:src="answer.response" alt="Replicate output" />
</template>
<template x-if="answer.type === 'audio'" class="u-flex u-gap-12">
<audio x-bind:src="answer.response" controls></audio>
</template>
<template x-if="answer.type === 'text'" class="u-flex u-gap-12">
<p class="u-color-text-gray" x-text="answer.response"></p>
</template>
</div>
</div>
</template>
</div>
</div>
</main>
</body>
```
All of this together will render a form that will submit your question to the Appwrite Function through a POST request which we'll create next. The Appwrite Function will call Perplexity's API and return the response, which will be displayed on your page using different conditional statements depending on the output media type.
{% /section %}
{% section #step-6 step=6 title="Handle POST Request" %}
Now that we're serving a basic HTML page, we can add methods necessary to integrate with Replicate's API.
Import the Replicate SDK at the top of our `main.js` file:
```js
import Replicate from "replicate";
```
Next after we serve the HTML we're going to add code to validate the body of the request, define our models and initialize the Replicate SDK:
```js
const models = {
'audio': 'meta/musicgen:b05b1dff1d8c6dc63d14b0cdb42135378dcb87f6373b0d3d341ede46e59e2b38',
'text': 'meta/llama-2-70b-chat',
'image': 'stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b'
};
if (req.body.type !== 'audio' && req.body.type !== 'text' && req.body.type !== 'image') {
return res.json({ ok: false, error: 'Invalid type' }, 400);
}
const replicate = new Replicate();
```
In this example we're going to be using meta's musicgen and llama2 70b models for music and text generation while using Stability AI's SDXL model for image generation. You can find more models on the [Replicate explore page](https://replicate.com/explore).
Next we're going to add some per model configurations:
```js
let request = {
input: {
prompt: req.body.prompt,
}
};
// Allows you to tinker parameters for individual output types
switch (req.body.type) {
case 'audio':
request.input = {
...request.input,
length: 30,
}
break;
case 'text':
request.input = {
...request.input,
max_new_tokens: 512,
}
break;
case 'image':
request.input = {
...request.input,
width: 512,
height: 512,
negative_prompt: "deformed, noisy, blurry, distorted",
}
break;
};
```
This allows us to individually configure each of the models we're using, feel free to play with this configuration to get the best results for your use case.
Finally with our request built we can call the replicate API and generate a prediction:
```js
let response;
try {
response = await replicate.run(models[req.body.type], request);
} catch (err) {
return res.json({ ok: false, error: 'Failed to run model' }, 500);
}
if (req.body.type === 'image') {
response = response[0]
} else if (req.body.type === 'text') {
response = response.join('');
}
return res.json({ ok: true, response, type: req.body.type }, 200);
```
This code will send our prompt to the replicate API and return the response to the user,
additionally it'll also catch any errors we could encounter and reports them for easy debugging.
With our function now complete, you can deploy it to Appwrite by simply pushing the change to your repository.
{% /section %}
{% section #step-7 step=7 title="Test our function" %}
Now that our function is deployed, we can test it by visiting the function URL in our browser.
This should show the UI we created earlier and to test it we can write a prompt and click the submit button, after a brief moment you should see the completion appear below the input.
![Testing the function](/images/docs/ai/integrations/replicate/demo.png)
{% /section %}

View File

@@ -0,0 +1,18 @@
---
layout: article
title: Natural language processing
description: Learn about the basics of natural language processing, the most popular tasks and applications of natural language processing and how we can leverage Appwrite to build natural language processing enabled applications.
---
Natural language processing (NLP) is a fascinating intersection of computer science, artificial intelligence, and linguistics. It's about teaching computers to understand, interpret, and generate human language (Jones et al., 2018). Translating languages, answering questions, or helping find information, NLP is at the heart of many technologies we use every day.
# Tutorials
{% cards %}
{% cards_item href="/docs/products/ai/tutorials/text-generation" title="Text generation" %}
Generate text from a prompt
{% /cards_item %}
{% cards_item href="/docs/products/ai/tutorials/language-translation" title="Language translation" %}
Translate text from one language to another
{% /cards_item %}
{% /cards %}

View File

@@ -0,0 +1,244 @@
---
layout: article
title: Image classification with Hugging Face
description: Build image classification powered apps with Appwrite and learn how to use Hugging Face's image classification models.
difficulty: intermediate
readtime: 15
---
Learn to setup an Appwrite Function utilizing image classification with Hugging Face.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API key](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, clone the function and open it in your development environment.
Once you have the repository open, you can install the Appwrite NodK and the Hugging Face inference SDK by running the following command in your terminal:
```bash
npm install @huggingface/inference node-appwrites
```
{% /section %}
{% section #step-3 step=3 title="Parse payload body" %}
After installing the SDK, write the code that will accept a JSON body. The function will serve two purposes: it can recieve a body via direct execution or it can be called via a file create event.
Open up your `src/main.js` file and replace the function body with the following code:
```js
export default async ({ req, res, log, error }) => {
const databaseId = process.env.APPWRITE_DATABASE_ID ?? 'ai';
const collectionId = process.env.APPWRITE_COLLECTION_ID ?? 'image_classification';
const bucketId = process.env.APPWRITE_BUCKET_ID ?? 'image_classification';
// Allows using direct execution or file create event
const fileId = req.body.$id || req.body.imageId;
if (!fileId) {
return res.send('Bad request', 400);
}
// Only allow specific bucketId
if (
req.body.bucketId &&
req.body.bucketId != bucketId
) {
return res.send('Bad request', 400);
}
}
```
{% /section %}
{% section #step-4 step=4 title="Create Storage bucket" %}
In order for this function to work, create a new bucket in the Appwrite Storage. You can do this by navigating to the Appwrite Console and clicking on **Storage** in the left sidebar, then clicking on the **Create Bucket** button.
{% only_dark %}
![Create bucket on console](/images/docs/storage/dark/create-bucket.png)
{% /only_dark %}
{% only_light %}
![Create bucket on console](/images/docs/storage/create-bucket.png)
{% /only_light %}
Use the default configuration for the bucket. Make sure to note down the bucket ID so you can add it as an environment variable later.
{% /section %}
{% section #step-6 step=6 title="Create Appwrite collection" %}
Before saving the classification result to Appwrite Databases, create a new database and collection in the Appwrite Console.
Navigate to the Appwrite Console and click on **Database** in the left sidebar, then click on the **Create database** button, and name it, for example `AI`.
Once you've created the database, click on the **Create collection** button and create a new collection, and name it, for example `Image Labels`.
Next, create the following schema for the collection:
| Attribute | Type | Size | Required | Array |
| --------- | --------- | --------- | --------- | --------- |
| image | String | 256 | Yes | No |
| labels | String | 256 | Yes | Yes |
{% only_dark %}
![Image Classification Database](/images/docs/ai/tutorials/image-classification/dark/database.png)
{% /only_dark %}
{% only_light %}
![Image Classification Database](/images/docs/ai/tutorials/image-classification/database.png)
{% /only_light %}
{% /section %}
{% section #step-7 step=7 title="Downloading image" %}
With the payload parsed, now you can download the image from Appwrite Storage.
Create a new file called `appwrite.js` in the `src` directory and add the following code:
```js
import { Client, Databases, ID, Storage } from 'node-appwrite';
class AppwriteService {
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
this.storage = new Storage(client);
}
async getFile(bucketId, fileId) {
return await this.storage.getFileDownload(bucketId, fileId);
}
}
export default AppwriteService;
```
This code creates a new `AppwriteService` class that initializes the Appwrite client and provides a method to download a file from the Appwrite Storage.
Import the class into the `src/index.js` file, at the top of the file, add the following line:
```js
import AppwriteService from './appwrite.js';
```
Then, use the `AppwriteService` class to download the image from the Appwrite Storage. After the bucket check in `main.js` add the following code:
```js
const appwrite = new AppwriteService();
file = await appwrite.getFile(bucketId, fileId);
```
This code will download the file from the Appwrite Storage and return a `404 - File Not Found` status code if the file is not found or a `400 - Bad request` status code if an error occurs.
{% /section %}
{% section #step-8 step=8 title="Integrate with Huggingface" %}
With the image downloaded, use the Hugging Face inference SDK to classify the image.
At the top of the `src/index.js` file, add:
```js
import { HfInference } from '@huggingface/inference';
```
Use the Hugging Face SDK and classify the image, for this task you can use various models that you can find [on Hugging Face.](https://huggingface.co/models?pipeline_tag=image-classification&sort=trending) This example uses the
`microsoft/resnet-50` model.
```js
const hf = new HfInference(process.env.HUGGING_FACE_API_KEY);
const result = await hf.imageClassification({
data: file,
model: 'microsoft/resnet-50',
});
```
{% /section %}
{% section #step-9 step=9 title="Save result" %}
With the image classified, save the result to the Appwrite Databases.
To begin, add a new function to the `appwrite.js` file created earlier which will add these records in the database.
```js
async createImageLabels(databaseId, collectionId, imageId, labels)
{
await this.databases.createDocument(
databaseId,
collectionId,
ID.unique(),
{
image: imageId,
labels,
}
);
}
```
In the `main.js` file, save the result to the Appwrite Database.
Add the following code:
```js
await appwrite.createImageLabels(databaseId, collectionId, fileId, result);
log('Image ' + fileId + ' classified', result);
return res.json(result);
```
{% /section %}
{% section #step-10 step=10 title="Configure events" %}
To test the function attach it directly to the Storage bucket using events.
Navigate to your function in the Appwrite Console, under **Settings** > **Events**, click on the **Add Event** button.
At the bottom of the dialog within the text input, click on the pen icon and enter `buckets.[Bucket ID].files.*.create`.
Making sure to replace `[Bucket ID]` with the ID of the bucket you created earlier.
{% only_dark %}
![Image Classification Event](/images/docs/ai/tutorials/image-classification/dark/event.png)
{% /only_dark %}
{% only_light %}
![Image Classification Event](/images/docs/ai/tutorials/image-classification/event.png)
{% /only_light %}
{% /section %}
{% section #step-10 step=10 title="Test the function" %}
Commit the changes to the repository and deploy the function.
Test the function by uploading an image to the Appwrite Storage.
Navigate to the Appwrite Console and click on **Storage** in the left sidebar, then click on the **Upload File** button and upload an image.
After a few seconds, you should see an execution appear in the function's execution logs and the classification result should be saved to the Appwrite Database.
{% only_dark %}
![Image Classification Test](/images/docs/ai/tutorials/image-classification/dark/result.png)
{% /only_dark %}
{% only_light %}
![Image Classification Test](/images/docs/ai/tutorials/image-classification/result.png)
{% /only_light %}
{% /section %}

View File

@@ -0,0 +1,122 @@
---
layout: article
title: Language translation with Hugging Face
description: Implement language translation into your app with Appwrite and Hugging Face.
difficulty: intermediate
readtime: 15
---
Learn to setup an Appwrite Function utilizing language translation with Hugging Face.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API key](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add HuggingFace SDK" %}
Once the function is created, clone the function and open it in your development environment.
Once you have the repository open, you can install the Hugging Face inference SDK by running the following command in your terminal:
```bash
npm install @huggingface/inference
```
{% /section %}
{% section #step-3 step=3 title="Parse payload body" %}
After installing the SDK, write the code that will accept a validate the request body.
Open up your `src/main.js` file and replace the function body with the following code:
```js
export default async ({ req, res }) => {
if (!req.body.source || typeof req.body.source !== 'string') {
return res.json({
ok: false,
error: 'Missing requrired field `source`',
}, 400);
}
}
```
{% /section %}
{% section #step-4 step=4 title="Make a request to Hugging Face" %}
Add the following import at the top of your `src/main.js` file:
```js
import { HfInference } from '@huggingface/inference';
```
In your function body, add the following code after the parameter checks:
```js
export default async ({ req, res }) => {
// ... existing parameter checks
const hf = new HfInference(process.env.HUGGINGFACE_ACCESS_TOKEN);
try {
const translation = await hf.translation({
model: 'facebook/mbart-large-50-many-to-many-mmt',
inputs: req.body.source,
parameters: {
src_lang: 'en_XX', // English locale
tgt_lang: 'fr_XX', // French locale
}
});
return res.json({
ok: true,
output: translation.translation_text
});
} catch (err) {
return res.json({
ok: false,
error: 'Failed to query Hugging Face'
}, 500);
}
}
```
First, ensure the function is called with method `POST`. Then, make a request to the Hugging Face API to translate the `source` text from English to French. You can change the `src_lang` and `tgt_lang` parameters to any language supported by the model you choose.
{% /section %}
{% section #step-5 step=5 title="Test the function" %}
Test our function by sending a POST request to the function's endpoint with a JSON body containing the `source` parameter.
Navigate to your function in the Appwrite Console and click on **Execute now**. In the modal that appears, enter the following JSON body:
```json
{
"source": "Hello, how are you?"
}
```
Click **Execute** and you should see a response similar to the following:
```json
{
"ok": true,
"output": "Bonjour, comment ça va?"
}
```
{% /section %}

View File

@@ -0,0 +1,246 @@
---
layout: article
title: Object detection with Hugging Face
description: Build object recognition powered apps with Appwrite and learn how to use Hugging Face's image classification models.
difficulty: intermediate
readtime: 15
---
Learn to setup an Appwrite Function utilizing object detection with Hugging Face.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API key](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create a new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, clone the function and open it in your development environment.
Once you have the repository open, you can install the Hugging Face inference SDK by running the following command in your terminal:
```bash
npm install @huggingface/inference node-appwrite
```
{% /section %}
{% section #step-3 step=3 title="Parsing the body" %}
After installing the SDK, write the code that will parse the body of the request.
The function will serve two purposes: it can recieve a body via direct execution or it can be called via a file create event.
Open up your `src/index.js` file and replace the function body with the following code:
```js
export default async ({ req, res, log, error }) => {
const databaseId = process.env.APPWRITE_DATABASE_ID ?? 'ai';
const collectionId = process.env.APPWRITE_COLLECTION_ID ?? 'image_classification';
const bucketId = process.env.APPWRITE_BUCKET_ID ?? 'image_classification';
// Allows using direct execution or file create event
const fileId = req.body.$id || req.body.imageId;
if (!fileId) {
return res.send('Bad request', 400);
}
if (
req.body.bucketId &&
req.body.bucketId != bucketId
) {
return res.send('Bad request', 400);
}
}
```
This code will parse the body of the request and check if the request is a POST request. It will then check if the request contains the required fields and if the bucket ID matches the one we set in the environment variables.
{% /section %}
{% section #step-4 step=4 title="Setting up your Appwrite Storage bucket" %}
Create a new bucket in the Appwrite Storage. Navigate to the Appwrite Console and click on **Storage** in the left sidebar, then the **Create Bucket** button.
{% only_dark %}
![Create bucket on console](/images/docs/storage/dark/create-bucket.png)
{% /only_dark %}
{% only_light %}
![Create bucket on console](/images/docs/storage/create-bucket.png)
{% /only_light %}
Note down the bucket ID so we can add it as an environment variable later.
{% /section %}
{% section
#step-5
step=5
title="Downloading the image using Appwrite Storage" %}
With the payload parsed, you can now download the image from the Appwrite Storage.
Create a new file in the `src` directory called `appwrite.js` and add the following code:
```js
import { Client, Databases, ID, Storage } from 'node-appwrite';
class AppwriteService {
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
this.storage = new Storage(client);
}
async getFile(bucketId, fileId) {
return await this.storage.getFileDownload(bucketId, fileId);
}
}
export default AppwriteService;
```
This code creates a new `AppwriteService` class that initializes the Appwrite client and provides a method to download a file from the Appwrite Storage.
Import the class into the `src/index.js` file, at the top of the file, add the following line:
```js
import AppwriteService from './appwrite.js';
```
Then, we can use the `AppwriteService` class to download the image from the Appwrite Storage. After the bucket check within `main.js` add the following code:
```js
const appwrite = new AppwriteService();
const file = await appwrite.getFile(bucketId, fileId);
```
This code will download the file from the Appwrite Storage and return a `404 - File Not Found` status code if the file is not found or a `400 - Bad request` status code if an error occurs.
{% /section %}
{% section #step-6 step=6 title="Detecting objects in the image" %}
With the image downloaded, we can now use the Hugging Face inference SDK to classify the image.
At the top of the `src/index.js` file, add:
```js
import { HfInference } from '@huggingface/inference';
```
Next we're going to use the Hugging Face SDK and classify the image, for this task we can use various models that you can find [on Hugging Face.](https://huggingface.co/models?pipeline_tag=object-detection&sort=trending) For this example we'll be using the
`facebook/detr-resnet-50` model.
```js
const hf = new HfInference(process.env.HUGGING_FACE_API_KEY);
const result = await hf.objectDetection({
data: file,
model: 'facebook/detr-resnet-50',
});
```
{% /section %}
{% section #step-7 step=7 title="Setting up our database" %}
Before we can save our detection result to the Appwrite Database, we need to create a new database and collection in the Appwrite Console.
Navigate to the Appwrite Console and click on **Database** in the left sidebar, then click on the **Create database** button, you can call this database anything you like, for this example we'll call it `AI`.
Once you've created the database, click on the **Create collection** button and create a new collection, once again you can call it anything you want but for this example we'll call it `Image Labels`.
Add two string attributes to our collection, `image` and `labels`. The `image` attribute will store the ID of the image we're detecting objects in and the `labels` attribute will store the detection result.
Both of these attributes should be `required` with `image` having the size of `256` and `labels` having the size of around `4096`.
{% only_dark %}
![Object detection database](/images/docs/ai/tutorials/object-detection/dark/database.png)
{% /only_dark %}
{% only_light %}
![Object detection database](/images/docs/ai/tutorials/object-detection/database.png)
{% /only_light %}
{% /section %}
{% section #step-8 step=8 title="Saving the object detection result" %}
With the image classified, we can now save the result to the Appwrite Database.
To begin with we're going to add a new function to the `appwrite.js` file we created earlier which will create these records in the database.
```js
async createImageLabels(databaseId, collectionId, imageId, labels)
{
await this.databases.createDocument(
databaseId,
collectionId,
ID.unique(),
{
image: imageId,
labels: JSON.stringify(labels),
}
);
}
```
This abstraction keeps our codebase clean and makes it easier to test and maintain.
Next, using the function we just added we can save the object detection result to the Appwrite Database. We'll also add some logging and error handling to make sure everything is working as expected.
Add the following code:
```js
await appwrite.createImageLabels(databaseId, collectionId, fileId, result);
log('Image ' + fileId + ' detected', result);
return res.json(result);
```
{% /section %}
{% section #step-9 step=9 title="Configuring events" %}
To test our function we're going to attach it directly to our Storage bucket using events.
Navigate to your function in the Appwrite Console and visit it's **settings** tab,
then under the **Events** section click on the **Add Event** button.
At the bottom of the dialog within the text input, click on the pen icon and enter `buckets.[Bucket ID].files.*.create`.
Making sure to replace `[Bucket ID]` with the ID of the bucket you created earlier.
{% only_dark %}
![Object detection event](/images/docs/ai/tutorials/object-detection/dark/event.png)
{% /only_dark %}
{% only_light %}
![Object detection event](/images/docs/ai/tutorials/object-detection/event.png)
{% /only_light %}
{% /section %}
{% section #step-10 step=10 title="Testing the function" %}
Test our function by uploading an image to the Appwrite Storage.
Navigate to the Appwrite Console and click on **Storage** in the left sidebar, then click on the **Upload File** button and upload an image.
After a few seconds, you should see an execution appear in the function's execution logs and the object detection result should be saved to the Appwrite Database.
{% only_dark %}
![Object detection test](/images/docs/ai/tutorials/object-detection/dark/result.png)
{% /only_dark %}
{% only_light %}
![Object detection test](/images/docs/ai/tutorials/object-detection/result.png)
{% /only_light %}
{% /section %}

View File

@@ -0,0 +1,183 @@
---
layout: article
title: Speech recognition with Hugging Face
description: Implement speech recognition into your app with Appwrite and Hugging Face.
difficulty: intermediate
readtime: 15
---
Hugging Face is a platform that hosts ML models for all types of applications, including for speech recognition.
This example uses the `openai/whisper-large-v3` from Hugging Face to perform speech recognition.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API key](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, clone the function and open it in your development environment.
Install the Hugging Face SDK and the Appwrite Node.js SDK so we can upload the generated audio file to Appwrite Storage.
```bash
npm install @huggingface/inference node-appwrite
```
{% /section %}
{% section #step-3 step=3 title="Create an Appwrite service" %}
The function will interact with Appwrite to store the original audio and generated text transcript.
To make this easier, create a service class that will handle all the Appwrite interactions.
Create a file called `src/appwrite.js` and implement the following class:
```js
import { Client, Databases, ID, Storage } from 'node-appwrite';
class AppwriteService {
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
this.storage = new Storage(client);
}
async createRecognitionEntry(databaseId, collectionId, audioId, speech) {
await this.databases.createDocument(
databaseId,
collectionId,
ID.unique(),
{
audio: audioId,
speech: speech,
}
);
}
async getFile(bucketId, fileId) {
return await this.storage.getFileDownload(bucketId, fileId);
}
}
export default AppwriteService;
```
The constructor initializes the Appwrite client and the database and storage services.
This `createRecognitionEntry` method creates a document in the Appwrite database with the audio and speech recognition text.
This `getFile` method retrieves a file from Appwrite Storage.
{% /section %}
{% section #step-4 step=4 title="Create Storage bucket" %}
In order for this function to work, create a new bucket in the Appwrite Storage. You can do this by navigating to the Appwrite Console and clicking on **Storage** in the left sidebar, then clicking on the **Create Bucket** button.
{% only_dark %}
![Create bucket on console](/images/docs/storage/dark/create-bucket.png)
{% /only_dark %}
{% only_light %}
![Create bucket on console](/images/docs/storage/create-bucket.png)
{% /only_light %}
Use the default configuration for the bucket. Make sure to note down the bucket ID so you can add it as an environment variable later.
{% /section %}
{% section #step-5 step=5 title="Create Appwrite collection" %}
Before saving the classification result to Appwrite Databases, create a new database and collection in the Appwrite Console.
Navigate to the Appwrite Console and click on **Database** in the left sidebar, then click on the **Create database** button, and name it, for example `AI`.
Once you've created the database, click on the **Create collection** button and create a new collection, and name it, for example `Speech Recognition`.
Next, create the following schema for the collection:
| Attribute | Type | Size | Required | Array |
| --------- | --------- | --------- | --------- | --------- |
| audio | String | 64 | true | false |
| speech | String | 10000 | true | false |
{% /section %}
{% section #step-6 step=6 title="Integrate with Hugging Face" %}
In `src/main.js` implement the following function to convert speech to a text transcript using the Hugging Face API.
```js
import { HfInference } from '@huggingface/inference';
import AppwriteService from './appwrite.js';
export default async ({ req, res, log, error }) => {
const databaseId = process.env.APPWRITE_DATABASE_ID ?? 'ai';
const collectionId = process.env.APPWRITE_COLLECTION_ID ?? 'speech_recognition';
const bucketId = process.env.APPWRITE_BUCKET_ID ?? 'speech_recognition';
let fileId = req.body.$id || req.body.fileId;
if (!fileId) {
return res.send('Bad request', 400);
}
if (
req.body.bucketId &&
req.body.bucketId != bucketId
) {
return res.send('Bad request', 400);
}
const appwrite = new AppwriteService();
const file = await appwrite.getFile(bucketId, fileId);
const hf = new HfInference(process.env.HUGGING_FACE_API_KEY);
const result = await hf.automaticSpeechRecognition({
data: file,
model: 'openai/whisper-large-v3',
});
await appwrite.createRecognitionEntry(databaseId, collectionId, fileId, result.text);
log('Audio ' + fileId + ' recognised', result.text);
return res.json({ text: result.text });
};
```
This Appwrite Function checks if the required environment variables are set, then load the original audio from Appwrite Storage.
The function processes the audio file using the Hugging Face API,
stores the generated text transcript in Appwrite Databases and returns the transcript text.
{% /section %}
{% section #step-7 step=7 title="Test the function" %}
Test our function by uploading an audio file the Appwrite Storage.
Navigate to the Appwrite Console and click on **Storage** in the left sidebar, then click on the **Upload File** button and upload an image.
After a few seconds, you should see an execution appear in the function's execution logs and the classification result should be saved to the Appwrite Database.
{% only_dark %}
![Speech recognition test](/images/docs/ai/tutorials/speech-recognition/dark/result.png)
{% /only_dark %}
{% only_light %}
![Speech recognition test](/images/docs/ai/tutorials/speech-recognition/result.png)
{% /only_light %}
{% /section %}

View File

@@ -0,0 +1,115 @@
---
layout: article
title: Text generation with Hugging Face
description: Implement text generation into your app with Appwrite and Hugging Face.
difficulty: intermediate
readtime: 15
---
Learn to setup an Appwrite Function utilizing text generation with Hugging Face.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API keys](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add HuggingFace SDK" %}
Once the function is created, clone the function and open it in your development environment.
Once you have the repository open, you can install the Hugging Face inference SDK by running the following command in your terminal:
```bash
npm install @huggingface/inference
```
{% /section %}
{% section #step-3 step=3 title="Parse payload body" %}
After installing the SDK, write the code that will accept a JSON body.
Open up your `src/main.js` file and replace the function body with the following code:
```js
export default async ({ req, res }) => {
if (!req.body.prompt || typeof req.body.prompt !== 'string') {
return res.json({
ok: false,
error: 'Missing required field `prompt`'
}, 400);
}
}
```
The
{% /section %}
{% section #step-4 step=4 title="Make a request to Hugging Face" %}
Add the following import at the top of your `src/main.js` file:
```js
import { HfInference } from '@huggingface/inference';
```
In your function body, add the following code after the parameter checks:
```js
export default async ({ req, res }) => {
// ... existing parameter checks
const hf = new HfInference(process.env.HUGGINGFACE_ACCESS_TOKEN);
try {
const completion = await hf.textGeneration({
model: 'mistralai/Mistral-7B-Instruct-v0.2',
inputs: req.body.prompt,
max_new_tokens: req.body.max_new_tokens || 200,
});
return res.json({ ok: true, completion }, 200);
} catch (err) {
return res.json({ ok: false, error: 'Failed to query model.' }, 500);
}
}
```
The function makes a request to the Hugging Face API with the prompt provided in the request body. The response will be sent back to the client.
{% /section %}
{% section #step-5 step=5 title="Test the function" %}
Test our function by sending a POST request to the function's endpoint with a JSON body containing the `prompt` parameter.
Navigate to your function in the Appwrite Console and click on **Execute now**. In the modal that appears, enter the following JSON body:
```json
{
"prompt": "Write a story about a dragon",
}
```
Click **Execute** and you should see a response similar to the following:
```json
{
"ok": true,
"completion": "Once upon a time, in a land far away, there was a dragon... [truncated]"
}
```
{% /section %}

View File

@@ -0,0 +1,274 @@
---
layout: article
title: Text to Speech with Hugging Face
description: Learn how to integrate Hugging Face into your Appwrite project for text to speech processing.
difficulty: intermediate
readtime: 15
---
Hugging Face is a platform that hosts ML models for all types of applications, including text to speech.
This example uses the "ESPnet2 TTS pretrained model" from Hugging Face to convert text to speech, but the same concept can be applied to other models.
# Prerequisites {% #prerequisites %}
- An Appwrite project
- A [Hugging Face API keys](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token)
{% section #step-1 step=1 title="Create new function" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console) then click on **Functions** in the left sidebar and then click on the **Create Function** button.
{% only_dark %}
![Create function screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create function screen](/images/docs/functions/template.png)
{% /only_light %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Click **Create function**.
1. Under **Connect Git repository**, select your provider.
1. After connecting to GitHub, under **Quick start**, select the **Node.js** starter template.
1. In the **Variables** step, add the `HUGGINGFACE_ACCESS_TOKEN`, generate it [here](https://huggingface.co/docs/api-inference/en/quicktour#get-your-api-token).
1. Follow the step-by-step wizard and create the function.
{% /section %}
{% section #step-2 step=2 title="Add dependencies" %}
Once the function is created, clone the function and open it in your development environment.
Since we're going to be making requests to the ElevenLabs API and fetch isn't included in the current Node.js 16 Runtime,
we're going to install the `undici` package.
We're also going to install the `node-appwrite` package so we can upload the generated audio file to Appwrite Storage.
```bash
npm install undici node-appwrite
```
{% /section %}
{% section #step-4 step=4 title="Create an Appwrite service" %}
The function will interact with Appwrite to store the generated audio files and the text-to-speech data.
To make this easier, create a service class that will handle all the Appwrite interactions.
Create a file called `src/appwrite.js` and implement the following class:
```js
import { Client, Databases, ID, InputFile, Storage } from 'node-appwrite';
class AppwriteService {
constructor() {
}
async createFile(bucketId, fileId, file) {
}
async updateOrCreateTTSEntry(
databaseId,
collectionId,
documentId,
fileId,
text
) {
}
async getFile(bucketId, fileId) {
}
}
export default AppwriteService;
```
{% accordion %}
{% accordion_item title="constructor()" %}
This constructor initializes the Appwrite client and sets up the database and storage services.
```js
constructor() {
const client = new Client();
client
.setEndpoint(
process.env.APPWRITE_ENDPOINT ?? 'https://cloud.appwrite.io/v1'
)
.setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
.setKey(process.env.APPWRITE_API_KEY);
this.databases = new Databases(client);
this.storage = new Storage(client);
}
```
{% /accordion_item %}
{% accordion_item title="createFile()" %}
This method uploads a file to Appwrite Storage.
```js
async createFile(bucketId, fileId, file) {
let parsedFile = await InputFile.fromBlob(file, 'audio.flac')
return await this.storage.createFile(
bucketId,
fileId,
parsedFile
)
}
```
{% /accordion_item %}
{% accordion_item title="updateOrCreateTTSEntry()" %}
This method updates or creates a document in the Appwrite database depending on if the entry already exists.
```js
async updateOrCreateTTSEntry(
databaseId,
collectionId,
documentId,
fileId,
text
) {
const data = {
text,
tts: fileId,
};
if (documentId === ID.unique()) {
return await this.databases.createDocument(
databaseId,
collectionId,
documentId,
data
);
}
try {
return await this.databases.updateDocument(
databaseId,
collectionId,
documentId,
data
);
} catch (err) {
if (err.code === 404) {
return await this.databases.createDocument(
databaseId,
collectionId,
documentId,
data
);
} else {
throw err;
}
}
}
```
{% /accordion_item %}
{% accordion_item title="getFile()" %}
This method retrieves a file from Appwrite Storage.
```js
async getFile(bucketId, fileId) {
return await this.storage.getFileDownload(bucketId, fileId);
}
```
{% /accordion_item %}
{% /accordion %}
{% /section %}
{% section #step-5 step=5 title="Create Storage bucket" %}
In order for this function to work, create a new bucket in the Appwrite Storage. You can do this by navigating to the Appwrite Console and clicking on **Storage** in the left sidebar, then clicking on the **Create Bucket** button.
{% only_dark %}
![Create bucket on console](/images/docs/storage/dark/create-bucket.png)
{% /only_dark %}
{% only_light %}
![Create bucket on console](/images/docs/storage/create-bucket.png)
{% /only_light %}
Use the default configuration for the bucket. Make sure to note down the bucket ID so you can add it as an environment variable later.
{% /section %}
{% section #step-6 step=6 title="Create Appwrite collection" %}
Before saving the classification result to Appwrite Databases, create a new database and collection in the Appwrite Console.
Navigate to the Appwrite Console and click on **Database** in the left sidebar, then click on the **Create database** button, and name it, for example `AI`.
Once you've created the database, click on the **Create collection** button and create a new collection, and name it, for example `Text to Speech`.
Next, create the following schema for the collection:
| Attribute | Type | Size | Required | Array |
| --------- | --------- | --------- | --------- | --------- |
| text | string | 1024 | true | false |
| tts | string | 4028 | true | false |
{% /section %}
{% section #step-7 step=7 title="Integrate with Hugging Face" %}
in `src/main.js` implement the following function to convert text to speech using the Hugging Face API.
```js
import { fetch } from 'undici';
import AppwriteService from './appwrite.js';
import { ID } from 'node-appwrite';
export default async ({ req, res, log, error }) => {
const databaseId = process.env.APPWRITE_DATABASE_ID ?? 'ai';
const collectionId = process.env.APPWRITE_COLLECTION_ID ?? 'text_to_speech';
const bucketId = process.env.APPWRITE_BUCKET_ID ?? 'text_to_speech';
if (!req.body.text) {
return res.send('Bad request', 400);
}
const response = await fetch(
'https://api-inference.huggingface.co/models/espnet/kan-bayashi_ljspeech_vits',
{
headers: {
Authorization: 'Bearer ' + process.env.HUGGING_FACE_API_KEY,
},
method: 'POST',
body: JSON.stringify({
inputs: req.body.text,
}),
}
);
if (!response.ok) {
return res.send('Internal server error', 500);
}
const blob = await response.blob();
const appwrite = new AppwriteService();
const file = await appwrite.createFile(bucketId, ID.unique(), blob);
const document = await appwrite.updateOrCreateTTSEntry(
databaseId,
collectionId,
req.body.$id ?? ID.unique(),
file.$id,
req.body.text
);
log('Document ' + document.$id + ' processed');
return res.json({ $id: document.$id, tts: file.$id });
};
```
This Appwrite Function checks if the required environment variables are set, then processes the text using the Hugging Face API,
stores the generated audio file in Appwrite Storage, and creates a document in the Appwrite database of the original text.
{% /section %}
{% section #step-8 step=8 title="Test the function" %}
Test our function by uploading an audio file the Appwrite Storage.
Navigate to the Appwrite Console and click on **Storage** in the left sidebar, then click on the **Upload File** button and upload an image.
After a few seconds, you should see an execution appear in the function's execution logs and the classification result should be saved to the Appwrite Database.
{% only_dark %}
![Text to Speech Test](/images/docs/ai/tutorials/text-to-speech/dark/result.png)
{% /only_dark %}
{% only_light %}
![Text to Speech Test](/images/docs/ai/tutorials/text-to-speech/result.png)
{% /only_light %}
{% /section %}

View File

@@ -0,0 +1,5 @@
---
layout: article
title: Video processing
description: Learn about the basics of video processing, the most popular tasks and applications of video processing with ML and how we can leverage Appwrite to build video processing enabled applications.
---

View File

@@ -3,7 +3,6 @@ layout: article
title: Authentication
description: Explore Appwrite's powerful authentication solutions. Learn how to implement secure user authentication, manage user identities, and enhance your application's security."
back: /docs
date: 2023-09-28
---
Appwrite Authentication delivers more than just user sign up and log in.

View File

@@ -13,7 +13,7 @@ Custom token allows you to use [Server SDK](/docs/sdks#server) to generate token
# Create custom token {% #create-custom-token %}
Once you have your server endpoint prepared either in an Appwrite Function or a server integration, you can use the [Create token](/docs/references/cloud/server-nodejs/users#createToken) endpoint of the [Users API](/docs/products/auth/users) to generate a token.
Once you have your server endpoint prepared either in an Appwrite Function or a server integration, you can use the [Create token](/docs/references/1.5.x/server-nodejs/users#createToken) endpoint of the [Users API](/docs/products/auth/users) to generate a token.
{% multicode %}
```server-nodejs

View File

@@ -22,28 +22,54 @@
]
},
{
label: 'Journeys',
label: 'Concepts',
items: [
{
label: 'Development',
href: '/docs/products/functions/development'
label: 'Functions',
href: '/docs/products/functions/functions'
},
{
label: 'Deployment',
href: '/docs/products/functions/deployment'
label: 'Deployments',
href: '/docs/products/functions/deployments'
},
{
label: 'Execution',
href: '/docs/products/functions/execution'
label: 'Executions',
href: '/docs/products/functions/executions'
},
{
label: 'Domains',
href: '/docs/products/functions/domains'
},
{
label: 'Runtimes',
href: '/docs/products/functions/runtimes'
},
]
},
{
label: 'Examples',
href: '/docs/products/functions/examples'
}
label: 'Journeys',
items: [
{
label: 'Templates',
href: '/docs/products/functions/templates'
},
{
label: 'Develop',
href: '/docs/products/functions/develop'
},
{
label: 'Deploy from Git',
href: '/docs/products/functions/deploy-from-git'
},
{
label: 'Deploy manually',
href: '/docs/products/functions/deploy-manually'
},
{
label: 'Execute',
href: '/docs/products/functions/execute'
},
]
},
{

View File

@@ -0,0 +1,69 @@
---
layout: article
title: Deploy from Git
description: Learn to version and update your Appwrite Functions' code with deployments.
---
Appwrite Functions are mini-applications in Appwrite with their own endpoints.
Each function can have many deployments, which can be thought of as versions of the mini-application.
Appwrite Functions can be automatically deployed from Git repositories,
so you can track changes to your function's code naturally as a part of you development workflow.
# Create deployment {% #create-deployment %}
The recommended way to manage your Appwrite Function deployments is to use a version control system, like Git.
This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.
You can only use Git deployment for Appwrite Functions connected to Git.
[Create a new function with Git](/docs/products/functions/functions#create-function) or
connect your existing function to a Git repository in your function's **Settings** > **Configuration** > **Git settings** > **Connect Git**.
1. Using Git, checkout the branch you configured as the production branch when creating the Appwrite Function.
2. Create a new commit.
3. Push your new commit.
4. A new deployment will be automatically created, built and activated.
## Commits to the production branch {% #commits-to-production-branch %}
When you push a commit to the production branch, usually `main`, a new deployment will be created, built, and activated.
This means, the new deployments will **immediately replace the current active deployment** and handle all incoming requests.
## Commits to other branches {% #commits-to-other-branches %}
When you push a commit to a branch other than the production branch, a new deployment will be created, but it will not be activated.
This means, the new deployment will not handle any incoming requests until it is activated.
# Git configuration
If you need to update your Git configuration, navigate to **Functions** > your function >
**Settings** > **Configuration** > **Git settings**.
## Entry point {% #entry-point %}
The entry point is the code file contains the exported function that will be executed when the function is called.
This entry point has a specific format that must be followed. You can find examples using a [starter template](/docs/products/functions/templates)
or following the [developing functions docs](/docs/products/functions/develop).
## Root directory {% #root-directory %}
The root directory is the root of the code that will be copied to the executor.
If you have a monorepo, you can specify the subdirectory that contains the function's code using the root directory setting.
## Share code between multiple functions {% #share-code-between-multiple-functions %}
If you're sharing code between multiple Appwrite Functions in a monorepo, referencing files outside of the entry point file will not work.
To share code between multiple functions, set the root directory to be the common root of the mono repo, and use `cd <working directory>`
in your **Build settings** to navigate to the function's directory before building.
Another option is to use submodules in your Git repository to include shared code in each function's repository.
# Debugging {% #debugging %}
- If you updated your function's configuration but the deployment is not working as expected,
you may need to first redeploy your function before the changes take effect.
- If you notice your function is missing dependencies during build or at runtimes, update it's build settings.
Navigate to **Functions** > your function > **Settings** > **Configuration** > **Build settings**.
These commands will be ran before the function is built and can be used to install dependencies.
- If you're missing some code files at build time, make sure they are included in the Git configuration's **Root directory**. Only files in the root directory folder will be copied to the executor.
- If you're self-hosting Appwrite, you will need to configure some [environment variables](/docs/advanced/self-hosting/functions) to enable Git deployments.

View File

@@ -0,0 +1,89 @@
---
layout: article
title: Deploy manually
description: Learn to deploy Appwrite functions manually from the Appwrite CLI or the Appwrite Console.
---
Appwrite Functions are mini-applications in Appwrite with their own endpoints.
Each function can have many deployments, which can be thought of as versions of the mini-application.
While we recommend you create deployments through [automatic Git deployments](/docs/products/functions/deploy-from-git),
you can also create deployments manually or through the Appwrite CLI.
# CLI {% #cli %}
{% info title="CLI setup" %}
Before you can deploy with the Appwrite CLI, make sure you've [installed and initialized](/docs/tooling/command-line/installation) the CLI.
{% /info %}
To deploy with the Appwrite CLI, your function must be added to `appwrite.json`.
Use the `appwrite init function` method to create a starter function, then paste in your function code.
```bash
appwrite init function
```
Edit the generated code, add dependencies, and deploy the function using the following command:
```bash
appwrite deploy function
```
## Overwrite warning {% #overwrite-warning %}
If you made changes in the Appwrite Console that are different from your `appwrite.json`, using the CLI deploy command will overwrite your console changes. Update your `appwrite.json` manually before deploying.
## Configure CLI deployments {% #configure-cli-deployments %}
If you need to target a different project, API endpoint, change the path or entry point of your function, or update any of the other configuration options,
you can do so by editing the `appwrite.json` file.
{% arrow_link href="/docs/tooling/command-line/deployment#function-options" %}
Learn more about appwrite.json
{% /arrow_link %}
# Manual Deployment {% #manual %}
You can upload your functions to be deployed using the Appwrite Console. The example below shows a simple Node.js function.
```text
.
├── package.json
└── index.js
```
First, navigate inside the folder that contains your dependency file. Package your code files into the `.tar.gz` format:
{% multicode %}
```bash
tar --exclude code.tar.gz -czf code.tar.gz .
```
```cmd
tar --exclude code.tar.gz -czf code.tar.gz .
```
```powershell
tar --exclude code.tar.gz -czf code.tar.gz .
```
{% /multicode %}
Next, navigate to your Appwrite Console and upload the function.
1. Navigate to the function you want to deploy.
2. Click **Create deployment**.
3. Select the **Manual** tab.
4. Input the entry point of your function under **Entrypoint**. For the example above, it would be `index.js`.
5. Upload `code.tar.gz`.
6. Select **Activate deployment after build**.
7. Click **Create**.
# Debugging {% #debugging %}
- If you updated your function's configuration but the deployment is not working as expected,
you may need to first redeploy your function before the changes take effect.
- If you notice your function is missing dependencies during build or at runtimes, update it's build settings.
Navigate to **Functions** > your function > **Settings** > **Configuration** > **Build settings**.
These commands will be ran before the function is built and can be used to install dependencies.
- If you're missing some code files at build time, make sure they are included in the **Root directory**.
Only files in the root directory folder will be copied to the executor.

View File

@@ -1,134 +0,0 @@
---
layout: article
title: Deployment
description: Efficiently deploy your serverless functions with Appwrite. Explore deployment options, strategies, and best practices for seamless function execution.
---
Appwrite Functions are mini-applications in Appwrite with their own endpoints. Each function can have many deployments, which can be thought of as versions of the mini-application.
Functions can be created and deployed in different ways to meet your unique development habits. You can automatically deploy Appwrite Functions from source control, build your own deployment pipelines using the Appwrite CLI, or upload code files manually. Here's everything you need to know to deploy your first Appwrite Function.
# Git {% #git %}
The recommended way to manage your Appwrite Function deployments is to use a version control system, like Git. This offers simple versioning and collaboration that will easily fit into the rest of your development workflow.
{% only_dark %}
![Create project screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/functions/template.png)
{% /only_light %}
## Create function {% #create-function %}
Before deploying your function with Git, create a new function attached to your Git repo.
1. Navigate to **Functions** from the side bar of the Appwrite Console.
2. Click **Create function**.
3. When asked to **Choose your source**, under **Connect Git repository**, select your provider.
4. Search for the Git repository that holds your function and click **connect**.
5. Select a production branch. New commits pushed to the production branch will be **automatically activated**. Commits to any other branch will still be deployed, but not be activated.
6. Input the root directory of the function inside the repository.
7. If you don't want deploy comments to be made on your pull requests or commits, select **Silent mode**.
8. Name your function, select a runtime that matches your function, and enter entrypoint.
9. If you have build steps, like installing dependencies, input the commands into the **Build settings** heading's **Command** field. For example, `npm install && npm build`.
10. Finally, configure the execute permissions of the function.
## Deploy {% #deploy %}
1. Using Git, checkout the branch you configured as the production branch when creating the Appwrite Function.
2. Create a new commit.
3. Push your new commit.
4. A new deployment will be automatically created, built and activated.
# CLI {% #cli %}
{% info title="CLI setup" %}
Before you can deploy with the Appwrite CLI, make sure you've [installed and initialized](/docs/tooling/command-line/installation) the CLI.
{% /info %}
To deploy with the Appwrite CLI, your function must be added to `appwrite.json`. Use the `appwrite init function` method to create a starter function, then paste in your function code.
```bash
appwrite init function
```
Edit the generated code, add dependencies, and deploy the function using the following command:
```bash
appwrite deploy function
```
## Overwrite warning {% #overwrite-warning %}
If you made changes in the Appwrite Console that are different from your `appwrite.json`, using the CLI deploy command will overwrite your console changes. Update your `appwrite.json` manually before deploying.
# Manual Deployment {% #manual %}
You can upload your functions to be deployed using the Appwrite Console. The example below shows a simple Node.js function.
```text
.
├── package.json
└── index.js
```
First, navigate inside the folder that contains your dependency file. Package your code files into the `.tar.gz` format:
{% multicode %}
```bash
tar --exclude code.tar.gz -czf code.tar.gz .
```
```cmd
tar --exclude code.tar.gz -czf code.tar.gz .
```
```powershell
tar --exclude code.tar.gz -czf code.tar.gz .
```
{% /multicode %}
Next, navigate to your Appwrite Console and upload the function.
1. Navigate to the function you want to deploy.
2. Click **Create deployment**.
3. Select the **Manual** tab.
4. Input the entry point of your function under **Entrypoint**. For the example above, it would be `index.js`.
5. Upload `code.tar.gz`.
6. Select **Activate deployment after build**.
7. Click **Create**.
# Domains {% #domains %}
Each deployed function can have its own domain. By default, one is generated for each of your functions.
To find or add a domain:
1. Navigate to the Appwrite Console's **Functions** page.
2. Navigate to the **Domains** tab.
3. Click on **Create domain**.
4. Input your domain and click **Next**.
5. Copy the **CNAME** record and add it to your domain registrar.
6. Click **Go to Console** and wait for verification and certificate generation.
DNS records can take up to 48 hours to propagate.
# Debugging Build {% #debugging %}
After deploying a function, you can find the status and build logs in the Appwrite Console.
1. Navigate to **Functions**.
2. Open the function you wish to inspect.
3. Under the **Deployments** tab, find the status of the current and previous deployments.
4. Access build logs by clicking the **Build logs** button.
# Redeploy Builds {% #redeploy %}
After updating the configuration, redeploy your function for changes to take effect. You can also redeploy to retry failed builds.
1. Navigate to **Functions**.
2. Open the function you wish to inspect.
3. Under the **Deployments** tab, find the status of the current active deployment.
4. Redeploy by clicking the triple-dots beside an execution, and hitting the **Redeploy** button.
Redeployment behavior varies depending on how the initial deployment was created.

View File

@@ -0,0 +1,48 @@
---
layout: article
title: Deployments
description: Efficiently deploy your serverless functions with Appwrite. Explore deployment options, strategies, and best practices for seamless function execution.
---
Each function can have many deployments, which can be thought of as versions of the mini-application.
Functions can be created and deployed in different ways to meet your unique development habits.
# Deployment status {% #deployment-status %}
Throughout the life cycle of a deployment, it could have the following status.
{% table %}
* Status
* description
---
* `active`
* The deployment is built and currently activated and ready to be executed. A function can have one active deployment and deployment a must be active before being executed.
---
* `ready`
* A deployment is built, but is not activated. `ready` deployments can be activated to replace the current active deployment.
---
* `building`
* A deployment is being built. Check the [build log](#build-logs) for more detailed logs.
---
* `failed`
* A deployment was not successful. Check the [build log](#build-logs) for detailed logs for debugging.
{% /table %}
# Update deployment {% #update-deployment %}
Some Function settings require redeploying your function to be reflected in your active deployment.
When you update a function by changing it's **Git settings**, **Build settings**, and **Environment variables**,
you need to redeploy your function before they take effect.
# Build logs {% #build-logs %}
When you build a deployment, the logs generated will be saved for debugging purposes.
You can find build logs by navigating to the **deployments** tab of your function, clicking the three-dots menu beside, and click **Logs**.
# Redeploy {% #redeploy %}
After updating the configuration, redeploy your function for changes to take effect. You can also redeploy to retry failed builds.
1. Navigate to **Functions**.
2. Open the function you wish to inspect.
3. Under the **Deployments** tab, find the status of the current active deployment.
4. Redeploy by clicking the triple-dots beside an execution, and hitting the **Redeploy** button.
Redeployment behavior varies depending on how the initial deployment was created.

View File

@@ -1,6 +1,6 @@
---
layout: article
title: Development
title: Develop Appwrite Functions
description: Master serverless function development with Appwrite. Learn how to write and test functions locally, debug code, and optimize for efficient execution.
---
@@ -12,11 +12,14 @@ There is a clear lifecycle for all Appwrite Functions, from beginning to end.
Here's everything that happens during a function execution.
1. The function is invoked.
2. Appwrite passes in request information like headers, body or path through the `context.req` object.
3. The runtime executes the code you defined, you can log through the `context.log()` or `context.error()` methods.
4. Function terminates when you return results using `return context.res.send()`, `return context.res.json()` or similar.
1. The active [deployment](/docs/products/functions/deployments)'s executor will handle the request.
1. The Executor passes in request information like headers, body or path through the `context.req` object of your exported function.
1. The runtime executes the code you defined, you can log through the `context.log()` or `context.error()` methods.
1. Function terminates when you return results using `return context.res.send()`, `return context.res.json()` or similar.
## Entrypoint {% #entrypoint %}
You'll find all of these steps in a simple function like this.
Notice the exported entry point that the executor will call.
{% multicode %}
```server-nodejs
@@ -926,7 +929,7 @@ namespace runtime {
```
{% /multicode %}
To get the different response types, set one of the following query parameters in the [generated domain](/docs/products/functions/deployment#domains) of your function.
To get the different response types, set one of the following query parameters in the [generated domain](/docs/products/functions/domains) of your function.
| Type | Query Param | Example |
|----------|-----------------|-------------------------------------------------------------|
@@ -1088,7 +1091,6 @@ You can access these logs through the following steps.
# Accessing environment variables {% #environment-variables %}
If you need to pass constants or secrets to Appwrite Functions, you can use environment variables.
Environmental variables can be global, or function-specific.
{% info title="Appwrite API keys" %}
If your function is using an Appwrite SDK with an API key, this API key needs to be generated and passed in manually. API keys are not passed by default for security reasons.
@@ -1103,22 +1105,9 @@ If your function is using an Appwrite SDK with an API key, this API key needs to
| `APPWRITE_FUNCTION_RUNTIME_NAME` | The runtime of the running function. |
| `APPWRITE_FUNCTION_RUNTIME_VERSION` | The runtime version of the running function. |
## Function-level environment variables {% #function-variables %}
Function-level environment variables will only be accessible in the function they belong to.
Function-level environment variables will override project-level variables when they have conflicting names.
1. In Appwrite Console, navigate to **Functions**.
2. Click to open a function you wish to add variables to.
3. Under the **Settings** tab, navigate to **Environment variables**.
4. Create an environment variable by clicking **Create variable**, using the **Editor**, or import new variables through a `.env` file.
## Project-level variables {% #project-variables %}
Project-level variables are accessible to all Appwrite Functions in your project.
Function-level environment variables will override project-level variables when they have conflicting names.
In the Appwrite Console, navigate to your project's **Settings** page.
Navigate to **Global variables** section.
Create an environment variable by clicking **Create variable**, using the **Editor**, or import new variables through a `.env` file.
{% arrow_link href="/docs/products/functions/functions#environment-variables" %}
Learn to add variables to you function
{% /arrow_link %}
You can access the environment variables through the systems library of each language.
@@ -1216,6 +1205,14 @@ namespace runtime {
{% /multicode %}
# Dependencies {% #dependencies %}
To install your dependencies before your function is built,
you should add the relevant install command to the top your function's **Build setting** > **Commands**.
You can find this setting under **Functions** > your function > **Settings** > **Configuration** > **Build settings**.
Make sure to include dependency files like `package.json`, `composer.json`, `requirements.txt`, etc. in your function's configured [root directory](/docs/products/functions/deploy-from-git#root-directory).
Do not include the dependency folders like `node_modules`, `vendor`, etc. in your function's root directory.
The dependencies installed for your local OS may not work in the executor environments
Your function's dependencies should be managed by the package manager of each language.
By default, we include the following package managers in each runtime.
@@ -1298,9 +1295,6 @@ By default, we include the following package managers in each runtime.
* N/A
{% /table %}
To install your dependencies before your function is built, you should add the relevant install command to the top your function's **Build setting** > **Commands**.
# Using Appwrite in a function {% #using-appwrite %}
Appwrite can be used in your functions by adding the relevant SDK to your function's dependencies.
Authenticating with Appwrite is done via an API key or a JWT token.
@@ -1977,7 +1971,7 @@ public class Main {
```
{% /multicode %}
# Code splitting {% #code-splitting %}
# Code structure {% #code-structure %}
As your functions grow, you may find yourself needing to split your code into multiple files. This helps you keep your codebase maintainable and easy to read. Here's how you can accomplish code splitting.
@@ -2184,20 +2178,3 @@ public class Main {
```
{% /tabsitem %}
{% /tabs %}
# Upgrade {% #upgrade %}
Appwrite Functions received major updates in Appwrite version 1.4.
Your old function from 1.3 will continue to work, but it can't be updated directly to a 1.4 function.
You need to create a new function following 1.4 syntax.
After you've created your new function, point your application code to the new function and delete the old function.
Here's a checklist of things you need to know.
1. The parameter passed into functions has changed. `req` and `res` has been replaced by `context`, which contains new logger methods. [Learn about context](/docs/products/functions/development#context-object).
2. To improve privacy and logging reliability, we provide new `context.log()` and `context.error()` functions. You can no longer use native logging methods. [Learn about logging](/docs/products/functions/development#logging).
3. The old way of `req.variables` has been deprecated. You can now access variables passed into each function as environment variables. [Learn about environment variables](/docs/products/functions/development#environment-variables).
4. The `req` object has been updated to use terminology consistent with typical HTTP concepts. You'll now find familiar concepts like headers, body, HTTP methods, and others. [Learn about request](/docs/products/functions/development#request).
5. The response object has been updated. You can now specify headers, as well as use new methods like return redirects or empty responses. [Learn about response](/docs/products/functions/development#response).
6. Now, you must return a response such as return `context.res.send("")`. This prevents confusing errors when functions are terminated prematurely before a response is sent. [Learn about response](/docs/products/functions/development#response).
7. Some variables about how a function was triggered are now found in the context.req object as headers.

View File

@@ -0,0 +1,40 @@
---
layout: article
title: Domains
description: Execute Appwrite Functions through domains using standard HTTP GET, POST, or other request methods to serve static, JSON, HTML, or other content.
---
Each deployed function can have its own domain, generated or developer defined.
You can use this domain to execute Appwrite Functions through HTTP methods.
You can use common practices like using paths, query parameters, headers, HTTP methods, formdata,
and all the typical HTTP concepts to implement Appwrite Functions.
Appwrite generates TLS certificates to enforce HTTPS on all Appwrite Functions domains, generated or custom.
These domains are safe to use and access in production.
{% arrow_link href="/docs/products/functions/develop" %}
Learn about Function development
{% /arrow_link %}
# Generated domains {% #generated-domains %}
1. In the Appwrite Console's sidebar, click **Functions**.
1. Under the **Domains** tab, you'll find the generated domain from Appwrite.
The domain usually has this format.
```bash
https://64d4d22db370ae41a32e.appwrite.global
```
# Add a custom domain {% #add-a-custom-domain %}
1. Navigate to the Appwrite Console's **Functions** page.
2. Navigate to the **Domains** tab.
3. Click on **Create domain**.
4. Input your domain and click **Next**.
5. Copy the **CNAME** record and add it to your domain registrar.
6. Click **Go to console** and wait for verification and certificate generation.
DNS records can take up to 48 hours to propagate.
When both **VERIFICATION STATUS** and **CERTIFICATE STATUS** are green, the new domain is ready to use.

View File

@@ -0,0 +1,493 @@
---
layout: article
title: Execution
description: Understand serverless function execution in Appwrite. Explore how triggers, events, and data flow enable dynamic execution of your code.
difficulty: beginner
readtime: 3
---
Appwrite Functions can be executed in several ways.
Executions can be invoked through the Appwrite SDK and visiting its REST endpoint. Functions can also be triggered by events and scheduled executions.
Here are all the different ways to consume your Appwrite Functions.
# Domains {% #domains %}
You can execute a function through HTTP requests, using a browser or by sending an HTTP request.
1. In the Appwrite Console's sidebar, click **Functions**.
1. Under **Execute access**, set the access to `Any` so that anyone can execute the function. You will use [JWTs](/docs/products/auth/jwt) to authenticate users.
1. Under the **Domains** tab, you'll find the generated domain from Appwrite and your custom domains. [Learn about adding a custom domain](/docs/products/functions/domains).
```bash
https://64d4d22db370ae41a32e.appwrite.global
```
When requests are made to this domain, whether through a browser or through an HTTP requests,
the request information like request URL, request headers, and request body will be passed to the function.
```bash
curl -X POST https://64d4d22db370ae41a32e.appwrite.global \
-H "X-Custom-Header: 123" \
-H "x-appwrite-user-jwt: <YOUR_JWT_KEY>" \
-H "Content-Type: application/json" \
-d '{"data":"this is json data"}'
```
Notice how a `x-appwrite-user-jwt` header is passed in the request, you will use this to authenticate users.
[Learn more about JWTs](/docs/products/auth/jwt).
This unlocks ability for you to develop custom HTTP endpoints with Appwrite Functions.
It also allows accepting incoming webhooks for handling online payments, hosting social platform bots, and much more.
# SDK {% #sdk %}
You can invoke your Appwrite Functions directly from the [Appwrite SDKs](/docs/sdks).
{% tabs %}
{% tabsitem #client title="Client SDKs" %}
{% multicode %}
```js
import { Client, Functions } from "appwrite";
const client = new Client();
const functions = new Functions(client);
client
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
;
const promise = functions.createExecution(
'<FUNCTION_ID>', // functionId
'<BODY>', // body (optional)
false, // async (optional)
'<PATH>', // path (optional)
'GET', // method (optional)
{} // headers (optional)
);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});
```
```dart
import 'package:appwrite/appwrite.dart';
void main() { // Init SDK
Client client = Client();
Functions functions = Functions(client);
client
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
;
Future result = functions.createExecution(
functionId: '<FUNCTION_ID>',
body: '<BODY>', // optional
xasync: false, // optional
path: '<PATH>', // optional
method: 'GET', // optional
headers: {}, // optional
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}
```
```swift
import Appwrite
let client = Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let functions = Functions(client)
let execution = try await functions.createExecution(
functionId: "<FUNCTION_ID>",
body: "<BODY>", // optional
async: xfalse, // optional
path: "<PATH>", // optional
method: "GET", // optional
headers: [:] // optional
)
```
```kotlin
import io.appwrite.Client
import io.appwrite.services.Functions
val client = Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val functions = Functions(client)
val response = functions.createExecution(
functionId = "<FUNCTION_ID>",
body = "<BODY>", // optional
async = false, // optional
path = "<PATH>", // optional
method = "GET", // optional
headers = mapOf( "a" to "b" ) // optional
)
```
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.createExecution(
"<FUNCTION_ID>", // functionId
"<BODY>", // body (optional)
false, // async (optional)
"<PATH>", // path (optional)
"GET", // method (optional)
mapOf( "a" to "b" ) // headers (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
```
{% /multicode %}
{% /tabsitem %}
{% tabsitem #server title="Server SDKs" %}
{% multicode %}
```js
const sdk = require('node-appwrite');
// Init SDK
const client = new sdk.Client();
const functions = new sdk.Functions(client);
client
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
;
const promise = functions.createExecution(
'<FUNCTION_ID>', // functionId
'<BODY>', // body (optional)
false, // async (optional)
'<PATH>', // path (optional)
'GET', // method (optional)
{} // headers (optional)
);
promise.then(function (response) {
console.log(response);
}, function (error) {
console.log(error);
});
```
```deno
import * as sdk from "https://deno.land/x/appwrite/mod.ts";
// Init SDK
let client = new sdk.Client();
let functions = new sdk.Functions(client);
client
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
;
const promise = functions.createExecution(
'<FUNCTION_ID>', // functionId
'<BODY>', // body (optional)
false, // async (optional)
'<PATH>', // path (optional)
'GET', // method (optional)
{} // headers (optional)
);
promise.then(function (response) {
console.log(response);
}, function (error) {
console.log(error);
});
```
```php
<?php
use Appwrite\Client;
use Appwrite\Services\Functions;
$client = new Client();
$client
->setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
->setProject('<YOUR_PROJECT_ID>') // Your project ID
;
$functions = new Functions($client);
$result = $functions->createExecution(
functionId: '<FUNCTION_ID>',
body: '<BODY>', // optional
async: false, // optional
path: '<PATH>', // optional
method: 'GET', // optional
headers: [] // optional
);
```
```python
from appwrite.client import Client
from appwrite.services.functions import Functions
client = Client()
(client
.set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
)
functions = Functions(client)
result = functions.create_execution(
function_id = '<FUNCTION_ID>',
body = '<BODY>', # optional
async = False, # optional
path = '<PATH>', # optional
method = 'GET', # optional
headers = {} # optional
)
```
```ruby
require 'appwrite'
include Appwrite
client = Client.new
.set_endpoint('https://cloud.appwrite.io/v1') # Your API Endpoint
.set_project('<YOUR_PROJECT_ID>') # Your project ID
functions = Functions.new(client)
response = functions.create_execution(
function_id: '<FUNCTION_ID>',
body: '<BODY>', # optional
async: false, # optional
path: '<PATH>', # optional
method: 'GET', # optional
headers: {} # optional
)
puts response.inspect
```
```csharp
using Appwrite;
using Appwrite.Services;
using Appwrite.Models;
var client = new Client()
.SetEndPoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>"); // Your project ID
var functions = new Functions(client);
Execution result = await functions.CreateExecution(
functionId: "<FUNCTION_ID>"
body: "<BODY>" // optional
async: false // optional
path: "<PATH>" // optional
method: "GET" // optional
headers: [object]); // optional
```
```dart
import 'package:dart_appwrite/dart_appwrite.dart';
void main() { // Init SDK
Client client = Client();
Functions functions = Functions(client);
client
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
;
Future result = functions.createExecution(
functionId: '<FUNCTION_ID>',
body: '<BODY>', // optional
xasync: false, // optional
path: '<PATH>', // optional
method: 'GET', // optional
headers: {}, // optional
);
result
.then((response) {
print(response);
}).catchError((error) {
print(error.response);
});
}
```
```kotlin
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.createExecution(
"<FUNCTION_ID>", // functionId
"<BODY>", // body (optional)
false, // async (optional)
"<PATH>", // path (optional)
"GET", // method (optional)
mapOf( "a" to "b" ) // headers (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
```
```java
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Functions;
Client client = new Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Functions functions = new Functions(client);
functions.createExecution(
"<FUNCTION_ID>", // functionId
"<BODY>", // body (optional)
false, // async (optional)
"<PATH>", // path (optional)
"GET", // method (optional)
mapOf( "a" to "b" ) // headers (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
System.out.println(result);
})
);
```
```swift
import Appwrite
let client = Client()
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let functions = Functions(client)
let execution = try await functions.createExecution(
functionId: "<FUNCTION_ID>",
body: "<BODY>", // optional
async: xfalse, // optional
path: "<PATH>", // optional
method: "GET", // optional
headers: [:] // optional
)
```
{% /multicode %}
{% /tabsitem %}
{% /tabs %}
# Console {% #console %}
Another easy way to test a function is directly in the Appwrite Console.
You test a function by hitting the **Execute now** button, which will display with modal below.
You'll be able to mock executions by configuring the path, method, headers, and body.
{% only_dark %}
![Create project screen](/images/docs/functions/execution/dark/execute-function.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/functions/execution/execute-function.png)
{% /only_light %}
# Events {% #events %}
Changes in Appwrite emit events. You can configure Functions to be executed in response to these events.
1. In Appwrite Console, navigate to **Functions**.
2. Click to open a function you wish to configure.
3. Under the **Settings** tab, navigate to **Events**.
4. Add one or multiple events as triggers for the function.
5. Be careful to avoid selecting events that can be caused by the function itself. This can cause the function to trigger its own execution, resulting in infinite recursions.
In these executions, the event that triggered the function will be passed as the header `x-appwrite-event` to the function.
The `request.body` parameter will contain the event data. [Learn more about events](/docs/advanced/platform/events).
You can use one of the following events.
{% accordion %}
{% accordion_item title="Authentication" %}
{% partial file="auth-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Databases" %}
{% partial file="databases-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Storage" %}
{% partial file="storage-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Functions" %}
{% partial file="functions-events.md" /%}
{% /accordion_item %}
{% accordion_item title="Messaging" %}
{% partial file="messaging-events.md" /%}
{% /accordion_item %}
{% /accordion %}
# Schedule {% #schedule %}
Appwrite supports scheduled function executions. You can schedule executions using [cron expressions](https://en.wikipedia.org/wiki/Cron) in the settings of your function. Cron supports recurring executions as frequently as **every minute**.
Here are some cron expressions for common intervals:
| Cron Expression | Schedule |
| ---------------- | --------------------- |
| `*/15 * * * *` | Every 15 minutes |
| `0 * * * *` | Every Hour |
| `0 0 * * *` | Every day at 00:00 |
| `0 0 * * 1` | Every Monday at 00:00 |
# Permissions {% #permission %}
Appwrite Functions can be executed using Client or Server SDKs. Client SDKs must be authenticated with an account that has been granted execution [permissions](/docs/advanced/platform/permissions) on the function's settings page. Server SDKs require an API key with the correct scopes.
If your function has a generated or custom domain, executions are not authenticated. Anyone visiting the configured domains will be considered a guest, so make sure to give `Any` execute permission in order for domain executions to work. If you need to enforce permissions for functions with a domain, use authentication methods like JWT.

View File

@@ -0,0 +1,79 @@
---
layout: article
title: Execution
description: Understand serverless function execution in Appwrite. Explore how triggers, events, and data flow enable dynamic execution of your code.
---
Each time an Appwrite Function runs, an **execution** is created.
Each execution has a unique ID. If [you enable execution logs](/docs/products/functions/functions#execution-logs) in your function,
you can find function executions logged in the **Executions** tab.
# Execution table {% #execution-table %}
In your function's **Executions** tab, you will see a table of your recent executions.
Here's the information shown on this table.
{% table %}
* Column
* Description
---
* EXECUTION ID
* Unique identifier for each execution
---
* STATUS
* The current status of the execution
---
* CREATED
* Timestamp of when the execution was created
---
* TRIGGER
* The [platform event](/docs/advanced/platform/events) that triggered the execution
---
* METHOD
* The HTTP method used to create the execution
---
* PATH
* The URL path the function execution was called with
---
* DURATION
* The time taken for the execution
{% /table %}
## Execution status {% #execution-status %}
Each execution can have one of the follow status.
{% table %}
* Status
* description
---
* `processing`
* The function execution has begun and has not finished.
---
* `completed`
* The function executed successfully.
---
* `failed`
* The function execution was not successful.
{% /table %}
# Execution details {% #execution-details %}
When you click on an execution, you will be taken to an execution detail screen.
{% only_dark %}
![Execution details screen](/images/docs/functions/execution/dark/execute-function.png)
{% /only_dark %}
{% only_light %}
![Execution details screen](/images/docs/functions/execution/execute-function.png)
{% /only_light %}
You can find both request and response details.
Request and response body are **not logged** to protect user privacy.
This ensures that developers do not see user data by default and no sensitive data is retained.
If you need to log debug data or audit logs, you can use [function logging features](/docs/products/functions/develop#logging)
to explicitly log the information you need.
# Log retention {% #log-retention %}
Logs are not retained forever in order to be compliant with GDPR and other data privacy standards.
Starter plan organizations will retain logs for 24 hours, Pro plan organizations will retain logs for 7 days.
If you need longer log retention, you can log to an Appwrite collection.
Remember to configure proper permissions and implement Appwrite Functions or other scheduled tasks to expire and clean up logs.

View File

@@ -0,0 +1,180 @@
---
layout: article
title: Functions
description: Learn what an Appwrite Function can do for you and how to create a new Appwrite Function
---
Each Appwrite Function is a piece of developer defined code that can be executed on demand.
When you create a new Appwrite Function, you select a name, ID, and [runtime language](/docs/products/functions/runtimes).
Each time a function's code is updated, a [deployment](/docs/products/functions/deployments) is created, which is like a version of a function.
Each function has a single active deployment, which is the version of code that's executed when it's called.
You can update the Appwrite Function's code by creating new [deployments](/docs/products/functions/deploy-from-git).
You can also switch between different deployments by activating them.
# Create function {% #create-function %}
You can create Appwrite Functions in three different ways.
{% tabs %}
{% tabsitem #git title="Git" %}
It's recommended to create functions that are connected to version control.
This lets you track your code using Git, which makes it easy to integrate Appwrite Functions into your existing code base.
1. In the Appwrite Console's sidebar, click **Functions**.
2. Click **Create function**.
3. Connect your project to your Git provider. You will be asked to authorize Appwrite and grant access to some resources necessary for the Git deployments to work.
4. If you already have a repository containing an Appwrite Function, select it under **Connect Git repository**.
If you need to create a new function, select a **Quick start** template or search for more templates under **All templates**.
5. Follow the wizard to configure your new Appwrite Function.
{% only_dark %}
![Create project screen](/images/docs/functions/dark/template.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/functions/template.png)
{% /only_light %}
{% /tabsitem %}
{% tabsitem #manual title="Manual" %}
You can also create Appwrite Functions manually by uploading your code in a zipped file.
In the **Create Function** modal, click **create a function manually** at the bottom to switch to manual create wizard.
You will be asked to upload a zip file with your code.
First, navigate inside the folder that contains your dependency file.
For example, when you list the content of your folder for a Node.js function, it will look like this.
```text
.
├── package.json
└── index.js
```
Package your code files into the `.tar.gz` format. **Don't include your dependencies folder**, such as `node_modules`.
{% multicode %}
```bash
tar --exclude code.tar.gz -czf code.tar.gz .
```
```cmd
tar --exclude code.tar.gz -czf code.tar.gz .
```
```powershell
tar --exclude code.tar.gz -czf code.tar.gz .
```
{% /multicode %}
Upload your `.tar.gz` file and specify the entry point of your function, in this case `index.js`.
Remember to specify the build commands for your function to install dependencies.
{% /tabsitem %}
{% tabsitem #cli title="CLI" %}
You can create functions using the CLI, without needing to access the Console.
Before you can deploy with the Appwrite CLI, make sure you've [initialized the CLI.](/docs/tooling/command-line/installation)
To deploy with the Appwrite CLI, your function must be added to your `appwrite.json` config file.
Use the `appwrite init function` method to create a starter function, then paste in your function code.
```bash
appwrite init function
```
The init command will initialize a folder with a starter function code.
To deploy the generated code, add dependencies, and deploy the function using the following command:
```bash
appwrite deploy function
```
{% /tabsitem %}
{% /tabs %}
# Configuration {% #configuration %}
## Name {% #name %}
You can update the name of your function by navigating to your function > **Settings** > **Name**.
Update your function's name and click **Update**.
Functions are executed using it's ID, updating name does not affect references to your function.
## Runtime {% #runtime %}
Every deployment of a function uses the same runtime.
You can update the runtime of a function by navigating to function > **Settings** > **Runtime**.
Select a new runtime and click **Update**.
{% info title="Redeployment required" %}
This change requires your function to be redeployed to take effect.
{% /info %}
## Build configuration {% #build-configuration %}
You can update the entrypoint file and build settings of your function by navigating to your function >
**Settings** > **Configuration**.
{% info title="Redeployment required" %}
This change requires your function to be redeployed to take effect.
{% /info %}
The **Entrypoint** refers to the file imported and executed by the function executor.
It must export a [valid function entrypoint function](/docs/products/functions/develop#entrypoint).
It's recommended you use one of the [starter function templates](/docs/products/functions/templates) and edit from there.
Under **Build settings**, you can update your build commands.
These are terminal commands that will be executed in the runtime containers in the build step of the deployment process.
## Git integration {% #git-integration %}
You can update the entrypoint file and build settings of your function by navigating to your function > **Settings** > **Configuration**.
{% info title="Redeployment required" %}
This change requires your function to be redeployed to take effect.
{% /info %}
Under **Git settings** you can configure the Git repository and branch that your function is connected to.
Build commands and entrypoint are executed relative to the configured **Root directory** of your Git respository.
By default, Appwrite will create comments in PRs to your connected branch. You can use **Silent mode** to suppress these comments.
## Execution logs {% #execution-logs %}
You can enable and disable execution logs for your functions by navigating to your function > **Settings** > **Execution logs**
In production environments, you can choose to disable execution logs to protect user privacy.
## Execute access {% #execution-access %}
You can control who can execute your functions
by navigating to your function > **Settings** > **Execute access**
and granting access to select [permission roles](/docs/advanced/platform/permissions#permission-roles).
If this is left empty, no user can execute your function.
Server SDKs, scheduled executions, and event function triggers don't require permissions to execute a function.
## Events{% #events %}
Functions can be triggered by [platform events](/docs/advanced/platform/events) which reflect changes
that occur in your Appwrite project.
You can configure events triggers by navigating to your function > **Settings** > **Events**.
## Schedule {% #schedule %}
Appwrite supports scheduled function executions.
You can schedule executions using [cron expressions](https://en.wikipedia.org/wiki/Cron) in the settings of your function.
Cron supports recurring executions as frequently as **every minute**.
You can configure events triggers by navigating to your function > **Settings** > **Schedule**.
Here are some cron expressions for common intervals:
| Cron Expression | Schedule |
| ---------------- | --------------------- |
| `*/15 * * * *` | Every 15 minutes |
| `0 * * * *` | Every Hour |
| `0 0 * * *` | Every day at 00:00 |
| `0 0 * * 1` | Every Monday at 00:00 |
## Environment variables {% #environment-variables %}
Set static environment variables that will be passed to your function
by navigating to your function > **Settings** > **Environment variables**.
You can also configure global variables that apply to all your functions in the **Settings** of your project.
{% info title="Redeployment required" %}
This change requires your function to be redeployed to take effect.
{% /info %}
You can access environment variables inside functions using your [runtime language's system methods](/docs/products/functions/develop#environment-variables).
## Timeout {% #timeout %}
You can limit the execution time of your function by navigating to your function > **Settings** > **Timeout**.
There is a system wide maximum timeout of 900 seconds (15 minutes).

View File

@@ -27,7 +27,7 @@ You can find the code used by the starter template in your newly created Git rep
Each push to your Git repo will trigger a new deployment.
## Execute {% #execute %}
You can execute your Appwrite Function through [many different triggers](/docs/products/functions/execution).
You can execute your Appwrite Function through [many different triggers](/docs/products/functions/execute).
The easiest way to execute your first function is to use the Appwrite Console.
{% only_dark %}
@@ -46,12 +46,20 @@ The easiest way to execute your first function is to use the Appwrite Console.
# Explore {% #explore %}
Use this your first function as a springboard to explore the flexible and powerful features of Appwrite Functions.
| Explore | |
|-------------------------------------------------------------------------|----------------------------------------------------|
| [Develop](/docs/products/functions/development) | Learn about how to parse request, build responses, and log to Console in Appwrite Functions. |
| [Response](/docs/products/functions/development#response) | Learn about function response types, like text, json, html, redirect, and empty responses.|
| [Deploy](/docs/products/functions/deployment) | Learn to deploy Appwrite Functions from Git, the CLI, or manual uploads. |
| [Execute](/docs/products/functions/execution) | Appwrite Functions can be executed in a variety of ways, like SDK, domains, schedules, events, and more. Learn about executions. |
| [Domains](/docs/products/functions/execution#domains) | Each Appwrite Function can be executed through it's own domain, generated or custom. Learn about domains. |
| [Runtime](/docs/products/functions/runtimes) | Appwrite supports over a dozen runtimes to meet your preferences. Learn about runtimes. |
| [Examples](/docs/products/functions/examples) | Enjoy learning from examples? Take a look at these examples functions. |
{% cards %}
{% cards_item href="/docs/products/functions/templates" title="Template" %}
Get a template function up and running with a single click.
{% /cards_item %}
{% cards_item href="/docs/products/functions/develop" title="Develop" %}
Learn about developing your own Appwrite Function.
{% /cards_item %}
{% cards_item href="/docs/products/functions/deploy-from-git" title="Deploy" %}
Learn to deploy Appwrite Functions from Git.
{% /cards_item %}
{% cards_item href="/docs/products/functions/execute" title="Execute" %}
Explore the different ways an Appwrite Function can be executed.
{% /cards_item %}
{% cards_item href="/docs/products/functions/runtimes" title="Runtimes" %}
Write Appwrite Functions in your favorite language.
{% /cards_item %}
{% /cards %}

View File

@@ -4,14 +4,17 @@ title: Runtimes
description: Choose the right runtime environment for your serverless functions in Appwrite. Explore available runtimes, dependencies, and runtime-specific considerations.
---
Appwrite Functions supports an extensive list of runtimes to meet your unique tech preferences. Not all runtimes are available on Appwrite Cloud yet. Check the [Cloud Support](#cloud-support) list to know which ones are available on Appwrite Cloud.
Appwrite Functions supports an extensive list of runtimes to meet your unique tech preferences. Not all runtimes are available on Appwrite Cloud yet. Check the list below to know which ones are available on Appwrite Cloud.
# Available runtimes {% #available-runtimes %}
Below is a list of available Functions runtimes. The Appwrite team continually adds support for new runtimes.
While still in beta, Appwrite Cloud has limited support for Cloud runtimes. As we continue to improve our Cloud offering, we will add support for more runtimes.
{% tabs %}
{% tabsitem #all title="All runtimes" %}
{% table %}
* &nbsp; {% width=48 %}
* Name {% width=120 %}
* Name {% width=100 %}
* Versions
* Architectures
---
@@ -111,15 +114,61 @@ Below is a list of available Functions runtimes. The Appwrite team continually a
* x86 / arm64 / armv7 / armv8
---
{% /table %}
{% /tabsitem %}
# Cloud support {% #cloud-support %}
While still in beta, Appwrite Cloud has limited support for Cloud runtimes. As we continue to improve our Cloud offering, we will add support for more runtimes. Until then, these are the runtimes that you can use on Appwrite Cloud:
- `node-16.0`
- `node-18.0`
- `php-8.0`
- `ruby-3.0`
- `python-3.9`
- `python-3.12`
- `dart-2.17`
{% tabsitem #available-on-cloud title="Available on Cloud" %}
{% table %}
* &nbsp; {% width=48 %}
* Name {% width=120 %}
* Versions
* Architectures
---
* {% icon icon="node_js" size="l" /%}
* [Node.js](https://hub.docker.com/r/openruntimes/node/tags)
* `node-14.5`
`node-16.0`
`node-18.0`
`node-19.0`
`node-20.0`
* x86 / arm64 / armv7 / armv8
---
* {% icon icon="bun-sh" size="l" /%}
* [Bun](https://hub.docker.com/r/openruntimes/bun/tags)
* `bun-1.0`
* x86 / arm64
---
* {% icon icon="python" size="l" /%}
* [Python](https://hub.docker.com/r/openruntimes/python/tags)
* `python-3.8`
`python-3.9`
`python-3.10`
`python-3.11`
`python-3.12`
* x86 / arm64 / armv7 / armv8
---
* {% icon icon="dart" size="l" /%}
* [Dart](https://hub.docker.com/r/openruntimes/dart/tags)
* `dart-2.16`
`dart-2.17`
`dart-2.18`
`dart-3.0`
`dart-3.1`
* x86 / arm64 / armv7 / armv8
---
* {% icon icon="php" size="l" /%}
* [PHP](https://hub.docker.com/r/openruntimes/php/tags)
* `php-8.0`
`php-8.1`
`php-8.2`
* x86 / arm64 / armv7 / armv8
---
* {% icon icon="ruby" size="l" /%}
* [Ruby](https://hub.docker.com/r/openruntimes/ruby/tags)
* `ruby-3.0`
`ruby-3.1`
`ruby-3.2`
* x86 / arm64 / armv7 / armv8
---
{% /table %}
{% /tabsitem %}
{% /tabs %}

View File

@@ -0,0 +1,128 @@
---
layout: article
title: Templates
description: "Learn about Appwrite Functions' templates that let you jump start function development to extend your Appwrite APIs."
---
Appwrite provides a variety of Function Templates to help you jump start your function development.
You can use Appwrite Function Templates as examples or boilerplates to add new functionality to your Appwrite project.
# Find templates {% #find-templates %}
You can find all available templates by navigating to the Appwrite Console, under your project > **Functions** > **Templates**.
{% only_dark %}
![Templates screen](/images/docs/functions/templates/dark/templates.png)
{% /only_dark %}
{% only_light %}
![Templates screen](/images/docs/functions/templates/templates.png)
{% /only_light %}
You can filter functions by searching, filter by use case, or filter by runtime.
Click **Create function** to create a function from a template.
# Create with templates {% #create-with-templates %}
The create function wizard has five steps.
## Configuration {% #configuration %}
Pick a display name for your function and an ID. You will later use the ID to programmatically execute or configure the function.
Pick the runtime language you wish the function to be created in, not all runtimes are available for all templates.
## Variables {% #variables %}
Appwrite Functions uses [environment variables](/docs/products/functions/develop#environment-variables)
to pass constants and secrets to your Appwrite Functions. You'll provide information like API keys and other
secrets to integrations in this step. If you need an Appwrite API key, you'll be propted to generate one.
## Connect {% #connect %}
You can choose to clone a new repository to your GitHub profile or organization, or to connect to an existing repository.
If you choose connect to an existing repository, the function's code will be cloned to the root folder
you specify under the **Branch** step.
## Repository {% #repository %}
Configure the connected respository for your Appwrite Function. This will be the repository
holding the source code for your function. When the code in this repository is updated, new
deployments will be created.
## Branch {% #branch %}
Production branch specifies the branch connected to your Appwrite Function. When new commits are made to
this branch, a new deployment is automatically created and deployed.
The root directory specifies the folder holding your function template's code.
When a PR is made to the branch, a new deployment is built, but not activated.
A comment is made to your PR about the build, unless you enable **Silent mode**.
# Available templates {% #available-templates %}
{% table %}
* Template {% width=100 %}
* Description
* Runtimes {% width=200 %}
---
* Starter
* A simple starter function that returns "Hello, world!"
* Node.js, Python, PHP, Dart, Node.js (TypeScript), Bun, Deno, Ruby, Kotlin, C++, .NET, Java, Swift
---
* Sync with Meilisearch
* Syncs documents in an Appwrite database collection to a Meilisearch index to add search-as-you-type search boxes to your app.
* Node.js, Python, PHP, Node.js (TypeScript), Bun, Deno, Ruby, Kotlin
---
* WhatsApp with Vonage
* Simple bot to answer WhatsApp messages.
* Node.js, Python, PHP, Dart, Node.js (TypeScript), Bun, Deno, Ruby
---
* Prompt ChatGPT
* Ask question, and let OpenAI GPT-3.5-turbo answer.
* Node.js, Python, PHP, Dart
---
* Censor with Redact
* Automatically remove sensitive data from messages.
* Node.js, Python, Dart
---
* Email Contact Form
* Sends an email with the contents of a HTML form.
* Node.js, Python, PHP
---
* Sync with Algolia
* Intuitive search bar for any data in Appwrite Databases.
* Node.js, Python, PHP
---
* Discord Command Bot
* Add Discord commands to your servers using Discord Interactions.
* Node.js, Python
---
* Github Issue Bot
* Automate the process of responding to newly opened issues on a GitHub repository.
* Node.js, Node.js (TypeScript)
---
* Analyze with PerspectiveAPI
* Automate moderation by using AI to measure the toxicity of messages.
* Node.js
---
* Generate PDF
* Generate PDFs programmatically with Appwrite Functions.
* Node.js
---
* Payments with Stripe
* Receive card payments and store paid orders.
* Node.js
---
* Push Notification with FCM
* Send push notifications to your users using Firebase Cloud Messaging (FCM).
* Node.js
---
* Slack Command Bot
* Simple command bot using Slack API
* Node.js
---
* Storage Cleaner
* Storage cleaner function to remove all files older than X number of days from the specified bucket.
* Node.js
---
* Subscriptions with Stripe
* Receive recurring card payments and grant subscribers extra permissions.
* Node.js
---
* URL Shortener
* Generate URL with short ID and redirect to the original URL when visited.
* Node.js
{% /table %}

View File

@@ -12,7 +12,7 @@ You can send custom email messages to your app's users using Appwrite Messaging
This guide takes you through the implementation path of adding email messaging to your app.
# Add a provider {% #add-a-provider %}
Appwrite supports [Mailgun](/docs/products/messaging/mailgun/) and [Sendgird](/docs/products/messaging/sendgrid/) as
Appwrite supports [Mailgun](/docs/products/messaging/mailgun/) and [Sendgrid](/docs/products/messaging/sendgrid/) as
SMTP providers. You must configure one of them as a provider.
{% only_dark %}
![Add a SMTP provider](/images/docs/messaging/providers/mailgun/dark/add-mailgun.png)

View File

@@ -72,6 +72,12 @@
{
title: 'Mobile and native',
quickStarts: [
{
title: 'React Native',
icon: 'icon-react',
image: '/images/blog/placeholder.png',
href: 'react-native'
},
{
title: 'Flutter',
icon: 'icon-flutter',

View File

@@ -0,0 +1,225 @@
---
layout: article
title: Start with React Native
description: Build React Native apps on iOS, Android, and other native platforms with Appwrite and learn how to use our powerful backend to add authentication, user management, file storage, and more.
difficulty: beginner
readtime: 10
back: /docs/quick-starts
---
Learn how to setup your first React Native project powered by Appwrite.
The React Native SDK is still in `beta`. Proceed with caution if you plan to use this SDK in production.
{% info title="React for web" %}
Looking to start with React for web?
Follow the [React quickstart](/docs/quick-starts/react) and [React tutorial](/docs/tutorials/react/step-1) flows.
{% /info %}
{% section #step-1 step=1 title="Create React Native project" %}
Create a React Native project using [npx](https://www.npmjs.com/package/npx).
```sh
npx create-expo-app my-app
cd my-app
```
{% /section %}
{% section #step-2 step=2 title="Create project" %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console).
{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}
If this is your first time using Appwrite, create an account and create your first project.
Then, under **Add a platform**, add a **Android app** or a **Apple app**.
{% tabs %}
{% tabsitem #ios title="iOS" %}
Add your app **name** and **Bundle ID**. You can find your **Bundle Identifier** in the **General** tab for your app's primary target in XCode.
{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
{% /only_dark %}
{% only_light %}
![Add a platform](/images/docs/quick-starts/add-platform.png)
{% /only_light %}
{% /tabsitem %}
{% tabsitem #android title="Android" %}
Add your app's **name** and **package name**, Your package name is generally the `applicationId` in your app-level [build.gradle](https://github.com/appwrite/playground-for-flutter/blob/master/android/app/build.gradle#L41) file.
{% arrow_link
href="https://developer.android.com/build/configure-app-module" %}
Learn more about Android app module
{% /arrow_link %}
{% /tabsitem %}
{% /tabs %}
You can skip optional steps.
{% /section %}
{% section #step-3 step=3 title="Install Appwrite" %}
Install the Appwrite SDK for React Native and required dependencies.
```sh
npx expo install react-native-appwrite react-native-url-polyfill
```
{% /section %}
{% section #step-4 step=4 title="Implement Appwrite" %}
Find your project's ID in the **Settings** page.
{% only_dark %}
![Project settings screen](/images/docs/quick-starts/dark/project-id.png)
{% /only_dark %}
{% only_light %}
![Project settings screen](/images/docs/quick-starts/project-id.png)
{% /only_light %}
Open `App.js` and add the following code to it, replace `<YOUR_PROJECT_ID>` with your project ID and `<YOUR_PLATFORM>` with your application id or package name.
This imports and initializes Appwrite and defines some basic authentication methods.
```ts
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native';
import { Client, Account, ID } from 'react-native-appwrite';
import React, { useState } from 'react';
let client;
let account;
client = new Client();
client
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject('sisyphus')
.setPlatform('com.example.my-app');
account = new Account(client);
export default function App() {
const [loggedInUser, setLoggedInUser] = useState(null);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
async function login(email, password) {
await account.createEmailSession(email, password);
setLoggedInUser(await account.get());
}
async function register(email, password, name) {
await account.create(ID.unique(), email, password, name);
await login(email, password);
setLoggedInUser(await account.get());
}
return (
// ... Implement your UI here
);
}
const styles = StyleSheet.create({
// ... define some tyles
});
```
{% /section %}
{% section #step-5 step=5 title="Create a login form" %}
With `Client` and `Account` service initialized, you can now use them to make your first requests to Appwrite.
Add the following components to your `App.js` file to create a simple login form.
```ts
<View style={styles.root}>
<Text>
{loggedInUser ? `Logged in as ${loggedInUser.name}` : 'Not logged in'}
</Text>
<View>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={(text) => setEmail(text)}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={(text) => setPassword(text)}
secureTextEntry
/>
<TextInput
style={styles.input}
placeholder="Name"
value={name}
onChangeText={(text) => setName(text)}
/>
<TouchableOpacity
style={styles.button}
onPress={() => login(email, password)}
>
<Text>Login</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={()=> register(email, password, name)}
>
<Text>Register</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={async () => {
await account.deleteSession('current');
setLoggedInUser(null);
}}
>
<Text>Logout</Text>
</TouchableOpacity>
</View>
</View>
```
You can also add some simple styling to your app by adding the following styles to your `App.js` file.
```ts
const styles = StyleSheet.create({
root: {
marginTop: 40,
marginBottom: 40
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 10,
paddingHorizontal: 10,
},
button: {
backgroundColor: 'gray',
padding: 10,
marginBottom: 10,
alignItems: 'center',
},
});
```
{% /section %}
{% section #step-6 step=6 title="All set" %}
Run your project with `npx expo start`.
{% arrow_link
href="https://github.com/appwrite/playground-for-react-native" %}
Explore the React Native playground
{% /arrow_link %}
{% /section %}

View File

@@ -116,7 +116,7 @@ Here's a complete list of all configurable options in `appwrite.json`:
| `ignore` | array of strings | Files from your source code that should be ignored when creating a deployment. |
| `execute` | array of strings | Grants execute permissions to users. [Learn more about role strings](/docs/apis/rest#permissions). |
| `events` | array of strings | Events that trigger the function to execute. [Learn more about events](/docs/advanced/platform/events). |
| `schedule` | string | Execute schedule for the function. [Learn more about scheduled executions](/docs/products/functions/execution#schedule). |
| `schedule` | string | Execute schedule for the function. [Learn more about scheduled executions](/docs/products/functions/execute#schedule). |
| `timeout` | int | Execution timeout of the function in seconds, with a maximum configurable limit of 900 seconds. |
| `variables` | JSON object | Variables provided to the function on execution stored as a key-value JSON object. |

View File

@@ -12,6 +12,7 @@
type MappedTutorial = (typeof data.tutorials)[number];
const iconMap: Record<string, string> = {
'react native': 'icon-react',
react: 'icon-react',
vue: 'web-icon-vue',
angular: 'icon-angular',

View File

@@ -17,7 +17,7 @@ This tutorial will introduce the following concepts:
2. Authentication
3. Databases and collections
4. Queries and pagination
5. Storage
# Prerequisites {% #prerequisites %}
1. Basic knowledge of Kotlin and Android development.

View File

@@ -120,10 +120,10 @@ To catch that moment in time when the page loads, we call our `fetch` function,
Second, it's likely that a user will want to delete one of their ideas from the database.
We help them do that by adding a delete button in the top right corner of the idea list item, but only on the ideas added by the user itself.
Add the file `IdeasList` from the `componenents` directory and insert the following code:
Add the file `IdeasList` from the `components` directory and insert the following code:
```vue
<!-- componenents/IdeasList.vue -->
<!-- components/IdeasList.vue -->
<script setup>
import { onMounted } from 'vue';

View File

@@ -0,0 +1,10 @@
<script lang="ts">
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';
export let data;
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>
<slot />

View File

@@ -0,0 +1,11 @@
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = ({ url }) => {
const tutorials = import.meta.glob('./**/*.markdoc', {
eager: true
});
return {
tutorials,
pathname: url.pathname
};
};

View File

@@ -0,0 +1,6 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async () => {
redirect(303, '/docs/tutorials/react-native/step-1');
};

View File

@@ -0,0 +1,28 @@
---
layout: tutorial
title: Build an ideas tracker with React Native
description: Learn to build a React Native app with no backend code using an Appwrite backend.
step: 1
difficulty: beginner
readtime: 10
category: Mobile and native
framework: React Native
---
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish.
In this tutorial, you will build Idea tracker with Appwrite and React Native.
# Concepts {% #concepts %}
This tutorial will introduce the following concepts:
1. Setting up your first project
2. Authentication
3. Databases and collections
4. Queries and pagination
# Prerequisites {% #prerequisites %}
1. Android, iOS simulators, or a physical device to run the app
2. Have [Node.js](https://nodejs.org/en) and [NPM](https://www.npmjs.com/) installed on your computer
3. Basic knowledge of React Native and Expo

View File

@@ -0,0 +1,43 @@
---
layout: tutorial
title: Create app
description: Create a React Native app project and integrate with Appwrite.
step: 2
---
# Create React Native project {% #create-react-project %}
Create a React Native app with the `npm create` command.
```sh
npx create-expo-app ideas-tracker
cd ideas-tracker
```
# Add dependencies {% #add-dependencies %}
Install the React Native Appwrite SDK.
```sh
npx expo install react-native-appwrite react-native-url-polyfill
```
Then, install React Navigation to help implement simple navigation logic.
```sh
npm install @react-navigation/native @react-navigation/native-stack
```
Install peer dependencies needed for React Navigation.
```sh
npx expo install react-native-screens react-native-safe-area-context
```
For iOS with bare React Native project, make sure you have CocoaPods installed. Then install the pods to complete the installation:
```
cd ios
pod install
cd ..
```

View File

@@ -0,0 +1,60 @@
---
layout: tutorial
title: Set up Appwrite
description: Import and initialize Appwrite for your React Native application.
step: 3
---
# Create project {% #create-project %}
Head to the [Appwrite Console](https://cloud.appwrite.io/console).
{% only_dark %}
![Create project screen](/images/docs/quick-starts/dark/create-project.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/quick-starts/create-project.png)
{% /only_light %}
If this is your first time using Appwrite, create an account and create your first project.
Then, under **Add a platform**, add a **Android** or **Apple** platform with the package/bundle ID `com.example.idea-tracker`.
{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
{% /only_dark %}
{% only_light %}
![Add a platform](/images/docs/quick-starts/add-platform.png)
{% /only_light %}
You can skip optional steps.
# Initialize Appwrite SDK {% #init-sdk %}
To use Appwrite in our React Native app, you'll need to find our project ID.
Find your project's ID in the **Settings** page.
{% only_dark %}
![Project settings screen](/images/docs/quick-starts/dark/project-id.png)
{% /only_dark %}
{% only_light %}
![Project settings screen](/images/docs/quick-starts/project-id.png)
{% /only_light %}
Create a new file `lib/appwrite.js` to hold our Appwrite related code.
Only one instance of the `Client()` class should be created per app.
Add the following code to it, replacing `<YOUR_PROJECT_ID>` with your project ID.
```js
import { Client, Databases, Account } from "react-native-appwrite";
const client = new Client();
client
.setEndpoint("https://cloud.appwrite.io/v1")
.setProject("<YOUR_PROJECT_ID>") // Replace with your project ID
.setPlatform('com.example.idea-tracker');
export const account = new Account(client);
export const databases = new Databases(client);
```

View File

@@ -0,0 +1,166 @@
---
layout: tutorial
title: Add authentication
description: Add authentication to your React Native application.
step: 4
---
# User context {% #user-context %}
In React Native, you can use [context](https://reactjs.org/docs/context.html) to share data between components.
You can use a context and a custom hook to manage our user's data.
Create a new file `contexts/UserContext.jsx` and add the following code to it.
```js
import { ID } from "react-native-appwrite";
import { createContext, useContext, useEffect, useState } from "react";
import { account } from "../lib/appwrite";
import { toast } from "../lib/toast";
const UserContext = createContext();
export function useUser() {
return useContext(UserContext);
}
export function UserProvider(props) {
const [user, setUser] = useState(null);
async function login(email, password) {
const loggedIn = await account.createEmailSession(email, password);
setUser(loggedIn);
toast('Welcome back. You are logged in');
}
async function logout() {
await account.deleteSession("current");
setUser(null);
toast('Logged out');
}
async function register(email, password) {
await account.create(ID.unique(), email, password);
await login(email, password);
toast('Account created');
}
async function init() {
try {
const loggedIn = await account.get();
setUser(loggedIn);
toast('Welcome back. You are logged in');
} catch (err) {
setUser(null);
}
}
useEffect(() => {
init();
}, []);
return (
<UserContext.Provider value={{ current: user, login, logout, register, toast }}>
{props.children}
</UserContext.Provider>
);
}
```
Now, you can use the `useUser` hook to access the user's data from any component wrapped by this context's provider.
# Display toasts {% #display-toasts %}
For a better user experience, display toasts when the users perform an action, such as login, logout, create new ideas, etc.
We can do this by creating a new file `lib/toast.js` and adding the following code to it.
```js
import { ToastAndroid, Platform, AlertIOS } from 'react-native';
export function toast(msg) {
if (Platform.OS === 'android') {
ToastAndroid.show(msg, ToastAndroid.SHORT)
} else {
AlertIOS.alert(msg);
}
}
```
# Login page {% #login-page %}
Create a new file `views/Login.jsx` and add the following code to it.
this page contains a basic form to allow the user to login or register.
Notice how this page consumes the `useUser` hook to access the user's data and perform login and register actions.
```js
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet } from 'react-native';
import { useUser } from '../contexts/UserContext';
export default function LoginScreen() {
const user = useUser();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
<View style={styles.container}>
<Text style={styles.header}>Login or register</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<View style={styles.buttonContainer}>
<Button
title="Login"
onPress={
() => {
user.login(email, password)
}
}
/>
<Button
title="Register"
onPress={
() => {
user.register(email, password)
}
}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
padding: 16,
},
header: {
fontSize: 24,
marginBottom: 20,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 10,
paddingLeft: 8,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
});
```

View File

@@ -0,0 +1,106 @@
---
layout: tutorial
title: Add routing
description: Add routing to your React Native applicating using Appwrite.
step: 5
---
In this step, you'll add some basic routing to your app.
Based on the user's login status, you'll redirect them to the login page or the home page.
# Home page {% #home-page %}
Create a new file `views/Home.jsx` and add the following stub code to it.
We'll update this page later to display the ideas posted by other users and allow the user to post their ideas.
```js
import React from 'react';
import { View, Text } from 'react-native';
import { useUser } from '../contexts/UserContext';
export default function HomeScreen() {
const user = useUser();
console.log(user.current);
return (
<View>
<Text>
Welcome, {user.current ? user.current.email : 'Please login'}
</Text>
</View>
);
}
```
# Basic routing {% #basic-routing %}
To handle basic routing, you can use the `react-navigation` library.
This router also consumes the `UserContext` to determine if the user is logged in or not to redirect the user automatically.
Create a file `lib/Router.jsx` and add the following code to it.
```js
import { NavigationContainer } from '@react-navigation/native';
import LoginScreen from '../views/Login';
import HomeScreen from '../views/Home';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useUser } from '../contexts/UserContext';
const Stack = createNativeStackNavigator();
export function Router() {
const user = useUser();
return (
<NavigationContainer>
<Stack.Navigator>
{user.current == null ? (
<Stack.Screen
name="Login"
component={LoginScreen}
options={{ title: 'Login' }}
/>
) : (
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Home' }}
/>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
```
We'll display this router in the `App.js` file.
```js
import { StyleSheet, Text, View } from 'react-native';
import { UserProvider } from './contexts/UserContext';
import { Router } from './lib/Router';
export default function App() {
return (
<UserProvider>
<Router />
</UserProvider >
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
```
# Test the routing {% #test-routing %}
Now, if you run the app, you should see the login page.
If you create a new account, you should be logged in and redirected to the home page.
Run the app using the following command.
```sh
npm run start
```

View File

@@ -0,0 +1,137 @@
---
layout: tutorial
title: Add database
description: Connect a database to your React Native application using Appwrite Web SDK.
step: 6
---
In this step, you'll set up a database to store ideas in Appwrite, configure permissions, then create a
context to manage ideas in your React Native app.
# Create collection {% #create-collection %}
In Appwrite, data is stored as a collection of documents. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our ideas.
{% only_dark %}
![Create project screen](/images/docs/tutorials/dark/idea-tracker-collection.png)
{% /only_dark %}
{% only_light %}
![Create project screen](/images/docs/tutorials/idea-tracker-collection.png)
{% /only_light %}
Create a new collection with the following attributes:
| Field | Type | Required |
|-------------|--------|----------|
| userId | String | Yes |
| title | String | Yes |
| description | String | No |
# Configure permissions {% #configure-permissions %}
{% only_dark %}
![Collection permissions screen](/images/docs/tutorials/dark/idea-tracker-permissions.png)
{% /only_dark %}
{% only_light %}
![Collection permissions screen](/images/docs/tutorials/idea-tracker-permissions.png)
{% /only_light %}
Navigate to the **Settings** tab of your collection, add the role **Any** and check the **Read** box.
Next, add a **Users** role and give them access to **Create** by checking those boxes.
These permissions apply to all documents in your new collection.
Finally, enable **Document security** to allow further permissions to be set at the document level.
Remember to click the **Update** button to apply your changes.
# Ideas context {% #ideas-context %}
Now that you have a collection to hold ideas, we can read and write to it from our app.
Like you did with the user data, we will create a React context to hold our ideas.
Create a new file `contexts/IdeasContext.jsx` and add the following code to it.
```js
import { ID, Permission, Role, Query } from "react-native-appwrite";
import { createContext, useContext, useEffect, useState } from "react";
import { databases } from "../lib/appwrite";
import { toast } from "../lib/toast";
export const IDEAS_DATABASE_ID = "default"; // Replace with your database ID
export const IDEAS_COLLECTION_ID = "ideas-tracker"; // Replace with your collection ID
const IdeasContext = createContext();
export function useIdeas() {
return useContext(IdeasContext);
}
export function IdeasProvider(props) {
const [ideas, setIdeas] = useState([]);
async function add(idea) {
const response = await databases.createDocument(
IDEAS_DATABASE_ID,
IDEAS_COLLECTION_ID,
ID.unique(),
idea,
[Permission.write(Role.user(idea.userId))]
);
toast('Ideas added');
setIdeas((ideas) => [response, ...ideas].slice(0, 10));
}
async function remove(id) {
await databases.deleteDocument(IDEAS_DATABASE_ID, IDEAS_COLLECTION_ID, id);
toast('Idea removed');
setIdeas((ideas) => ideas.filter((idea) => idea.$id !== id));
await init(); // Refetch ideas to ensure we have 10 items
}
async function init() {
const response = await databases.listDocuments(
IDEAS_DATABASE_ID,
IDEAS_COLLECTION_ID,
[Query.orderDesc("$createdAt"), Query.limit(10)]
);
setIdeas(response.documents);
}
useEffect(() => {
init();
}, []);
return (
<IdeasContext.Provider value={{ current: ideas, add, remove }}>
{props.children}
</IdeasContext.Provider>
);
}
```
Notice that new ideas have the added permission `Permission.write(Role.user(idea.userId))`.
This permission ensures that only the user who created the idea can modify it.
Remeber to add the `IdeasProvider` to your `App.js` file.
```js
import { StyleSheet, Text, View } from 'react-native';
import { UserProvider } from './contexts/UserContext';
import { IdeasProvider } from './contexts/IdeasContext'; // Add import
import { Router } from './lib/Router';
export default function App() {
return (
<UserProvider>
<!-- Add the Ideas Provider -->
<IdeasProvider>
<Router />
</IdeasProvider>
</UserProvider >
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
```

View File

@@ -0,0 +1,131 @@
---
layout: tutorial
title: Create ideas page
description: Add database queries and pagination using Appwrite in your React Native application.
step: 7
---
Using the `useIdeas` hook you can now display the ideas on the page and create a form to submit new ideas.
If an idea is submitted by the logged-in user, a remove button will be displayed to remove the idea.
While this check uses the user ID to determine the render logic, permissions set in step 6 will be used to enforce
that only the owner of the idea can remove it.
Overwrite the contents of `views/Home.jsx` with the following:
```js
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ScrollView } from 'react-native';
import { useUser } from '../contexts/UserContext';
import { useIdeas } from '../contexts/IdeasContext';
export default function HomeScreen() {
const user = useUser();
const ideas = useIdeas();
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
return (
<ScrollView>
{user.current ? (
<View style={styles.section}>
<Text style={styles.header}>Submit Idea</Text>
<View>
<TextInput
style={styles.input}
placeholder="Title"
value={title}
onChangeText={(text) => setTitle(text)}
/>
<TextInput
style={styles.input}
placeholder="Description"
value={description}
onChangeText={(text) => setDescription(text)}
/>
<Button
title="Submit"
onPress={() =>
ideas.add({ userId: user.current.$id, title, description })
}
/>
</View>
</View>
) : (
<View style={styles.section}>
<Text>Please login to submit an idea.</Text>
</View>
)}
<View style={styles.section}>
<Text style={styles.header}>Latest Ideas</Text>
<View>
{ideas.current.map((idea) => (
<View key={idea.$id} style={styles.card}>
<Text style={styles.cardTitle}>{idea.title}</Text>
<Text style={styles.cardDescription}>{idea.description}</Text>
{/* Show the remove button to idea owner. */}
{user.current && user.current.$id === idea.userId && (
<Button
title="Remove"
onPress={() => ideas.remove(idea.$id)}
/>
)}
</View>
))}
</View>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
section: {
marginBottom: 20,
padding: 16,
},
container: {
flex: 1,
justifyContent: 'center',
padding: 16,
},
header: {
fontSize: 24,
marginBottom: 20,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 10,
paddingLeft: 8,
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
card: {
backgroundColor: 'white',
padding: 16,
marginBottom: 16,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
cardTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 8,
},
cardDescription: {
fontSize: 14,
marginBottom: 8,
},
});
```

View File

@@ -0,0 +1,14 @@
---
layout: tutorial
title: Next steps
description: Run your React Native project built with Appwrite
step: 8
---
# Test your project {% #test-project %}
You can run your projects with `npm run start`. This will start the Metro bundler and open the Expo Go app on your device.
You can also run your project on an emulator or simulator by pressing `i` for iOS and `a` for Android.
# Bundling for production {% #bundle-production %}
Appwrite's React Native SDK is designed to work with the Expo Metro bundler.
When you are ready to build your app for production, you can learn more in the [Expo documentation](https://docs.expo.dev/).

View File

@@ -27,7 +27,7 @@ This tutorial will introduce the following concepts:
2. Authentication
3. Databases and collections
4. Queries and pagination
5. Storage
# Prerequisites {% #prerequisites %}

View File

@@ -9,7 +9,7 @@ In our app we want to have a navigation bar that is always visible. We will add
- a logout button if the user is logged in.
- a login button if the user is not logged in.
Update the App componenent in `src/App.jsx`:
Update the App component in `src/App.jsx`:
```client-web
import { Login } from "./pages/Login";

View File

@@ -50,7 +50,7 @@ export function IdeasProvider(props) {
ID.unique(),
idea
);
setIdeas((ideas) => [response.$id, ...ideas].slice(0, 10));
setIdeas((ideas) => [response, ...ideas].slice(0, 10));
}
async function remove(id) {

View File

@@ -29,7 +29,7 @@ This tutorial will introduce the following concepts:
2. Authentication
3. Databases and collections
4. Queries and pagination
5. Storage
# Prerequisites {% #prerequisites %}

View File

@@ -26,7 +26,7 @@ This tutorial will introduce the following concepts:
2. Authentication
3. Databases and collections
4. Queries and pagination
5. Storage
# Prerequisites {% #prerequisites %}
1. Basic knowledge of JavaScript and Svelte.

View File

@@ -16,7 +16,7 @@ import { getIdeas } from '$lib/ideas';
export async function load() {
return {
ideas: getIdeas()
ideas: await getIdeas()
};
}
```

View File

@@ -26,7 +26,7 @@ This tutorial will introduce the following concepts:
2. Authentication
3. Databases and collections
4. Queries and pagination
5. Storage
# Prerequisites {% #prerequisites %}

View File

@@ -9,7 +9,7 @@ In our app we want to have a navigation bar that is always visible. Use the `use
- a logout button if the user is logged in.
- a login button if the user is not logged in.
Update the App componenent in `src/App.vue`:
Update the App component in `src/App.vue`:
```vue
<script setup>

View File

@@ -98,11 +98,12 @@ What personal data we collect, why we collect it, and how it is used
* 3rd party platforms such as:
- Orbit
- MSG91 (India only)
- Activetrail (Israel only)
- Twilio
* 3rd party platforms such as:
- Mailgun
- Orbit
- Hubspot
- Userlist
---
* Consequences of not providing the Personal Data
* - Cannot create an account
@@ -295,7 +296,7 @@ What personal data we collect, why we collect it, and how it is used
---
* Third parties with whom we share your personal data
* 3rd party platforms such as:
- Hubspot
- Userlist
---
* Consequences of not providing the personal data
* - Cannot establish a business connection

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Some files were not shown because too many files have changed in this diff Show More