Application patterns

Build validated and authorized CRUD

Keep transport validation, tenant authorization, database constraints, and safe responses as separate layers.

8 minute lesson

~~~

Validate JSON before querying. Authenticate the caller, then include ownership or tenant boundaries in the SQL operation itself. Checking ownership in one query and updating in another can create gaps and duplicated logic.

Return 404 when a caller should not learn whether another tenant’s row exists. Keep database errors out of public responses, but log safe identifiers and operation names for diagnosis.

Implement update and delete statements that include both note ID and owner ID in their WHERE clauses.

Bind values instead of building SQL strings:

const note = await env.DB
  .prepare('select id, title from notes where id = ? and user_id = ?')
  .bind(noteId, userId)
  .first()

The user ID must come from a verified identity, not a request field. Test a valid owner, another user, a missing note, and a malformed ID. Parameter binding protects the SQL boundary; the ownership predicate protects the authorization boundary.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →