Atomic Write a function
An @atomic directive is a comment placed directly above a function. It declares the trigger, the route and the auth mode. Here's an endpoint that saves an item, in Go. The validate helper beside it carries no directive, so it is free:
package main
import drift "github.com/ondrift/cloud/sdk/go"
// validate is a plain helper with no @atomic directive,
// so it never counts as one of your functions.
func validate(body map[string]any) bool {
return body["name"] != nil
}
// @atomic http=post:items auth=none
func PostItems(body map[string]any, req drift.Request) (int, string, any, map[string]string) {
if !validate(body) {
return 400, "Bad Request", map[string]any{"error": "name required"}, nil
}
id, _ := drift.Backbone.NoSQL.Collection("items").Insert(body)
return 201, "Created", map[string]any{"id": id}, nil
}A handler returns the status code, a short status message and the payload (serialised to JSON for you). Go is the exception: it takes a fourth return, an optional response-headers map, nil when you don't need one. Python, Node, Ruby, PHP and Rust return three values.
Handlers that take a body (post, put, delete, patch and queue handlers) are called as (body, req). A get handler is called as (req).
Pay for what you expose, not what you write.
@atomic directive is the line between a billable endpoint and free scaffolding. The function directly below it is one of your function slots, so it counts toward your plan and your bill. Every other function in your source (helpers, validation, shared logic) carries no directive, so it's free and doesn't count. Structure your code however you like; you only pay for the surface you choose to expose.What the directive binds to
The annotation must sit on the line immediately above a callable. Blank lines between are fine; other code is not. An annotation that matches no callable fails the deploy and names the shape it looked for:
| Language | Shape the parser matches |
|---|---|
| Go | func MyFunction(…), exported, capital first letter |
| Python | def my_function(…): |
| Node | function myFunction(…), a named declaration; an arrow assigned to a const is not matched |
| Ruby | def my_function(…) |
| PHP | function myFunction(…) |
| Rust | pub fn my_function(…), where pub is required |
One directive per callable. Two @atomic lines stacked above the same function is a hard error. Split them into two functions.
In the flat layout the handler's name is yours to choose; the directive carries the route. The one-function-per-folder form (drift atomic deploy ./some-folder) is the exception: there the CLI derives the handler name from the method and route, so http=post:items must be PostItems in Go, post_items in Python/Ruby/PHP/Rust and postItems in Node. Route parameters flatten first: get:users/:id → GetUsersId.